我正在编写自己的会话控制器,在登录后向用户发出唯一ID,然后在每次加载页面时验证并验证该唯一ID。生成这样一个id最安全的方法是什么?唯一ID应该是完全随机的吗?将用户ID作为唯一ID的一部分包含在内是否有任何缺点?
答案 0 :(得分:7)
去买Bruce Schneier的Secrets and Lies和他的Practical Cryptography。来吧,当你在它的时候订购Ross Anderson的Security Engineering 2nd Ed.。现在,阅读秘密和谎言 - 相信我,这是一个有趣的阅读。 :)然后阅读Practical Cryptography。此时,在实现需要一定安全性的软件时,您应该更好地了解需要做的事情。继续执行粗略草案实施。既然您的安全工程副本已经到了,请阅读它...虽然那个你可能想要消化得慢一点;这是一本相当沉重的书。
还有一个whitepaper on web authentication dos and don'ts值得一读,而且更直接适用于你正在做的事情。我仍然推荐上面的书。
而another timely article from LWN.net列出了您需要努力避免的大量陷阱。 (BTW,LWN.net非常值得订阅,我强烈推荐它。上述链接允许免费访问该文章,否则非订阅者将无法使用该文章。)
答案 1 :(得分:4)
使用2.5及更高版本的UUID模块。这样他们就不会被猜到,因为它们不是连续的。如果你希望它们非常安全,你可以用用户的id加盐,然后SHA1哈希它们,Base64对结果进行编码,然后将其用作值。通过这种方式,您可以在服务器端验证它们确实是您的cookie,而不仅仅是某些垃圾邮件发送者正在生成并投入您服务器的内容。为了使它们更加安全,请确保它们仅在服务器和客户端上持续一段时间。 Python 2.6.5内置了HMAC实现,您可以在生成的UUID上使用它。
答案 2 :(得分:4)
如果cookie中的唯一信息是标识符,实际上是标签,那么您试图保护的唯一攻击是攻击者猜测它是什么。所以使用一些随机字节。足够的随机字节,它是“不可思议的。”
然后将随机字节挤压成适合您可以在HTTP cookie中使用的字符范围的内容。 base64适用于此。它并不是绝对最优的,因为有超过64个cookie安全字符,它往往会留下尾随==
个字符,这些字符会在标题中添加字节而不是随机性,但这很好。它可以工作,Python可以比我们想出的任何其他东西更快地执行base64编码。
如果你还想为它添加用户ID,这将使跟踪和调试更容易,这可能会让你的生活更轻松,所以继续。它不会给攻击者任何重大优势。 (除非他们设法窃取一个而不能确定他们偷窃的用户,这似乎不太可能。)
答案 3 :(得分:4)
您可以使用os.urandom:
os.urandom(n)的
返回一串适合加密使用的n个随机字节。
此函数返回来自OS特定随机源的随机字节。 对于加密应用程序,返回的数据应该是不可预测的,尽管它的确切质量取决于操作系统 实现。在类UNIX系统上,这将查询/ dev / urandom, 在Windows上,它将使用CryptGenRandom。如果是随机源 未找到,将引发NotImplementedError。
版本2.4中的新功能。
答案 4 :(得分:2)
如果您使用的是Python 3.6+,则可以使用新库secrets。
from secrets import token_urlsafe
token = token_urlsafe(64) # something like Drmhze6EPcv0fN_81Bj-nA....
答案 5 :(得分:0)
http://docs.python.org/2/library/random.html
“警告:此[随机]模块的伪随机生成器不应用于安全目的。如果需要加密安全的伪随机数生成器,请使用os.urandom()或SystemRandom。”
“os.urandom函数从特定于操作系统的随机源返回随机字节。返回的数据对于加密应用程序应该是不可预测的,尽管它的确切质量取决于操作系统实现。在类UNIX系统上,这将查询/ dev / urandom,在Windows上它将使用CryptGenRandom()。如果找不到随机源,将引发NotImplementedError。
有关平台提供的随机数生成器的易用界面,请参阅random.SystemRandom。“
答案 6 :(得分:0)
我会完全避免随机因素。我通常做的是:
session_id
(我在本地存储)。session_id
进行签名(我为此使用itsdangerous
)。session_id
的签名完好无损,我会继续,否则,403(400也会有意义)。请注意,我在本地存储了session_id
,因为我希望能够提前过期终止会话。
如果您的会话总是持续一段固定的时间,并且您不需要提前结束,则只需签署user_id + datetime.now()
即可。这允许完全无状态的会话管理。
答案 7 :(得分:-2)
最好的方法是generate a UUID,使用uuid.uuid1
函数:
从主机ID,序列号和当前时间生成UUID。