用于用户身份验证和密码安全的 PHP 最佳实践
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1624846/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP best practices for user authentication and password security
提问by Daren Schwenke
What are the best current libraries/methods to authenticate users without the use of a CMS or heavy framework?
在不使用 CMS 或繁重框架的情况下,当前最好的库/方法是什么来验证用户身份?
Responses should include suggestions for anything you think should be considered the standard for new PHP development involving user authentication.
响应应包括您认为应被视为涉及用户身份验证的新 PHP 开发标准的任何建议。
采纳答案by Daren Schwenke
OpenIDis a method to authenticate users based on their existing accounts on common web services such as Yahoo, Google and Flickr.
OpenID是一种基于用户在常见网络服务(例如 Yahoo、Google 和 Flickr)上的现有帐户进行身份验证的方法。
Logins to your site are based on a successful login to the remote site.
登录到您的站点基于成功登录到远程站点。
You do not need to store sensitive user information or use SSL to secure user logins.
您不需要存储敏感的用户信息或使用 SSL 来保护用户登录。
A current PHP version of the library can be found here.
可以在此处找到该库的当前 PHP 版本。
回答by Scott Arciszewski
Implementing user authentication securelywithout relying on a framework (or third-party library, such as OpenID) to do it for you is not a trivial undertaking.
在不依赖框架(或第三方库,例如 OpenID)的情况下安全地实现用户身份验证并非易事。
At a 10,000 foot overview, you have to decide:
在 10,000 英尺的概览中,您必须决定:
- Do you have usernames, email addresses, or user IDs as a primary selector?
- How should you store passwords?PROTIP:
password_hash()or scryptare the way to go. - How should you handle "remember me" checkboxes?There are a lot of bad strategies for this on the Internet. Treat every one of them with skepticism, because they might introduce vulnerabilities into your application.
- How should the application handle users who forget their password?
- 您是否将用户名、电子邮件地址或用户 ID 作为主要选择器?
- 您应该如何存储密码?PROTIP:
password_hash()或scrypt是要走的路。 - 你应该如何处理“记住我”复选框?互联网上有很多不好的策略。用怀疑的态度对待它们中的每一个,因为它们可能会给您的应用程序带来漏洞。
- 应用程序应该如何处理忘记密码的用户?
The information in this answer is relevant and up-to-date as of May 9, 2015 and might be obsoleted by the conclusion of the password hashing competition
此答案中的信息是截至 2015 年 5 月 9 日的相关信息和最新信息,并且可能会因密码哈希竞赛的结束而过时
Primary Selectors
主要选择器
In general, usernames and email addresses are better than ID numbers.
一般来说,用户名和电子邮件地址比 ID 号要好。
There should be no security requirement to keep usernames secret, because in practice they will be leaked when someone tries to register anyway.
不应该有保密用户名的安全要求,因为实际上当有人尝试注册时,它们会被泄露。
You can decide whether or not to treat email addresses as a secret. Users generally like not being exposed to spammers, scammers, and trolls.
您可以决定是否将电子邮件地址视为机密。用户通常不喜欢被垃圾邮件发送者、诈骗者和巨魔攻击。
Password Hashing
密码散列
You should use password_hash()and password_verify()unless you are sufficiently experienced with writing cryptography libraries to go above and beyond.
您应该使用password_hash()andpassword_verify()除非您在编写加密库方面有足够的经验来超越它。
Beyond Bcrypt
超越 Bcrypt
Sometimes developers like to get creative (e.g. adding a "pepper", which usually means pre-hashing or HMACing passwords with a static key) and go beyond the standard implementations. We ourselves have done this, but very conservatively.
有时,开发人员喜欢发挥创意(例如添加“胡椒”,这通常意味着使用静态密钥对密码进行预散列或 HMAC 处理)并超越标准实现。我们自己已经这样做了,但非常保守。
For our internal projects (which have a much higher margin of security than most people's blogs), we wrote a wrapper around this API called PasswordLockthat first hashes a password with sha256, then base64 encodes the raw hash output, then passes this base64-encoded hash to password_hash(), and finally encrypts the bcrypt hash with a properly-implemented encryption library.
对于我们的内部项目(它们的安全性比大多数人的博客高得多),我们围绕这个 API 编写了一个包装器,称为PasswordLock首先用 散列密码sha256,然后 base64 编码原始散列输出,然后将此 base64 编码散列传递给password_hash(),最后使用正确实现的加密库加密 bcrypt 哈希。
To reiterate, instead of peppering, we encrypt our password hashes.This gives us more agility in case of a leak (we can decrypt then re-encrypt because we know the key). Additionally, we can run our webserver and database on separate hardware in the same datacenter to mitigate the impact of a SQL injection vulnerability. (In order to start cracking hashes, you need the AES key. You can't get it from the database, even if you escape to the filesystem.)
重申一下,我们对密码哈希进行加密,而不是添加。这使我们在发生泄漏时更加敏捷(我们可以解密然后重新加密,因为我们知道密钥)。此外,我们可以在同一数据中心的不同硬件上运行我们的网络服务器和数据库,以减轻 SQL 注入漏洞的影响。(为了开始破解散列,您需要 AES 密钥。即使您转义到文件系统,也无法从数据库中获取它。)
// Storage:
$stored = \ParagonIE\PasswordLock\PasswordLock::hashAndEncrypt($password, $aesKey);
// Verification:
if (\ParagonIE\PasswordLock\PasswordLock::decryptAndVerify($password, $stored, $aesKey)) {
// Authenticated!
}
Password Storage with PasswordLock:
密码存储PasswordLock:
hash('sha256', $password, true);base64_encode($step1);password_hash($step2, PASSWORD_DEFAULT);Crypto::encrypt($step3, $secretKey);
hash('sha256', $password, true);base64_encode($step1);password_hash($step2, PASSWORD_DEFAULT);Crypto::encrypt($step3, $secretKey);
Password Verification with PasswordLock:
密码验证PasswordLock:
Crypto::decrypt($ciphertext, $secretKey);hash('sha256', $password, true);base64_encode($step2);password_verify($step3, $step1);
Crypto::decrypt($ciphertext, $secretKey);hash('sha256', $password, true);base64_encode($step2);password_verify($step3, $step1);
Further Reading
进一步阅读
- Security Issue: Combining Bcrypt With Other Hash Functions
- password_hash
- password_verify
- password_compat(for PHP < 5.5.0)
- 安全问题:将 Bcrypt 与其他哈希函数结合使用
- 密码哈希
- 密码验证
- password_compat(适用于 PHP < 5.5.0)
回答by Martin York
I use OpenID.
我使用OpenID。
But like stackoverflow I use the Google project openid-selectorto do the heavy lifting.
Demo Page here.
但就像 stackoverflow 一样,我使用 Google 项目openid-selector来完成繁重的工作。
演示页面在这里。
The obvious advantages (of OpenID) are.
明显的优势(OpenID)是。
- You don't need to be a security expert.
- Users trust the big sites with their info.
- You can request things like (nickname etc) but user has to opt in.
- You don't need to worry about:
- registration processes
- lost/forgotten password
- 您无需成为安全专家。
- 用户相信大网站的信息。
- 您可以请求诸如(昵称等)之类的内容,但用户必须选择加入。
- 您无需担心:
- 注册流程
- 丢失/忘记密码
回答by DougW
A lot of great answers here, but I feel like it's worth saying this--do NOT try to re-invent the wheelin this case! It is extremely easy to screw up user authentication in a wide variety of ways. Unless you reallyneed a custom solution, and have a firmknowledge of security schemes and best practices, you will almost certainly have security flaws.
这里有很多很棒的答案,但我觉得值得这么说——在这种情况下不要试图重新发明轮子!以多种方式破坏用户身份验证非常容易。除非你真的需要一个定制的解决方案,并有坚定的保障方案和最佳实践的知识,你几乎肯定会存在安全漏洞。
OpenID is great, or if you're going to roll your own, at least use an established library and follow the documentation!
OpenID 很棒,或者如果您要推出自己的,至少使用一个已建立的库并遵循文档!
回答by Daren Schwenke
PHPassis a lightweight, variable cost password hashing library using bcrypt.
PHPass是一个使用 bcrypt 的轻量级、可变成本的密码哈希库。
Variable cost means that you can later turn up the 'cost' of hashing passwords to seamlessly increase security without having to invalidate your previously hashed user passwords.
可变成本意味着您以后可以提高散列密码的“成本”,以无缝地提高安全性,而不必使您以前散列的用户密码无效。
The field size used for hash storage is constant even when increasing 'cost' due to increasing not the size of the hash, but the number of iterations required to produce it.
用于散列存储的字段大小是恒定的,即使由于增加的不是散列的大小而是增加生成散列所需的迭代次数而增加“成本”。
回答by Patrick Allaert
Login using HTTP AUTH
使用 HTTP AUTH 登录
- Using Apache: http://httpd.apache.org/docs/2.2/howto/auth.html
- It is also possible with IISand other web servers.
- 使用 Apache:http: //httpd.apache.org/docs/2.2/howto/auth.html
- 也可以使用IIS和其他 Web 服务器。
Once authenticated, for PHP, you just have to use $_SERVER['PHP_AUTH_USER']to retrieve the username used during authentication.
一旦通过身份验证,对于PHP,您只需使用$_SERVER['PHP_AUTH_USER']检索身份验证期间使用的用户名。
This can be a faster and, sometimes, more flexible solution than handling the solution at scripting level provided that limited information is needed regarding the user as all that is made available to you is the username used to login.
与在脚本级别处理解决方案相比,这可能是一种更快且有时更灵活的解决方案,前提是需要有关用户的有限信息,因为您可以使用的只是用于登录的用户名。
However, forget about integrating your authentication in an HTML form unless you implement a full HTTP AUTH scheme from within your scripting language (as described below).
但是,除非您在脚本语言中实现完整的 HTTP AUTH 方案(如下所述),否则不要将您的身份验证集成到 HTML 表单中。
Rolling your own HTTP AUTH within your scripting language
在您的脚本语言中滚动您自己的 HTTP AUTH
You can actually extendHTTP Basic Authby emulating it inyour scripting language. The only requirement for this is that your scripting language must be able to send HTTP headers to the HTTP client. A detailed explaination on how to accomplish this using PHP can be found here: (see more on PHP and HTTP AUTH).
您实际上可以通过在脚本语言中模拟它来扩展HTTP 基本身份验证。唯一的要求是您的脚本语言必须能够将 HTTP 标头发送到 HTTP 客户端。可以在此处找到有关如何使用 PHP 完成此操作的详细说明:(请参阅有关 PHP 和 HTTP AUTH 的更多信息)。
You can expand on the article above using a typical authentication schema, file store, even PHP sessions or cookies (if information isn't needed to be persistent), giving you much more flexibility when using HTTP AUTH, yet still maintaining some simplicity.
您可以使用典型的身份验证模式、文件存储,甚至 PHP 会话或 cookie(如果不需要持久化信息)来扩展上面的文章,在使用 HTTP AUTH 时为您提供更大的灵活性,同时仍然保持一些简单性。
Downsides to HTTP AUTH
HTTP AUTH 的缺点
The main downside to HTTP auth is the complications that logging out can have. The main way to clear the user's session is to close the browser, or pass off a header with 403 Authentication Required. Unfortunately, this means the HTTP AUTH popup comes back on the page and then requires users to either log back in or hit cancel. This may not work well when taking usability into consideration, but can be worked around with some interesting results (ie. using a combination of cookies and HTTP AUTH to store state).
HTTP AUTH popups, session, and HTTP header handling is determined by browser implementation of the standard. This means that you will be stuck with that implementation (including any bugs) without the possibility of workaround (unlike other alternatives).
Basic auth also means auth_user and password show up in server logs, and then you have to use https for everything because otherwise username and password also go over the network on every query in plain text.
HTTP 身份验证的主要缺点是注销可能带来的复杂性。清除用户会话的主要方法是关闭浏览器,或传递带有 403 Authentication Required 的标头。不幸的是,这意味着 HTTP AUTH 弹出窗口会重新出现在页面上,然后要求用户重新登录或点击取消。这在考虑可用性时可能效果不佳,但可以解决一些有趣的结果(即使用 cookie 和 HTTP AUTH 的组合来存储状态)。
HTTP AUTH 弹出窗口、会话和 HTTP 标头处理由标准的浏览器实现决定。这意味着您将被该实现(包括任何错误)困住,而没有解决方法的可能性(与其他替代方案不同)。
基本身份验证还意味着 auth_user 和密码显示在服务器日志中,然后您必须对所有内容使用 https,否则用户名和密码也会在每次查询时以纯文本形式通过网络。
回答by Evan Kroske
It's important to separate the security layer of your application from the rest of it. If there's no distance between your application logic and your communication system, you are free to communicate insecurely in one place and securely somewhere else. Maybe you'll make a mistake and send a password in an unencrypted cookie, or maybe you'll forget to verify the user's credentials for one step. Without a 'right way' to communicate with the user, you're sure to make a mistake.
将应用程序的安全层与其其余部分分开很重要。如果您的应用程序逻辑和您的通信系统之间没有距离,您可以自由地在一个地方进行不安全的通信,而在其他地方进行安全的通信。也许您会犯错误并在未加密的 cookie 中发送密码,或者您可能会忘记验证用户的凭据一步。如果没有与用户沟通的“正确方式”,您肯定会犯错误。
For example, let's say this is how you verify users now:
例如,假设您现在验证用户的方式如下:
user_cookie = getSecureCookie()
if (user_cookie.password == session_user.password) {
do_secure_thing()
...
}
If a vulnerability is discovered in getSecureCookie(), and you use this code to verify users throughout your application, you might not find all the instances of getSecureCookie() that need to be fixed. If, however, you separate your logic from your security:
如果在 getSecureCookie() 中发现了漏洞,并且您使用此代码在整个应用程序中验证用户,您可能找不到所有需要修复的 getSecureCookie() 实例。但是,如果您将逻辑与安全分开:
if (userVerified()) {
do_secure_thing()
...
}
... you will be able to quickly and easily re-secure your application. Give yourself a 'right way' to do security, and you will be far less likely to make a major security blunder.
...您将能够快速轻松地重新保护您的应用程序。给自己一个“正确的方法”来做安全,你就不太可能犯下重大的安全错误。
回答by Igor Sill
Best authentication is to utilize multi-factor authentication, ideally a token-less version on all security sensitive log-ons.
最好的身份验证是使用多因素身份验证,最好是所有安全敏感登录的无令牌版本。
Password protected and easier to use with high reliability and security. There are several available beyond EMC/RSA. I prefer SwivelSecure's PINSafe.
密码保护且易于使用,具有高可靠性和安全性。除了 EMC/RSA,还有几个可用的。我更喜欢 SwivelSecure 的 PINSafe。
Igor S
伊戈尔

