使用指定的客户端密钥设计API

13
我正在设计一个JSON Web API,并希望通过唯一的ID来区分客户端,以便监控使用情况并阻止恶意或不良表现的客户端。该API没有封装在JavaScript库中,也不仅限于Web应用程序,任何类型的客户端都可以使用它(桌面、手机等)。
问题是,Web应用程序(官方网站)本身也是API的客户端,因此必须公开其API密钥。因此,某些用户可以从页面中的JavaScript提取密钥并使用它,而不是生成自己的密钥。
是否可能通过一些更好/更智能的设计选择来缓解这个问题,或者我必须接受任何恶意使用API的用户都可以利用这一事实?
我对前端应用程序(EmberJS)和后端服务器(Go)拥有100%的控制,因此可以建议任何改变。
我正在使用每个会话/IP的速率限制来增加额外的保护层。
Twitter.com页面曾经也是其API的客户端。他们是如何解决这个问题的?
注意:问题不是关于身份验证或安全本身,而是如何要求第三方用户在身份验证之外使用API密钥的!

1
这是一个非常接近 https://dev59.com/_XE95IYBdhLWcg3wkeqq 的副本。里面有一些很好的答案。 - Justin Anderson
很遗憾,这并不像我想象的那么容易。提供的答案假定应用程序仅在加载Google JS库的浏览器中使用。但这在我的情况下并不是这样。因此,仍然希望提供更具体的答案! - user187676
不,我不能要求一个域名(在JS中检查正确的域名),因为该API不仅专为Web应用程序设计,也面向桌面客户端、手机等其他终端。 - user187676
@ErikAigner 能否请您详细介绍一下这个 API 的作用。想知道是否可以将秘钥与 API 秘钥配合使用。 - Mehavel
你的官方Web应用程序是否仅直接从JS访问API?这是必须的要求吗?您能否重新设计它,以便仅从服务器端代码访问API?通常,当您想避免未经授权的API客户端冒充合法客户端时,您会希望使用某种方法对API密钥进行签名,但这需要在服务器端执行,否则您还需要在JS代码中公开API密钥。 - lanzz
Web应用程序使用EmberJS编写,因此基本上是100%的JS。 但是我对前端和后端架构有100%的控制权,因此可以引入一些更改。 - user187676
3个回答

5
你需要区分网络和非网络客户端。网络访问密钥不能用于非网络,反之亦然。对于网络客户端,可以进行 referer 检查等操作。你还可以为应用程序动态创建访问密钥,并每日(或每次会话)自动更改它们。你还可以为你的应用程序添加一些特殊验证,例如通过混淆 JS 计算的附加密钥,仅适用于你的应用程序。
没有什么能阻止恶意用户模拟浏览器、执行 JS、操纵它,然后做坏事——但你可以使它变得足够烦人,以至于他们决定不值得这样做。像权限等真正重要的东西显然需要在服务器端进行检查,因此滥用你的 API 不应该是一个大问题。你将不得不像处理常规 Web 应用程序滥用一样处理通过你网站的 API 密钥滥用 - IP 封锁等。
你仍然需要保密非网络客户端的 API 密钥。这只能通过混淆来不可靠地实现,你可以把它交给客户端开发者。如果他们的密钥被泄露并被滥用,你可以吊销它,他们将有动力修复它。
请看OAuth 2.0,它们实现了许多对您有用的功能。即使您不想使用它,也可以从中获取一些灵感。OpenStreetMap在其基于Flash的编辑器中使用OAuth(不确定是1还是2);只要由已登录用户从同一来源调用,OAuth权限授予就会自动完成。对于第三方应用程序,用户需要手动完成。您可能需要检查一下。

1
我之前考虑过其中的一些点。1) 推荐人可以轻易被欺骗。2) 如果我的应用每天或者其他频率可以获取新的凭证,那么任何人也都可以。3) 混淆只需要更多一点时间,但并不能起到阻挡作用。无论如何,我会检查权限并且对每个IP/会话进行速率限制。 - user187676
  1. 我不知道如果你是一个在你不能控制的浏览器中的网站,如何欺骗 referer。(当然,如果你控制客户端,那就可以了。)2) 你的应用程序从你的服务器加载。自动更改凭据、重新排序变量,然后重新混淆文件(例如使用 Closure 编译器)很容易。自动提取正确的变量是可能的,但并不简单,这可能足以让攻击者决定不值得去做。你无法获得完美的保护。 3) 混淆非常擅长使窥探密钥变得非常烦人,即不值得去做。
