21

我有一个使用 OAuth 1.0a 对使用它的应用程序进行身份验证的 api。它正在替换一个旧的 api,该 api 使用了许多已被弃用的自定义构建和大杂烩调用。

众所周知,OAuth 1.0a 在(客户端)Javascript 中是不安全的,因为它依赖于保密的消费者秘密。这是不可能的,因为源始终可见。

我们有 Chrome、Firefox、IE 和 Safari 的浏览器扩展,将来需要使用这个 api。这些扩展大部分或完全用 Javascript 编写,因此存在安全问题。

这些扩展是内部的,因此可以使用自定义身份验证方法来获取其访问令牌。

我计划实施的内容如下:

  • 用户在浏览器中登录网站。
  • 该网站向他们发送一个带有会话密钥的 cookie。
  • 然后,我们的扩展程序获取该 cookie 并将其传递给 api。
  • 该 api 验证它是一个有效且活动的会话,并向扩展发出其访问令牌。
  • 这些令牌在到期前最多持续一小时。
  • javascript 发布的 cookie 的速率限制也会较低。

它在以下假设下运作:

  • 如果另一个应用程序可以访问您的 cookie,那么他们无论如何都可以在网站上冒充您,因此访问 api 没有什么不同。
  • 所有身份验证方法仍然通过我们的控制。
  • 令牌的定期到期意味着如果它们被破坏,那么利用的时间是有限的。

我的问题是,这是限制对 api 的访问的安全方法吗?有没有更好的?

一些笔记。 我知道 chrome 扩展可以请求访问给定站点的 cookie 的权限。我相信 Firefox 扩展也可以做到这一点。

显然,我们不希望通过任何页面上的 javascript 访问我们的 cookie,否则我们会将自己暴露于 XSS 攻击,因此它们只需要通过扩展来访问。

4

3 回答 3

8

我编写了一个通过用于 OAuth 的 javascript 库进行 OAuth 登录的站点。这是工作流程:

  1. OAuth 仅在具有 LocalStorage 的浏览器上受支持
  2. 登录表单将检查 LocalStorage 中的 OAuth 密钥,并在存在 OAuth 密钥时自动尝试 OAuth 登录。
  3. 登录表单上有一个“记住我”复选框,因此用户可以在登录时为他们创建 OAuth 令牌。
  4. 成功登录并记住我将:
    • 查找或创建名称等于 User Agent 的 ClientApplication,并在必要时创建令牌
    • 在 HTML 响应中使用 javascript 标记进行响应。javascript 标记将调用带有作为参数传递的标记的 javascript 函数。此函数会将 OAuth 令牌保存到 LocalStorage。
  5. 不成功的 OAuth 登录尝试将:
    • 在 HTML 响应中使用 javascript 标记进行响应。javascript 标记将调用 javascript 函数来清除 OAuth 令牌的 LocalStorage 设置。这将阻止额外的 OAuth 登录尝试

这个过程有更多细节,如果你愿意,我可以告诉你更多。

于 2011-06-09T15:53:45.420 回答
3

对于之后来这篇文章的人来说,只是一些想法:

  1. “这安全吗?” -> 这取决于我们要保护的威胁。我将在以下几点假设该解决方案已经暗示了受信任的网络链接(以防止在途令牌或凭据拦截尝试)。但是,描述中缺少一个关键元素,因为它没有提到我们是否保护 API 免受未经授权的用户(人类)或未经授权的 API 客户端(如在浏览器中运行的恶意扩展)的影响。前者可以通过可用的开放标准很容易地实现,而人们应该忘记试图阻止未经授权的扩展的访问,因为该模型从根本上依赖于开源客户端技术。这与工作站安全有关,而不是设计一个强大的身份验证/授权机制。

  2. 基本上有两种方法可以设计平台。通过检测 API 以允许查询身份验证服务。或者使用基于令牌的访问,其中 API 将在它收到的每个请求中请求存在一个有效令牌,而不是它的发射器。这意味着使用一个新角色扩展身份验证服务:API 票证发行者,这很少引起人们的兴趣。在阅读这个命题时,我们似乎正在通过将会话令牌转发到 API 来合并两个世界。这是错误的。首先,这不是设计基于 cookie 的会话令牌的原因。其次,它强制 API 与用户认证服务的会话管理系统实现某种实时同步链接 -> 我们可以轻松避免的耦合。

  3. 我假设主要目标是保护 API 免受未经授权的用户的攻击,并且我们不会尝试解决依赖本地系统访问的威胁。

  4. 现在,考虑到我们不会在 API 中实现身份验证逻辑,我们必须依赖用户对身份验证服务进行身份验证的模型,从而使任何底层扩展都能够请求访问令牌。

  5. 这将原始场景修改如下:

    • 用户使用浏览器登录网站。
    • 该网站发布一个包含会话密钥的 cookie。
    • 该扩展现在可以向身份验证服务发送票证请求。请求将包括会话令牌(默认浏览器行为),因此将被验证。
    • 扩展程序收到票证后,会将其转发到 API 并请求会话令牌。
    • API 通过询问会话管理器来验证票证。如果会话管理器说“是的,我制作了这张票并且它仍然有效”,API 会生成一个会话令牌并将其返回给扩展。此令牌将插入所有后续请求,而不是票证。这将避免会话管理器上的任何不必要的工作量。
    • 令牌(不要将其与票证混淆)可以有一个非常短的生命周期,例如几分钟 -> 如果它过期,则扩展只是返回到身份验证服务并要求一个新票证(返回到上面的步骤 3)。
  6. 上述解决方案基本上取决于票证和令牌的安全性。他们的设计至少必须针对以下 5 个剩余威胁实施对策:i)尝试猜测票证/令牌(足够安全的随机生成),ii)尝试计算票证/令牌(足够大的熵),iii)尝试重用票证/令牌(到期),iv) 尝试篡改有效票证/令牌(完整性检查),v) 尝试在没有有效令牌/票证的情况下访问 API(验证 API 收到的每个请求中的令牌)。

  7. 这种方法的另一个优点是我们可以通过发出扩展特定令牌来优化资源分配,这反过来将触发 API 上的特定逻辑(减少 API 访问、减少生命周期、请求限制等)。

希望能帮助到你。

于 2012-02-07T00:45:10.057 回答
2

所以你在 example.com 上有一个网站,它需要访问 api.com。您的扩展假定用户登录到 example.com,提取会话 cookie 并将其传递给 api.com 以获取 Oauth 令牌。听起来很合理,但有更简单的方法,无需编写浏览器插件。

在您的情况下, api.com 将与 example.com 通信以验证会话 cookie。这两个系统之间存在很强的依赖性。OAuth 通常用于 example.com 和 api.com 互不信任的情况。

因为这两个系统已经相互信任,你可以做各种事情来简化架构:

  1. 您可以创建一个托管在 example.com/api/* 上的代理来验证会话,然后盲目转发到 api.com/*。就浏览器而言,没有跨域请求,所以一切正常。
  2. 您可以跨域使用联合登录。这比代理方法更复杂,但您可以轻松找到适合您平台的现有实现。
于 2011-06-09T20:25:18.340 回答