安全代码排列;安全方法论
我正在编写一个 Perl 电子邮件订阅管理应用程序,基于包含两个键码参数的 url。订阅时,脚本将为每个订阅者创建两个在数据库中唯一的密钥代码(请参阅下面的脚本示例)。
代码将使用 Digest::SHA qw(sha256_hex) 创建。我的理解是,确保数据库中的代码不重复的一种方法是在要编码的原始数据中创建唯一的前缀。 (另见下文)。
一旦一个人被订阅,我就会有一个人的数据库记录,其中有两个“代码”字段,每个字段都包含数据库中唯一的值。每个值都是 64 个字符长的字母数字字符的字符串,使用小写(仅?)az 和 0-9,例如:
code1: ae7518b42b0514d69ae4e87d7d9f888ad268f4a398e7b88cbaf1dc2542858ba3
code2: 71723cf0aecd27c6bbf73ec5edf dc6ac912f648683470bd31debb1a4fbe429e8
这些代码作为订阅管理 URL 中的参数在新闻通讯电子邮件中发送。因此,用户无需登录即可管理他们的订阅;但只需点击网址即可。
我的问题是:
如果一个订阅者试图猜测另一个人的这对代码的值,那么有多少种可能的组合不仅可以正确猜测代码 1,还可以正确猜测代码 2?我想,就像买彩票一样,一个人可能会很幸运,并且猜对了两者;但我想了解这样做的可能性及其对安全的影响。
如果猜中了组合,则该人将获得对数据库的访问权限;因此,我试图确定此方法提供的安全级别,与更常见的用户名和 8 字符密码方法相比(一般来说,密码本身可以被视为两个关键代码,但比上面的 64 个字符短得多。 )
我也欢迎任何有关此方法整体安全性的反馈。我注意到很多很多电子邮件通讯似乎使用类似的键码,并且不需要登录即可取消订阅等。主要问题(除了易用性之外)是一个人不应该能够取消订阅某人别的。
谢谢!
Peter(请参阅下面的代码生成片段)
请注意,每个 ID 和电子邮件都是唯一的。 该密码是“系统”密码,每个人都是相同的。
##
#!/usr/bin/perl use Digest::SHA qw(sha256_hex); $clear = `clear`; print $clear; srand; $id = 1; $email = '[email protected]'; $tag = ':!:'; $password = 'z9.4!l3tv+qe.p9@'; $rand_str = '9' x 15; $rand_num = int(rand( $rand_str )); $time = time() * $id; $key_data = $id . $tag . $password . $rand_num . $time; $key_code = sha256_hex($key_data); $email_data = $email . $tag . $password . $time . $rand_num; $email_code = sha256_hex($email_data); print qq~ ID: $id EMAIL: $email KEY_DATA: $key_data KEY_CODE: $key_code EMAIL_DATA: $email_data EMAIL_CODE: $email_code ~; exit;
I'm writing a Perl email subscription management app, based on a url containing two keycode parameters. At the time of subscription, a script will create two keycodes for each subscriber that are unique in the database (see below for script sample).
The codes will be created using Digest::SHA qw(sha256_hex). My understanding of it is that one way to ensure that codes are not duplicated in the database is to create a unique prefix in the raw data to be encoded. (see below, also).
Once a person is subscribed, I then have a database record of a person with two "code" fields, each containing values that are unique in the database. Each value is a string of alphanumeric characters that is 64 characters long, using lower case (only?) a-z and 0-9, e.g:
code1: ae7518b42b0514d69ae4e87d7d9f888ad268f4a398e7b88cbaf1dc2542858ba3
code2: 71723cf0aecd27c6bbf73ec5edfdc6ac912f648683470bd31debb1a4fbe429e8
These codes are sent in newsletter emails as parameters in a subscription management url. Thus, the person doesn't have to log in to manage their subscription; but simply click the url.
My question is:
If a subscriber tried to guess the values of the pair of codes for another person, how many possible combinations would there be to not only guess code1 correctly, but also guess code2? I suppose, like the lottery, a person could get lucky and just guess both; but I want to understand the odds against that, and its impact on security.
If the combo is guessed, the person would gain access to the database; thus, I'm trying to determine the level of security this method provides, compared to a more normal method of a username and 8 character password (which generically speaking could be considered two key codes themselves, but much shorter than the 64 characters above.)
I also welcome any feedback about the overall security of this method. I've noticed that many, many email newsletters seem to use similar keycodes, and don't require logging in to unsubscribe, etc. To, the primary issue (besides ease of use) is that a person should not be able to unsubscribe someone else.
Thanks!
Peter (see below for the code generation snippet)
Note that each ID and email would be unique.
The password is a 'system' password, and would be the same for each person.
#
#!/usr/bin/perl use Digest::SHA qw(sha256_hex); $clear = `clear`; print $clear; srand; $id = 1; $email = '[email protected]'; $tag = ':!:'; $password = 'z9.4!l3tv+qe.p9@'; $rand_str = '9' x 15; $rand_num = int(rand( $rand_str )); $time = time() * $id; $key_data = $id . $tag . $password . $rand_num . $time; $key_code = sha256_hex($key_data); $email_data = $email . $tag . $password . $time . $rand_num; $email_code = sha256_hex($email_data); print qq~ ID: $id EMAIL: $email KEY_DATA: $key_data KEY_CODE: $key_code EMAIL_DATA: $email_data EMAIL_CODE: $email_code ~; exit;
#
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
防止第三方取消订阅某人似乎非常复杂。为什么不为每个用户生成一个随机代码,并将其与用户名一起存储在数据库中?您使用的方法创建了一长串数字,但实际上并没有太多随机性。 SHA 是一种确定性算法,可彻底扰乱位,但不会增加熵。
对于 N 位真正的随机数,攻击者每次只有 1/(2^N) 的机会猜对。即使熵很小,例如 64 位,您的服务器也应该在攻击者获得很大的成功机会之前就限制来自攻击 IP 地址的取消订阅请求。他们如果能猜测用户的电子邮件密码,或者拦截传输中的未加密电子邮件,效果会更好。
这就是取消订阅代码通常很短的原因。不需要很长的代码,而且很长的 URL 更有可能被截断或输入错误。
This seems like a lot of complexity to guard against a 3rd party unsubscribing someone. Why not generate a random code for each user, and store it in the database alongside the username? The method you are using creates a long string of digits, but there isn't actually much randomness in it. SHA is a deterministic algorithm that thoroughly scrambles bits, but it doesn't add entropy.
For an N bit truly random number, an attacker will only have a 1/(2^N) chance of guessing it right each time. Even with a small amount of entropy, say, 64 bits, your server should be throttling unsubscribe requests from the attacking IP address long before the attacker gets significant odds of succeeding. They'd have better luck guessing the user's email password, or intercepting the unencrypted email in transit.
That is why the unsubscribe codes are usually short. There's no need for a long code, and a long URL is more likely to be truncated or mistyped.
如果你问“猜测”两个 256 位“数字”并找到你想要破解的特定人员有多困难,那么难度就是 2^512:1。比如说,如果数据库中有 1000 个用户,而攻击者并不关心他/她得到的是哪一个用户,那么这就是 2^512:1000 - 可能性没有显着变化。
但是,如果您的攻击者控制(足够接近)从您的计算机到用户计算机的邮件服务器之一,或者控制沿途的任何路由器,那么情况要简单得多,因为您的电子邮件以纯文本形式输出。一个及时发现电子邮件数据包的黑客将能够看到您嵌入的 URL,无论它有多少位。
与许多安全问题一样,问题在于付出多少努力与回报多少。密码很好,因为用户期望它们,因此发送需要密码才能输入的 URL 并不是一个重大障碍。如果您的 URL 甚至只是一个 SHA 密钥与密码挑战的结合,这几乎可以消除对您电子邮件的中间人攻击。就看你是否值得了。便宜、方便、安全。选择一个。 :-)
更多的努力是使用客户的公钥(而不是您的私钥)对您的电子邮件进行 gpg 加密。明显的缺点是 gpg(或 pgp)显然很少被使用,以至于普通用户不太可能设置它。同样,这将完全消除 MITM 攻击,并且不需要密码,因为它基本上使用客户端 gpg 私钥密码。
If you're asking how difficult it would be to "guess" two 256-bit "numbers", getting the one specific person you want to hack, that'd be 2^512:1 against. If there are, say, 1000 users in the database, and the attacker doesn't care which one s/he gets, that's 2^512:1000 against - not a significant change in likelihood.
However, it's much simpler than that if your attacker is either in control of (hacked in is close enough) one of the mail servers from your machine to the user's machine, or in control of any of the routers along the way, since your email goes out in plain text. A well-timed hacker who saw the email packet go through would be able to see the URL you've embedded no matter how many bits it is.
As with many security issues, it's a matter of how much effort to put in vs the payoff. Passwords are nice in that users expect them, so it's not a significant barrier to send out URLs that then need a password to enter. If your URL were even just one SHA key combined with the password challenge, this would nearly eliminate a man-in-the-middle attack on your emails. Up to you whether that's worth it. Cheap, convenient, secure. Pick one. :-)
More effort would be to gpg-encrypt your email with the client's public key (not your private one). The obvious downside is that gpg (or pgp) is apparently so little used that average users are unlikely to have it set up. Again, this would entirely eliminate MITM attacks, and wouldn't need a password, as it basically uses the client-side gpg private key password.
您实际上已经为给定的用户电子邮件 ID 生成了 1e15 个可能的不同哈希值(一旦与其他可以猜测的信息结合起来)。您也可以只提供相同长度的十六进制编码的随机数,并要求“取消订阅”链接包含要取消订阅的电子邮件地址或用户 ID。
我怀疑有人会花很大力气猜测 1 到 1e15 之间的数字,特别是如果您限制取消订阅请求,并在有人取消订阅时发送一封“谢谢,有人取消订阅您”电子邮件,并在其中添加一个新的订阅链接。
生成随机字符串的快速方法是:(
这为您提供 2^64,或大约 2*10^19 组合。如果您的速率限制,则为“充足”。)
You've essentially got 1e15 different possible hashes generated for a given user email id (once combined with other information that could be guessed). You might as well just supply a hex-encoded random number of the same length and require the 'unsubscribe' link to include the email address or user id to be unsubscribed.
I doubt anyone would go to the lengths required to guess a number from 1 to 1e15, especially if you rate limit unsubscribe requests, and send a 'thanks, someone unsubscribed you' email if anyone is unsubscribed, and put a new subsubscription link into that.
A quick way to generate the random string is:
(This gives you 2^64, or about 2*10^19 combinations. Or 'plenty' if you rate limit.)