简体   繁体   English

如何使此登录/重定向方法更安全

[英]How to make this login/redirect method more secure

I have an application which sends e-mails to users, each e-mail containing a link to a page the user must access. 我有一个向用户发送电子邮件的应用程序,每个电子邮件都包含用户必须访问的页面的链接。

The link is an md5 of an unique id + a random number. 链接是唯一ID +随机数的md5。

The link looks like: www.domain.com/index.php?id_page=<the md5> 链接看起来像: www.domain.com/index.php?id_page=<the md5>

On the index page i save the $_GET["id_page"] within a session variable, $_SESSION["id_page"] and I redirect the user to the page which he must see (the user is redirected to that page only if the link contains id_page ). 在索引页面上,我将$_GET["id_page"]在会话变量$_SESSION["id_page"] ,并将用户重定向到他必须看到的页面(仅当链接时,用户才被重定向到该页面)包含id_page )。

How can I improve this method and make it more secure? 如何改进此方法并使其更安全? How can I guarantee that the users enter the page designated to them - and cannot enter any other page? 如何保证用户输入为其指定的页面,并且不能输入其他页面?

You can add their email in the URL. 您可以在URL中添加他们的电子邮件。 the probability of someone guessing someone else's email and the associated hash is just about 0. 某人猜测别人的电子邮件和相关哈希的概率仅为0。

What you are concerned with here is a matter of time, rather than a matter of security :). 您在这里关心的只是时间问题,而不是安全性问题:)。 If you allow anyone to guess an infinite number of id_page values, then given enough time, eventually someone will happen upon a random valid id_page value. 如果您允许任何人猜测无限数量的id_page值,那么在给定足够的时间后,最终将有人偶然遇到一个有效的id_page值。

Your only real defense against this is to increase the length of your hash, causing it to take (on average) more time to happen upon a random valid id_page (on the order of months or years). 对此的唯一真正的防御措施是增加哈希的长度,使它在随机有效的id_page上(平均数月或数年)花费(平均)更多的时间。 This could be accomplished by using sha256 or sha512 rather than md5. 这可以通过使用sha256或sha512而不是md5来完成。

Another approach is to lock someone out for a period of time if they have, for example, 3 consecutive incorrect guesses at an id_page value. 另一种方法是,如果某人在id_page值上有3次连续不正确的猜测,则将他们锁定一段时间。 This will greatly decrease the number of values they can attempt in a given period of time. 这将大大减少它们在给定时间段内可以尝试的值的数量。

Lastly, If the user is already logged in at the time of redirect then you could also store the hashes you generate in a database table. 最后,如果用户在重定向时已经登录,那么您也可以将生成的哈希存储在数据库表中。 That way you can map a particular hash to one and only one userid in the table. 这样,您可以将特定的哈希映射到表中的一个且只有一个userid。 If a user attempts to visit a hash page to which they don't correspond in the DB, then you could redirect them elsewhere. 如果用户尝试访问数据库中不对应的哈希页,则可以将其重定向到其他位置。

One method that can work quite well for preventing guessing ID numbers is to add some sort of padding to the ID and then convert it to base32. 一种可以很好地防止猜测ID号的方法是在ID中添加某种填充,然后将其转换为base32。 Of course, this doesn't eliminate the ability to guess an ID entirely, but it does make it a little more time consuming for anyone who is snooping around. 当然,这并不能消除完全猜测ID的能力,但是对于任何偷窥的人来说,确实要花费更多的时间。

If you have the URL www.domain.com/index.php?id_page=1, you could convert the id to something unique in your application, for example: 如果您具有URL www.domain.com/index.php?id_page=1,则可以将ID转换为应用程序中唯一的内容,例如:

padded id = id x 9 - 2 填充的id = id x 9-2

7 = 1 x 9 - 2
16 = 2 x 9 -2
25 = 3 x 9 - 2

Then, you can convert the new padded ID to base 32, which would be 然后,您可以将新的填充ID转换为以32为基

7 = G4
16 = GE3A
25 = GI2Q

The new url would then be (for an id of 1): 然后,新的URL将是(ID为1):

