【问题标题】:Designing an API with designated client keys使用指定的客户端密钥设计 API
【发布时间】:2026-01-21 16:10:01
【问题描述】:

我正在设计一个 JSON Web API,并希望通过唯一的 ID 来区分客户端,以便监控使用情况并阻止恶意/行为不端的客户端。 API 没有封装在 JavaScript 库中,也不是 Web 应用程序独有的,任何客户端类型(桌面、电话等)都可以使用它。

问题是,web 应用(官网)也是 API 本身的客户端,因此必须公开其 API 密钥。因此,某些用户可以直接从页面上的 JavaScript 中提取密钥并使用它,而不是生成自己的密钥。

是否有可能通过一些更好/更智能的设计选择以某种方式缓解这个问题,还是我必须接受这样一个事实,即任何恶意使用 API 的人都可以利用这一点?

我对前端应用程序 (EmberJS) 和后端服务器 (Go) 有 100% 的控制权,因此可以建议任何更改。

  • 我正在使用每个会话/IP 的速率限制来为这种情况添加额外的保护层
  • twitter.com 页面曾经也是它自己的 API 的客户端。他们是如何解决这个问题的?

注意:问题不在于身份验证或安全性本身,而是如何要求第 3 方用户在身份验证之外(!)使用 API 密钥!

【问题讨论】:

  • 这是*.com/questions/2256305/… 的非常接近的副本。里面有一些很好的答案。
  • 不幸的是,这并不像我想象的那么容易。那里提供的答案假设该应用程序仅在加载 Google JS 库的浏览器中使用。我的情况并非如此。因此,仍然感谢更具体的答案!
  • 不,我不能要求域(分别在 JS 中检查正确的域),因为该 API 不仅适用于网络应用,还适用于桌面客户端、手机等。
  • @ErikAigner 您能否介绍一下 API 的功能。想考虑是否有可能与 API 密钥一起拥有一个秘密密钥。
  • 你的官方webapp是否只能直接从JS访问你的API?它是一个要求吗?您能否对其进行重新设计,以便仅从您的服务器端代码访问您的 API?通常,当您想避免未经授权的 API 客户端冒充合法客户端时,您会希望使用某种方法使用 API 密钥对请求进行签名,但这需要您在服务器端进行,否则您需要还可以在您的 JS 代码中公开您的 API 机密。

标签: web-services security api web-applications api-design


【解决方案1】:

仅使用单个 API 密钥将无法确保您的 API 安全。您所描述的 API 密钥基本上是一个公钥,您需要某种类型的私钥来进行安全识别/身份验证以及提供它的机制。

您询问了 Twitter 如何解决此问题。他们使用 Oath 1.0a。下面简要描述了它是如何与Twitter Developer FAQ 中的 API 密钥绑定的。

与 API 的大多数集成都需要您确定自己的 通过 API 密钥向 Twitter 应用程序。在推特平台上, 术语“API 密钥”通常指的是所谓的 OAuth 消费者 钥匙。此字符串在发出请求时标识您的应用程序 API。在 OAuth 1.0a 中,您的“API 密钥”可能指的是组合 这个消费者密钥和“消费者秘密”的字符串,用于 安全地“签署”您对 Twitter 的请求。对 Twitter 的大多数请求 除了应用程序上下文之外,还需要用户上下文。用户 上下文是通过使用另一种令牌/密钥来呈现的 称为“访问令牌”。有关更多信息,请参阅获取访问令牌 信息。

您可以在Apigee.com 找到很多关于设计 API 的优秀资源。他们建议使用OAuth 2.0 进行身份验证/授权。

这里是关于如何使用HMAC authentication to secure a Web API的描述。

当我不得不使用仅使用 API 密钥的 API 时,我为我的 Web 应用程序使用了一种解决方法。我不直接从 Web 应用程序的客户端部分(即 Web 浏览器中的 JavaScript)访问 API。相反,我访问 API 服务器端并将 API 密钥加密存储在安全配置文件中。我为原始 API 提供了一个 Facade,并使用我自己的安全方法来保护依赖于应用程序类型的 Facade API。

【讨论】:

  • 当 JS 应用程序是客户端时,HMAC/OAuth 将无济于事。您提到“并使用我自己的安全方法来保护 Facade API”。那部分实际上会很有趣。您的安全方法是什么?
  • 使用 OAuth、Forms Authentication 或 Basic Authentication 对 Web 应用程序的用户进行身份验证,然后在 Web 客户端和 Facade API 之间使用过期的 cookie 或令牌。
  • 问题不是关于身份验证,而是要求用户除了身份验证之外还需要使用 api 密钥。
  • 如果不是用于身份验证,您将使用 api 密钥做什么?你不是用 api 密钥来识别谁在使用你的 api 吗?我提供了使用 cookie 和令牌而不是 api 密钥的替代方案。您可以继续为您的 api 使用 api 密钥,但无法在 javascript 中确保其安全。具体来说,您的问题是,“......,还是我必须接受这样一个事实,即任何恶意使用 API 的人都可以利用这一点?”答案是肯定的,如果你只使用一个 api 密钥。
  • 不,api 密钥只是为了检查哪个用户正在使用哪个客户端——而不是用于身份验证本身。我只想要求第 3 方用户使用除了身份验证之外的 API 密钥。
【解决方案2】:

通用 API 工作流程:

  1. 客户端发送请求
  2. 请求已通过身份验证和授权
  3. 数据被发回

网站 - 登录

  1. 用户登录时提供用户名和密码
  2. 创建一个秘密并将其存储到一个 cookie 中

网站 - API 访问

  1. 客户端发送请求
  2. 请求根据 coockie 的秘密进行身份验证和授权(cookie 随请求一起发送)
  3. 数据被发回

非 WEB 客户端 - 获取 API KEY(长随机字母数字字符串)

  1. 选项 1 - 用户在网站上注册客户端,获取 API KEY 并将其存储到客户端中
  2. 选项 2 - 用户在客户端输入用户名和密码,客户端请求一个带有用户名和密码的 API KEY,密钥被返回并存储到客户端。用户名和密码不存储在客户端上。

非 WEB 客户端 - API 通信

  1. 客户端使用 API KEY 发送请求
  2. 请求根据API-KEY进行认证和授权
  3. 数据被发回

当使用选项 2 生成密钥时,您可以获得一些额外的数据,因为它来自客户端(操作系统、浏览器)。在这种情况下,在检查 API-KEY 时,如果用户更改了操作系统或浏览器,您可以强制用户生成一个新的

关键是您的 API 以两种方式对请求进行身份验证。 使用 coockie 的秘密或 API KEY。因此无需在网站上公开 API-KEY。

请注意,对于使用 API-KEY 的客户端,不涉及会话。每个请求仅由 API-KEY 进行身份验证。所以这些客户端被认为是非 WEB 应用程序。

【讨论】:

  • 这并没有解决问题中实际提出的问题。
  • 问题不是如何避免在网站上暴露 API-KEY 吗?
  • 这只是对基于会话的身份验证的解释,无论如何我都会这样做。您的答案中没有任何内容强制某人获取 API 密钥或如何将它们与常规网络应用程序/客户端区分开来(这是问题)。
  • Web 应用不需要 API 密钥,因为基于会话的身份验证授予对 API 的访问权限。对于其他客户端,除非您提供 API 密钥,否则您无法访问 API。不涉及会话。未经会话身份验证且带有 API-KEY 的请求被视为非 Web 应用程序。
  • 还有什么可以阻止某人只使用基于会话的身份验证而不是使用 API 密钥?如果问题是那么微不足道,我不会为它掏出 250 分...
【解决方案3】:

您应该区分网络客户端和非网络客户端。 Web 的访问密钥不能在非 Web 中使用,反之亦然。对于网络客户端,您可以进行引用检查等。您还可以为您的应用程序动态创建访问密钥并每天(或每个会话)自动更改它们。您还可以仅为您的应用添加一些特殊验证,例如一些由混淆 JS 计算的附加密钥。

没有什么可以阻止恶意用户模拟浏览器、执行 JS、操纵它,然后做坏事——但你可以让它变得非常烦人,以至于他们认为不值得他们努力。 权限等非常重要的事情显然需要在服务器端进行检查,因此滥用您的 API 应该不是什么大问题。您必须像对待通过网站的 API 密钥一样对待 API 滥用你会经常滥用网络应用程序 - IP 块等。

您仍然需要为非网络客户端的 API 密钥保密。这只能通过混淆来不可靠地完成,您可以将其留给客户端开发人员。如果他们的密钥被泄露和滥用,您将其撤消,他们将有动力修复它。

看看OAuth 2.0,它们包含许多可能对您有用的功能。即使你不想使用它,你也可以从中获得一些灵感。 OpenStreetMap 使用 OAuth(不确定是 1 还是 2)作为基于 flash 的编辑器;只要登录用户从同一来源调用它,就会自动完成 OAuth 权限授予。对于第三方应用程序,用户需要手动完成。您可能想检查一下。

【讨论】:

  • 我之前想过其中的一些观点。 1)推荐人很容易被欺骗 2)如果我的应用程序可以每天检索新的凭据,其他人也可以。 3)混淆只需要多一点时间,但没有障碍。无论如何,我都会检查每个 ip/会话的权限和速率限制。
  • 1) 如果您是在您无法控制的浏览器中使用的网站,我知道无法欺骗引荐来源网址。 (通过对客户端的控制,它显然是。) 2)您的应用程序从您的服务器加载。自动更改凭证,重新排序变量,然后重新混淆文件(例如使用 Closure 编译器)很容易。自动提取正确的 var 是可能的,当然,但不是微不足道的,并且可能足以让攻击者认为它不值得。 您无法获得完美的保护。 3) 混淆非常擅长使撬出密钥变得非常烦人,即不值得。
  • ad 1) 请参阅问题:“API 不是……网络应用独有的”
  • @ErikAigner:这就是为什么我建议为 Web 应用程序和非 Web 应用程序使用单独的密钥。如果非网络应用程序决定使用网络应用程序密钥,它将不得不欺骗引用者以及您实施的任何“这是一个真正的浏览器”检查。同样,没有办法实现完全保护。攻击者可以在修改后的浏览器中打开您的 Web 应用程序并更改 JS 的任意部分,即使您以某种方式确保只有您的页面可以访问 API。我怀疑在他自己的机器上冒充你的网络客户端会给攻击者带来任何好处。
  • (续)除非你有真正的理由相信攻击者有理由这样做(那么你可能需要重新设计安全模型 - 你不能完全信任客户端),我的建议是在 web/non-web 中进行关键分离,但只有在您实际遇到攻击时才实施进一步的对策(这可能永远不会发生)。