sme*_*eeb 3 security authentication hash hmac jwt
我正在为网络应用程序构建后端。当新用户访问该网站并单击“注册”按钮时,他们将填写一个超级简单的表格,要求他们提供用户名+密码,然后提交。这会提示服务器向该电子邮件地址发送一封验证电子邮件。然后,他们将检查电子邮件,单击链接(验证电子邮件),然后被路由到登录页面,以便他们可以选择登录。
为了验证他们的电子邮件,当服务器生成电子邮件时,它需要创建(并存储)一个验证令牌(可能是 UUID)并将其附加到电子邮件中的此链接,以便该链接看起来像这样:
vt=12345
“验证令牌”在哪里(也可能是 UUID)。因此,用户单击此链接,我的GET v1/users/verify
端点查看令牌,以某种方式确认其有效,并进行一些数据库更新以“激活”用户。他们现在可以登录。
当用户想要取消订阅接收电子邮件时,或者当他们忘记密码并需要恢复密码以便可以登录时,也会出现类似的情况。
用户想要停止接收电子邮件,但仍然想要使用该应用程序。他们点击我们发送给他们的每周新闻通讯中的“取消订阅”链接。此链接需要包含某种类似的“取消订阅令牌”,该令牌与上面的验证令牌一样,是在服务器上生成并存储的,用于验证用户取消订阅电子邮件的请求。
在这里,用户忘记了密码,需要恢复它。因此,在登录屏幕上,他们单击“忘记密码”链接,然后会看到一张表格,他们必须在其中填写电子邮件地址。服务器向该地址发送一封电子邮件。他们检查了这封电子邮件,其中包含一个表单链接,他们可以在其中输入新密码。此链接需要包含一个“重置密码令牌”,与上面的验证令牌一样,它是在服务器上生成并存储的,用于验证用户更改密码的请求。
因此,我们需要解决三个非常相似的问题,所有问题都需要使用我所说的“一次性(OTO)安全令牌”。这些 OTO 代币:
security_tokens
表中)我想出的解决方案很简单……几乎太简单了。
对于令牌,我只是生成随机 UUID(36 个字符)并将它们存储到security_tokens
具有以下字段的表中:
[security_tokens]
---
id (PK)
user_id (FK to [users] table)
token (the token itself)
status (UNCLAIMED or CLAIMED)
generated_on (DATETIME when created)
Run Code Online (Sandbox Code Playgroud)
当服务器创建它们时,它们是“未声明的”。当用户单击表内的链接时,他们将被“声明”。后台工作人员作业将定期运行,以清理任何已声明的令牌或删除任何已“过期”(基于其generated_on
字段)的未声明令牌。该应用程序还将忽略之前已声明的任何令牌(并且尚未清理)。
我认为这个解决方案可行,但我不是一个超级安全人员,我担心这种方法:
就像上面的第二个一样,我想知道是否应该使用哈希/HMAC/JWT 相关机制,而不是一个简单的 UUID。也许有一些聪明的加密/安全人员找到了一种方法,使这些令牌以安全/不可变的方式包含声明状态和到期日期本身。
你的路线是对的
根据我想要的功能,我的应用程序中有一个非常相似的方法。我有一个包含每个用户的表(用户表),我可以用它来引用每个单独的帐户并根据他们的身份执行操作。通过添加用户帐户和自我管理选项可以缓解许多安全威胁。以下是我如何应对其中一些漏洞的方法。
验证您的电子邮件
当用户注册时,服务器应该使用该类RNGCryptoServiceProvider()
生成一个足够长度的随机盐,使其永远无法被实际猜测到。然后,我对盐进行哈希处理(单独)并对它应用 Base64 编码,以便可以将其添加到 Url 中。通过电子邮件将完整的链接发送给用户,并确保根据表UserId
中的相关内容存储该哈希值Users
。
用户在收件箱中看到一个漂亮整洁的“单击此处验证您的电子邮件地址”,然后可以单击该链接。它应该重定向到接受可选 url 参数的页面(例如,mywebsite.com/account/verifyemail/myhash
然后检查服务器端的哈希值。然后,该站点可以根据其存储在数据库中的激活哈希值检查哈希值。如果它与记录匹配,那么您应该将该Users.EmailVerified
列标记true
并提交到表中。然后,您可以从表中删除该验证记录条目。
干得好,您已经成功验证了用户的电子邮件地址的真实性!
重设密码
在这里,我们实现了类似的方法。但是,我们最好将记录存储在 PasswordResetRequest 表中,而不是验证记录,并且不要删除记录 - 这使您可以查看密码是否以及何时重置。每次用户请求重置密码时,您都应该显示一条匿名消息,例如“一封电子邮件已发送到您的主要电子邮件地址,其中包含进一步的说明”。即使未发送或帐户不存在,它也会阻止潜在的攻击者枚举用户名或电子邮件地址以查看它们是否已在您的服务中注册。同样,如果它们是真实的,请使用与之前相同的方法发送链接。
用户打开他们的电子邮件地址并单击链接。然后它们会被重定向到重置页面,例如mywebsite.com/account/resetpassword/myhash
。然后,服务器针对数据库运行 url 中的哈希值,如果结果为真,则返回结果。现在,这是棘手的部分 - 你不应该长时间保持这些活动。我建议使用一列将哈希值链接到Users.UserId
,其中一个称为ExpiraryDateTime
包含类似内容Datetime.Now.AddMinutes(15)
(这使得以后更容易使用),另一个称为IsUsed
布尔值(默认情况下为 false)。
单击链接时,您应该检查链接是否存在。如果没有,请为他们提供默认的“该链接有问题。请请求新链接”文本。但是,如果链接有效,您应该检查它,Used == false
因为您不希望人们多次使用同一链接。如果不使用,那就太好了!让我们检查一下它是否仍然有效。最简单的方法很简单if (PasswordResetRequest.ExpiraryDateTime < DateTime.Now)
- 如果链接仍然有效,那么您可以继续重置密码。如果不是,则意味着它是不久前生成的,您不应该允许它再使用。说真的,有些网站今天仍然允许您生成链接,如果您的电子邮件在 1 个月后被黑客入侵,您仍然可以使用重置链接!
我还应该提到,每次用户请求重置密码时,您都应该检查表中的现有记录以获取有效链接。如果其中一个有效(意味着它仍然可以使用),那么您应该立即使其失效。将哈希值替换为一些辅助文本,例如“无效:用户请求新的重置链接”。这也让您知道他们请求了多个链接,同时也使他们的链接无效。如果您真的想通过聪明地将整个“无效:用户请求新的重置链接”作为编码的 URL 偷偷进入他们的浏览器来阻止人们尝试使用过期链接,您也可以将其标记为“已使用”。您永远不应该为同一个帐户设置多个激活的重置链接!
取消订阅
为此,我在数据库中有一个简单的标志,用于确定用户是否可以接收促销优惠和新闻通讯等。所以 aUsers.SubscribedToNewsletter
就足够了。他们应该能够登录并在电子邮件设置或通信首选项等中更改此设置。
一些代码示例
这是我的 C# RNGCryptoServiceProvider 代码
public static string GenerateRandomString(RNGCryptoServiceProvider rng, int size)
{
var bytes = new Byte[size];
rng.GetBytes(bytes);
return Convert.ToBase64String(bytes);
}
var rng = new RNGCryptoServiceProvider();
var randomString = GenerateRandomSalt(rng, 47); // This will end up being a string of almost entirely random bytes
Run Code Online (Sandbox Code Playgroud)
为什么我使用 RNGCryptoServiceProvider?
(这RNGCryptoServiceProvider()
是其安全库中的一个 C# 类)允许您基于完全随机且不可重现的事件生成看似随机的字节字符串。像这样的类Random()
仍然需要使用某种内部数据来根据可预测的算法事件(例如当前日期和时间)生成数字。它RNGCryptoServiceProvider()
使用 CPU 温度、正在运行的进程数等信息来创建无法重现的随机内容。这允许最终的字节数组尽可能随机。
为什么我要进行 Base64 编码?
Base64 编码将生成仅包含数字和字母的字符串。这意味着文本中不会有符号或编码字符,因此在 URL 中使用是安全的。这并不是一个安全功能,但它确实允许您只允许在方法的参数中使用数字和字母,并过滤或拒绝任何不符合此标准的输入。例如,过滤掉任何包含 V 形符号的输入<
,>
应该可以防止 XSS。
要记住的事情
您应该始终假设包含哈希值的链接无效,直到您对其执行每次检查以确保其通过要求。因此,您可以执行各种if
语句,但除非您通过了每一个语句,否则您会将默认的下一步操作留给用户某种形式的错误。为了澄清这一点,我应该检查密码重置链接是否有效,然后未使用,然后仍在时间窗口内,然后执行重置操作。如果它未能通过任何这些要求,默认操作应该是向用户显示错误,指出它是无效链接。
给其他人的注释
因为我非常有信心这不是唯一的方法,所以我想声明,多年来我一直这样做,从未让我失望过,并且让我的公司通过了几次广泛的渗透测试。但如果有人有更好/更安全的方法,请提供一些信息,因为我很乐意了解更多信息。如果您对我提到的特定部分还有任何其他问题或需要澄清,请告诉我,我会尽力提供帮助
归档时间: |
|
查看次数: |
4996 次 |
最近记录: |