我有一个向用户发送电子邮件的应用程序,每封电子邮件都包含指向用户必须访问的页面的链接。
该链接是唯一ID +随机数的md5。
链接如下:www.domain.com/index.php?id_page=<the md5>
在索引页面上,我将$_GET["id_page"]
保存在会话变量$_SESSION["id_page"]
中,然后将用户重定向到他必须看到的页面(仅当链接包含时,才会将用户重定向到该页面id_page
)。
如何改进此方法并使其更安全?我如何保证用户进入指定给他们的页面 - 并且不能进入任何其他页面?
答案 0 :(得分:1)
您可以在网址中添加他们的电子邮件。某人猜测别人的电子邮件和相关哈希的概率大约是0。
答案 1 :(得分:1)
你关注的是时间问题,而不是安全问题:)。如果你允许任何人猜测无限数量的id_page值,那么给定足够的时间,最终会有人发生随机有效的id_page值。
你唯一的真正的防御措施是增加哈希的长度,导致它(平均)在随机有效的id_page(大约几个月或几年)上花费更多时间)。这可以通过使用sha256或sha512而不是md5来实现。
另一种方法是将某人锁定一段时间,如果他们在id_page值上有3次连续的错误猜测。这将大大减少他们在给定时间内可以尝试的值的数量。
最后,如果用户在重定向时已经登录,那么您还可以将生成的哈希值存储在数据库表中。这样,您可以将特定哈希映射到表中的一个且只有一个用户ID。如果用户试图访问他们在数据库中不对应的哈希页面,那么您可以将它们重定向到其他位置。
答案 2 :(得分:1)
一种可以很好地防止猜测ID号的方法是在ID中添加某种填充,然后将其转换为base32。当然,这并没有消除完全猜测ID的能力,但它确实会让任何偷窥的人都花费更多的时间。
如果您的网址为www.domain.com/index.php?id_page=1,则可以将ID转换为应用中唯一的ID,例如:
填充ID = id x 9 - 2
7 = 1 x 9 - 2
16 = 2 x 9 -2
25 = 3 x 9 - 2
然后,您可以将新的填充ID转换为基数32,即
7 = G4
16 = GE3A
25 = GI2Q
然后新的url(id为1):
www.domain.com/index.php?id_page=G4
使用这种方法,如果有人猜到1-6的基数为32,那么它将返回404,因为你的ID实际上被填充为7.猜测8-15不会返回解析的ID,因为下一个id为2,填充为16,依此类推。
这不仅会使查询字符串的大小变小,而且也不会使用明显的MD5哈希值,这种哈希值很容易使用词典顺序浏览。
如果您希望将页面链接到特定用户,那么您没有理由不能将更多值附加到新的padded_id(让我们称之为哈希)。
例如,如果您的用户ID为12,并且您只希望该用户能够访问ID为10的页面,则需要创建包含以下两个值的哈希:
page_id(10)-user_id(12),使用上面的例子,这将产生:
(10 x 9 - 2) (12 x 9 - 2)
88-106
HA4A-GEYDM
您现在有一个很好的小散列链接,可以保护到单个用户ID。上面示例中的填充方法相当简单,但它可以让您全面了解如何解决问题。
答案 3 :(得分:1)
不要打扰散列或创建唯一的ID或任何东西。复杂性无助于你。
相反,只需生成一个随机令牌,然后使用它。 256位随机令牌应该足以满足您的任何需要。
所以,使用mcrypt
(核心扩展名):
$token = strtr(
base64_encode(mcrypt_create_iv(256/8, MCRYPT_DEV_URANDOM)),
'+/',
'-_'
);
这将为您提供44个字符的字母a-zA-Z0-9-_
结果,其中包含256位随机熵。
无需对结果或任何内容进行哈希处理。随机数据就足够了。
要了解原因,您需要了解The Birthday Problem。
基本上使用256位随机值,如果2个令牌发生碰撞的可能性为1%,则需要生成4.8e37令牌(即48后跟36 0)。
要获得10e-18碰撞的机会(通常被视为安全),您需要生成4.8e29令牌。
由于这些数字比你产生的数字更多,因此2个令牌碰撞的可能性极小。
另一个问题是人们猜测令牌。好吧,MCRYPT_DEV_URANDOM
使用底层操作系统的随机池。这意味着人们更有可能猜测你的“唯一身份和随机数”,而不是猜测这里生成的令牌。
因此,简而言之,只需使用随机令牌即可: - )