传输休息api的用户密码

时间:2012-10-24 15:28:15

标签: php security rest web

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

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

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

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

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

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

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

  • 我以纯文本发送密码(我正在使用SSL),我将bcrypt存储在db =>中仍然容易受到中间人的影响
  • 我使用SHA256并发送哈希,其中salt是用户名(仍使用SSL)=>数据库中的哈希值不太安全
  • 我使用bcrypt并且我有两个步骤:我要求给定用户的salt然后发送该用户的哈希值(仍然使用ssl)=>通过尝试使用其他用户名登录,我可以获得他的盐,而不是很棒

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

4 个答案:

答案 0 :(得分:2)

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

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

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


解决您的观点:

  

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

不要将用户名用作盐。盐应该是唯一的,用户名(及其派生词)肯定不是唯一的。独一无二的我并不仅仅意味着服务器的独特之处,而是无处不在。请使用加密随机数。

  

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

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

  

我以纯文本发送密码(我正在使用SSL)并且我将bcrypt存储在db =>中仍然容易受到中间人的影响

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

  

我使用SHA256并发送哈希,其中salt是用户名(仍使用SSL)=> db中的哈希值不太安全

不要将用户名用作盐。就像我之前说的那样,无论你是否在客户端做到这一点,你都必须在服务器端进行哈希。

  

我使用bcrypt并且我有两个步骤:我为给定用户请求salt然后发送该用户的哈希值(仍然使用ssl)=>通过尝试使用其他用户名登录,我可以获得他的盐,而不是很棒

确实不太棒。

答案 1 :(得分:1)

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

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

希望这有帮助!

答案 2 :(得分:1)

  1. 使盐不变,让我们说它是用户名的哈希值。所以hash_val = HASH(HASH('username') + 'password')存储在服务器端。
  2. 对于身份验证,您的服务器会发送一次性随机值,即:nonce = HASH(RAND())
  3. 您的客户端根据输入凭据client_hash = HASH( nonce + HASH(HASH('username') + 'password'))计算以下内容并将其发送回服务器。
  4. 服务器执行相同的操作,比较生成的哈希值,并丢弃现时值。
  5. 通过这种方式,通过网络发送的哈希只使用一次,并且您可以免受“重播”和MITM攻击。

    另外,查看PBKDF之类的东西来存储密码而不仅仅是哈希,它使得强制执行和彩虹表都完全不切实际。 Here's the PHP implementation I'm using since it's not in PHP yet

答案 3 :(得分:0)

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