- Jan Schejbal
广告1)请参见问题:“API不仅适用于Web应用程序” - user187676
@ErikAigner:这就是为什么我建议为Web应用程序和非Web应用程序使用单独的密钥。如果非Web应用程序决定使用Web应用程序密钥,它将不得不欺骗引用者和任何您实施的“这是真正的浏览器”检查。同样,没有办法实现完全保护。攻击者可以在修改后的浏览器中打开您的Web应用程序并更改JS的任意部分,即使您以某种方式确保只有您的页面可以访问API。我怀疑在他自己的机器上模仿您的Web客户端是否会给攻击者带来任何优势。 - Jan Schejbal
除非你有充分的理由相信攻击者会尝试这样做(那么你可能需要重新制定安全模型——你不能完全信任客户端),我的建议是在web/non-web中保持密钥分离,但只有在实际遭遇攻击时才实施进一步的对策(这种情况很可能永远不会发生)。 - Jan Schejbal

5
你无法仅使用单个API密钥使API安全。您所描述的API密钥基本上是公共密钥,您需要某种类型的私钥进行安全身份验证和机制来传递它。
您问Twitter是如何解决这个问题的。他们使用OAuth 1.0a。以下是从Twitter Developer FAQ中简要描述它与API密钥的关系。
“API密钥”通常指OAuth消费者密钥。此字符串标识在向API发出请求时使用的应用程序。在OAuth 1.0a中,“API密钥”可能指的是此消费者密钥和“消费者密钥”的组合,后者是用于安全“签名”您对Twitter的请求的字符串。大多数对Twitter的请求除了应用程序上下文外,还需要用户上下文。用户上下文通过使用另一种类型的令牌/密钥称为“访问令牌”来呈现。有关更多信息,请参见获取访问令牌。

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

以下是如何使用HMAC身份验证来保护Web API的说明。

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


当JS应用程序是客户端时,HMAC/OAuth不会有任何帮助。你提到了“并使用自己的安全方法来保护Facade API”。这部分实际上很有意思。你的安全方法是什么? - user187676
使用OAuth、表单身份验证或基本身份验证对Web应用程序的用户进行身份验证,然后在Web客户端和Facade API之间使用过期的Cookie或令牌。 - Kevin Junghans
这个问题不是关于身份验证,而是要求用户在身份验证的基础上使用 API 密钥 - user187676
如果API密钥不是用于身份验证,那么你会用它做什么?难道你不是在使用API密钥来识别谁在使用你的API吗?我提供了使用cookie和令牌而不是API密钥的替代方案。你可以继续为你的API使用API密钥,但是没有办法在JavaScript中使其安全。具体来说,你的问题是,“...,还是我必须接受任何恶意使用API的人都可以利用这一事实?”答案是肯定的,如果你只使用API密钥。 - Kevin Junghans
不,API密钥仅用于检查哪个用户正在使用哪个客户端 - 而不是用于身份验证本身。我只想要求第三方用户除了进行身份验证外还要使用API密钥。 - user187676
@ErikAigner “手机和桌面”客户端,它们是第三方应用程序还是您自己开发的?我猜想,在出现滥用/恶意活动的情况下,您希望封锁手机/桌面/网络客户端上的个别用户而不是整个应用程序,对吗?如果是这样,那么认证和API授权必须一起考虑。OAuth似乎是一个很好的选择。https://developers.google.com/accounts/docs/OAuth2 - pavelgj

-2

通用API工作流程:

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

网站 - 登录

  1. 用户提供用户名和密码登录
  2. 一个密钥被创建并存储到cookie中

网站 - API访问

  1. 客户端发送请求
  2. 请求基于cookie的密钥进行身份验证和授权(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-KEY。

关键点是您的API以两种方式验证请求。使用cookie的秘密或API KEY。因此,无需在网站上公开API-KEY。

请注意,对于使用API-KEY的客户端,没有会话涉及。每个请求仅通过API-KEY进行身份验证。因此,这些客户端被视为非WEB应用程序。


这并没有回答问题中实际被问到的内容。 - user187676
问题不是如何避免在网站上公开API密钥吗? - Andreyy
这只是会话认证的解释,而我无论如何都会这样做。你的回答中没有强制要求某人获取 API 密钥或如何将其与常规 Web 应用程序/客户端区分开来(这是问题所在)。 - user187676
Web应用程序不需要API密钥,因为基于会话的身份验证授予对API的访问权限。对于其他客户端,除非提供API密钥,否则无法访问API。这里没有涉及到会话。未经会话身份验证且带有API-KEY的请求被视为非Web应用程序。 - Andreyy
那么,有什么阻止别人使用基于会话的身份验证而不是使用API密钥吗?如果问题如此微不足道,我就不会花费250个积分来解决它了... - user187676
你可以模拟浏览器,而且你无法阻止它。基于会话的身份验证只能通过提供用户名和密码来实现。在此过程中,API-KEY从未被暴露,因为它是不必要的。 - Andreyy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接