www.domain.com/index.php?id_page=G4 www.domain.com/index.php?id_page=G4

Using this method, if someone guesses the base 32 of 1-6, it would return a 404, because your ID of 1 is actually being padded out to become 7. Guessing 8-15 wouldn't return a parsed ID because the next id of 2 is padded to 16, and so on. 使用此方法,如果有人猜到1-6的基数32,它将返回404,因为您的ID 1实际上正在被填充成7。猜测8-15不会返回已解析的ID,因为下一个ID 2填充到16,依此类推。

Not only does this keep the query string smaller in size, but it also doesn't use an obvious MD5 hash which can very easily be sequentially browsed using dictionaries. 这不仅使查询字符串的大小变小,而且没有使用明显的MD5哈希,该哈希很容易使用字典顺序浏览。

If you want the page to be linked to a specific users, well there's no reason why you cannot append more values to the new padded_id (let's call it a hash). 如果您希望页面链接到特定用户,那么没有理由不能将更多值附加到新的padded_id(我们称其为哈希)。

For instance, if you have a user with an ID of 12, and you only want that user to be able to visit a page with an id of 10, you would need to create a hash that comprises of both these values: 例如,如果您有一个ID为12的用户,并且只希望该用户能够访问ID为10的页面,则需要创建一个包含以下两个值的哈希值:

page_id(10)-user_id(12), using the above example, this would produce: page_id(10)-user_id(12),使用上面的示例,将产生:

(10 x 9 - 2) (12 x 9 - 2)
88-106
HA4A-GEYDM

You now have a nice small hashed link that can be secured to a single user ID. 现在,您有了一个不错的小型散列链接,该链接可以固定到单个用户ID。 The padding method in the example above is rather simple, but it gives you the overall idea of how to approach the issue. 上面的示例中的padding方法非常简单,但是它为您提供了解决问题的整体思路。

Don't bother hashing or creating unique ids or anything. 不要理会散列或创建唯一的ID或其他任何东西。 Complexity is not going to help you. 复杂性不会帮助您。

Instead, simply generate a random token, and use that. 相反,只需生成一个随机令牌,然后使用它即可。 A 256 bit random token should be sufficient for anything you need to do. 256位随机令牌应该足以完成您需要做的任何事情。

So, using mcrypt (a core extension): 因此,使用mcrypt (核心扩展):

$token = strtr(
    base64_encode(mcrypt_create_iv(256/8, MCRYPT_DEV_URANDOM)), 
    '+/', 
    '-_'
);

That will give you a 44 character result of the alphabet a-zA-Z0-9-_ which contains 256 bits of random entropy. 这将为您提供字母a-zA-Z0-9-_的44个字符的结果,其中包含256位随机熵。

There's no need to hash the result or anything. 无需哈希结果或其他任何东西。 The random data is enough. 随机数据就足够了。

To understand why, you need to understand The Birthday Problem . 要了解原因,您需要了解生日问题

Basically with a 256 bit random value, to have a 1% chance that 2 tokens collide you would need to generate 4.8e37 tokens (that's 48 followed by 36 0's). 基本上具有256位随机值,要使2个令牌发生冲突的可能性为1%,您需要生成4.8e37个令牌(48个后跟36个0)。

To get a 10e-18 chance of collision (which is typically seen as secure) you'd need to generate 4.8e29 tokens. 为了获得10e-18的碰撞机会(通常被视为安全的),您需要生成4.8e29令牌。

Since those numbers are WAY more than you'd ever generate, the chance of 2 tokens colliding is infinitesimally small. 由于这些数字比您生成的数字多得多,因此两个令牌碰撞的机会非常小。

The other problem is people guessing the token. 另一个问题是人们猜测令牌。 Well, MCRYPT_DEV_URANDOM uses the underlying operating system's random pool. 好吧, MCRYPT_DEV_URANDOM使用底层操作系统的随机池。 Which means that people are way more likely to guess your "unique id and random number" than they are to guess the token generated here. 这意味着人们比猜测这里生成的令牌更有可能猜测您的“唯一ID和随机数”。

So, in short, just use a random token and be done :-) 因此,简而言之,只需使用随机令牌并完成:-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM