【问题标题】:Transmit user password for a rest api为 REST API 传输用户密码
【发布时间】:2012-10-14 16:22:50
【问题描述】:

我正在设计一个 REST API,但在验证用户的安全性方面存在一些问题。 对于身份验证,我不希望密码以纯文本形式通过网络发送。

为了绕过这个问题,我可以发送密码的 SHA-256 哈希值(用户名为 salt),因此密码永远不会以纯文本形式发送。在我的数据库中,我将存储以下哈希值:SHA256(password + salt),如果两个哈希值匹配,我将进行比较。

此选项的问题是我将使用快速哈希算法计算哈希,并且盐不是随机的。

在安全方面,最佳做法是使用慢速签名算法,带有随机盐(如 bcrypt)。

慢速算法不是问题,我可以在客户端使用 bcrypt,但是对于 salt 我不知道该怎么做:

  • Bcrypt 需要一个定义大小的盐,所以我不能输入用户名
  • 如果我使用的是随机盐,客户端如何在计算密码哈希之前知道这个盐的值?

所以我可以看到 3 个选项,但没有一个令人满意:

  • 我以纯文本形式发送密码(我正在使用 SSL)并将 bcrypt 存储在数据库中 => 仍然容易受到中间人的攻击
  • 我使用 SHA256 并发送盐为用户名的哈希(仍在使用 SSL)=> 数据库中的哈希不太安全
  • 我使用 bcrypt,我有一个两步过程:我要求给定用户的 salt,然后发送该用户的哈希(仍在使用 ssl)=> 通过尝试使用其他用户名登录我可以获得他的盐,不好吃

有人有更好的解决方案或建议吗?

【问题讨论】:

  • fwiw,如果您使用 ssl(正确),密码不会以明文形式通过网络发送 - 它使用服务器的公钥加密(或者更确切地说,使用通过公钥)。我同意你想使用某种哈希(它不会有伤害),尽管我没有仔细阅读以弄清楚你的各种问题是什么
  • 您的第一个选项不会受到 MITM 的攻击,除非有人破坏了 SSL 而我还没有听说过。
  • “为了绕过这个问题,我可以发送一个 SHA-256 哈希”——这只是改变了需要发送到服务器的数据,它不会为该数据添加任何保护。 SSL是加密。使用 SSL。使用 SSL 并非“明明白白”
  • 这可能是security.stackexchange.com 的更好答案。如果您希望我将其迁移到那里,请告诉我。
  • @darkheir 但是如果你已经在使用 SSL,除非 SSL 被破坏,否则无法拦截哈希。

标签: php security rest web


【解决方案1】:

我认为您可能在这里混淆/混淆了一些问题:

  • 如果您将散列(密码 + 用户名)存储在服务器上,并且身份验证涉及发送散列(密码 + 用户名),那么您实际上没有比仅将密码存储在服务器上更好的方法。仅长期存储哈希的目标是,如果您有数据泄露(即,攻击者获得了对数据库的访问权限),他们仍然无法产生正确的值来进行身份验证。但如果你只是做一个简单的比较,这仍然是个问题。
  • 散列+加盐的正确用法是:(1)服务器存储(Salt,hash(Password + Salt)的元组;(2)用户发送(声称的密码);(3)服务器计算哈希(声称的密码+盐) ); (4) 如果 hash(claimed Password + Salt) == hash(Password + Salt),那么它们是真实的。这样,即使攻击者可以访问数据库,他们也无法生成声称的密码这样哈希(声称的密码+盐)是有效的。
  • 通过 SSL 发送明文密码不是“明文”。根据@NullUserException 的评论,除非攻击者破坏了 SSL。只有服务器才能获得密码的值(假设服务器的公钥是有效的,这完全是另一回事)。

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    在客户端散列的方法有几个优点。其中之一是服务器永远不会获得真正的密码,所以如果服务器以任何方式受到损害,它仍然不会获得真正的密码。另一个是,如果您打算使用慢散列,它可以减轻服务器端的负载。

    但是,哈希密码旨在保护您,以防数据库遭到破坏并且哈希被盗。这意味着如果有人掌握了散列密码,他们仍然可以通过发送散列来冒充用户。这意味着,即使你在客户端散列,你仍然需要在服务器上重新散列。

    另一个潜在的缺点是,这可能会疏远未启用 JavaScript 的部分用户群。


    解决您的问题:

    Bcrypt 需要一个定义大小的盐,所以我不能输入用户名

    不要将用户名用作盐。盐应该是唯一的,用户名(及其派生词)当然不是唯一的。我所说的唯一并不是指服务器唯一,而是在任何地方都是唯一的。请改用加密随机数。

    如果我使用随机盐,客户端如何在计算密码哈希之前知道这个盐的值?

    只需让服务器预先发送盐(随机数)。您也可以在客户端上执行此操作,但据我所知,Javascript 没有 CSPRNG,您仍然需要将 nonce 发送回服务器。

    我以纯文本形式发送密码(我正在使用 SSL)并将 bcrypt 存储在数据库中 => 仍然容易受到中间人的攻击

    SSL 旨在防止中间人攻击。除非它以某种方式损坏,否则这不会成为问题。

    我使用 SHA256 并发送盐为用户名的哈希(仍在使用 SSL)=> 数据库中的哈希不太安全

    不要将用户名用作盐。就像我之前说的,你必须在服务器端进行散列,不管你是否在客户端这样做。

    我使用 bcrypt,我有一个两步过程:我要求给定用户的 salt,然后发送该用户的哈希(仍然使用 ssl)=> 通过尝试使用我可以获得的其他用户名登录他的盐,不是很好

    确实不厉害。

    【讨论】:

      【解决方案3】:
      1. 将盐设为常量,假设将其设为用户名的哈希值。所以hash_val = HASH(HASH('username') + 'password') 存储在服务器端。
      2. 对于身份验证,您的服务器发送一个一次性随机值,即:nonce = HASH(RAND())
      3. 您的客户端根据输入凭据client_hash = HASH( nonce + HASH(HASH('username') + 'password')) 计算以下内容并将其发送回服务器。
      4. 服务器执行相同的操作,比较生成的哈希值,并丢弃 nonce。

      通过这种方式,通过网络发送的哈希仅使用一次,并且您可以免受“重放”和 MITM 攻击。

      此外,请查看 PBKDF 之类的东西来存储密码而不仅仅是散列,这使得暴力破解和彩虹表完全不切实际。 Here's the PHP implementation I'm using since it's not in PHP yet.

      【讨论】:

      • 很好的解决RE重放攻击! (尽管 SSL 应该否定它的需要)这里唯一的问题是所有这些盐渍业务的全部目的是处理潜在的数据泄露;但是,如果攻击者获得了对数据库的访问权并学习了“HASH(HASH('username') + 'password)),他们仍然可以成功回复随机数挑战。
      • 如果攻击者已经在数据库中,那么他们为什么需要登录呢? :P
      • 经常会看到泄露用户数据库内容的攻击。与 Anonymous 和 HBGary arstechnica.com/tech-policy/2011/02/… 发生的这一引人注目的事件正是这种失败的一个例子
      • 不要使用hash(username) 作为盐,使用随机数。此外,PBKDF2 不是散列密码的最佳选择,因为它仍然很容易并行化。如果你要走那条路,请使用 bcrypt。
      • 我在这里看到的三个选项是 1. 我提出的,这取决于数据库的安全性。 2. 发送明文密码,这取决于您对 SSL 连接的信任程度。 3. 实现类似 Kerberos 的东西。
      【解决方案4】:

      如果可能,创建 API 密钥和密钥(API 用户名/密码,显然每个用户都是唯一的)以使用 API。您应该在您的站点界面中提供一个选项来激活/停用 API 访问以及重新生成 API 密钥和密钥的选项。在这里,用户会在这个界面上看到API的API/Secret key。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-17
        相关资源
        最近更新 更多