PHP登录脚本应采用哪些最佳实践?


27

我想为客户网站重新编写我的登录脚本,以使其更加安全。我想知道我可以在其中实施哪些最佳实践。受密码保护的控制面板数量众多,但是似乎很少有人在代码编写,速度和安全性方面暗示最佳实践。

我将使用PHP和MYSQL数据库。

我曾经使用md5,但我发现sha256或sha512会更好(以及安全的哈希和盐)。

一些登录脚本会在整个会话甚至用户代理中记录IP地址,但是我想避免这种情况,因为它与代理服务器不兼容。

我在使用PHP 5中的会话的最佳实践(最后一次阅读的是PHP 4)方面也落后了一些,因此一些最佳实践可能会有所帮助。

谢谢。


1
在codereview.SE上扔你的脚本!
克里斯(Chris)

@Chris CodeReview帮助提高代码的清晰度等。绝对不应该为了安全而去他们那里。如果有的话,他们几乎总是会提醒您不要“自己动手”,这与他进入SE上任何编程社区的答案基本相同。
Alternatex

Answers:


21

最好的想法是不要重新发明轮子。但是,据我所知,在PHP世界中,可能很难找到已经做到这一点的高质量组件(甚至我很确定框架已经实现了这些功能,并且它们的实现已经过测试,可靠,经过代码审查等)。 )

如果由于某些原因您不能使用框架,请参考以下建议:

安全相关建议

  • 如果可以,请使用PBKDF2或Bcrypt。为此。

    原理:两种算法都可以使哈希过程任意变慢,这正是哈希密码时想要的(更快的替代方法意味着更容易的暴力破解)。理想情况下,您应该调整参数,以使在相同硬件上该过程随着时间的推移变得越来越慢,同时又发布了新的,更快的硬件。

  • 如果不能,至少不要使用MD5 / SHA1。决不。算了吧。例如,改用SHA512。也要用盐。

    理由:MD5和SHA1太快。如果攻击者可以访问包含哈希的数据库,并且拥有一台(甚至不是特别强大的)功能强大的计算机,那么暴力破解密码既快速又容易。如果没有盐,攻击者发现实际密码的机会就会增加(如果密码在其他地方被重用,可能会造成更大的危害)。

  • 在PHP 5.5.0及更高版本中,使用password_hashpassword_verify

    基本原理:调用框架提供的函数很容易,因此降低了犯错的风险。使用这两个函数,您不必考虑不同的参数,例如哈希。所述第一函数返回一个单一然后可将其存储在数据库中的字符串。第二个函数使用此字符串进行密码验证。

  • 保护自己免受暴力侵害。如果用户在0.01秒钟前已经提交了另一个错误的密码时提交了错误的密码,那么这是阻止它的一个很好的理由。尽管人类可以快速打字,但他们可能不会那么快。

    另一个保护措施是设置每小时的故障限制。如果用户在一小时内提交了3600个错误密码(每秒1个密码),那么很难相信这是合法用户。

    原理:如果您以不安全的方式对密码进行哈希处理,则蛮力可能非常有效。如果密码被安全地存储,蛮力仍在浪费服务器的资源和网络带宽,从而导致合法用户的性能降低。蛮力检测不容易开发和解决,但是对于任何微小的系统来说,这都是值得的。

  • 不要要求您的用户每四周更改一次密码。这非常令人讨厌,并降低了安全性,因为它鼓励基于便利贴的安全性。

    理由:每隔n周强制更改密码可以保护系统免受暴力攻击的想法是错误的。蛮力攻击通常会在几秒钟,几分钟,几小时或几天之内成功完成,这使每月更改密码变得无关紧要。另一方面,用户很难记住密码。此外,如果他们需要更改密码,他们将尝试使用非常简单的密码,或者只是在便利贴上注明密码。

  • 每次都审核所有内容。存储登录,但切勿将密码存储在审核日志中。确保审核日志无法修改(即,您可以在末尾添加数据,但不能修改现有数据)。确保审核日志需要定期备份。理想情况下,日志应存储在具有严格限制访问权限的专用服务器上:如果另一台服务器被黑客入侵,攻击者将无法清除日志以隐藏其存在(以及攻击过程中采用的路径)。

  • 不要记住Cookie中的用户凭据,除非用户要求这样做(默认情况下,必须取消选中“记住我”复选框,以避免人为错误)。

易用性建议

  • 即使大多数浏览器已经具有此功能,也可以让用户记住密码
  • 有时要求用户仅输入密码而不是用户名和密码时,不要使用Google方法,该用户名已显示在中<span/>。在这种情况下,浏览器无法填写密码字段(至少Firefox无法这样做),因此它会强制注销,然后以浏览器填写的普通格式登录。
  • 不要使用启用JavaScript的弹出窗口进行登录。它破坏了浏览器的密码记忆功能(并且在所有情况下都很糟糕)。
  • 让用户输入她的用户名或邮件地址。当我注册时,有时我要输入的用户名已经被占用,因此我必须发明一个新的用户名。我有机会在两个小时内忘记这个名字。
  • 始终在登录表单附近保留指向“忘记密码”功能的链接。仅当用户登录失败时才显示该密码:根本不记得自己密码的用户不知道她必须提交错误的密码才能看到“忘记密码”链接。
  • 不要通过便利贴来使用安全性
  • 不要发明与密码有关的愚蠢规则以使其更加脆弱。示例:“您的密码必须以小写字母开头。” “您的密码不能包含空格。”

没碰到bcrypt。您建议这样做的理由是什么?为什么不md5 / sha1?感谢您提出的其他建议!
baritoneuk

道理很简单:bcrypt由高素质的人来完成。还对其进行了测试,审查等。因此,没有理由自己实现相同的事情。这就像为您的网站创建自己的加密算法。*“为什么不使用md5 / sha1?”:例如,请参见en.wikipedia.org/wiki/MD5#Security
Arseni Mourzenko 2011年

5
bcrypt速度很慢,并且正如专业人员所提到的那样实施。md5是一种哈希算法,而不是一种加密算法,两者不太一样。快速哈希文件很好,这里可以使用md5 ...哈希密码不必那么快,bcrypt可以在这里提供帮助。我之所以这样说,是因为当crypt算法“缓慢”时,蛮力变得更加困难。
克里斯(Chris)

2
@baritoneuk:至少很酷。但是我无法计算出我曾经去过多少个站点,这些站点都受到限制,例如最大长度,没有非字母数字字符等。令人困惑的是,如果您仍然要对密码进行哈希处理-这使我担心它们不是,他们只是将其明确存储在数据库中。
Carson63000

1
@Brian Ortiz:并非总是如此。设置限制有时是个好主意。如果您不这样做,您是否会测试您的应用程序是否可以正常工作?怎么样?如果不进行测试,如何确定它是否有效?相反,当将限制设置为合理的值(例如100)时,您可以轻松测试100个字符的密码。
阿森尼·莫尔琴科(Arseni Mourzenko)2011年

6
  1. 您的网站应使用HTTPS。您绝对不应该显示登录页面或接受来自非加密连接的登录。
  2. 如果您使用HTTPS,则应将Cookie限制为仅HTTP,限制为安全连接。
  3. 登录过程应不少于2秒(如果您认为2秒太长,则为1秒)。在存储和验证密码时使用安全的哈希,并使用难以猜测的盐。bcrypt尽可能使用。否则,请使用其他类型的迭代哈希。
  4. 决不曾经撰写数据库查询中需要使用类似功能的任何方式mysql_real_escape_string。切勿使用字符串连接来构造查询。使用准备好的参数化查询。大多数(如果不是全部)PHP的数据库驱动程序都支持它。如果你不知道该怎么做,花一些时间学习如何preparebind以及execute使用任何DB你使用。这是防止SQL注入的唯一肯定方法。PDO支持这一点。
  5. 鼓励您的用户不要使用与电子邮件相同的密码。提醒他们,如果您的网站遭到入侵,并且在两个地方使用相同的密码,则有人可以劫持其电子邮件。

HTTPS +1,因为通过公共WiFi网络的流量越来越多。但是我不同意点3:从用户的角度来看,2秒太长了。0.5秒 很好,特别是如果使用其他反蛮力技术。
阿森尼·穆尔琴科(Arseni Mourzenko)2011年

我对第4点感到困惑。如何使用mysql_real_escape_string逃逸转义数据的约定?所有用户提交的数据都不应被信任并相应地进行过滤。
2011年

@chrisw:是的,应该将所有用户输入视为恶意内容。但是,当使用参数化查询时,这是阻止SQL注入的最佳方法。如果不使用准备好的和参数化的查询,则很容易受到SQL注入的攻击。mysql_real_escape_string是虚假的安全性-这不是保证。参数化查询
greyfade

在进一步阅读之后,我现在同意。函数:mysql_real_escape_string通常在大多数情况下是安全的,我不认为这是一个巨大的负担,但是我可以看到使用准备好的语句如何更加有效。
克里斯

1
您可以使用PDO。
Felix G

1

使用加盐的单向哈希(最好使用http://au2.php.net/manual/zh/function.hash.php的 SHA512 )...这样,即使有人确实入侵了数据库,即使他们知道加盐也是如此,则他们无法提取密码(没有彩虹表,对于SHA512来说是唯一的选择)。

使用HTTPS。

用户注册时,请勿通过电子邮件将用户名/密码确认发送回给用户。

如果您允许Cookie用作“记住我”,请添加一个额外的登录名以访问管理和配置文件编辑功能。

如果用户输入的密码有误,请不要说他们输入的密码有误(例如,他们始终输入的“用户名或密码错误”),那样可以减少关于有效用户名的线索。

尝试实现屏幕名称与登录名-这样,较少的用户将在用户列表中显示其登录名。


完全同意不通过电子邮件发送纯文本电子邮件的观点。我忘了网站这样做的次数。如果您为所有站点都使用1个密码(幸运的是,我没有),那么所有站点都有可能遭到破坏。这样的网站也可能以纯文本形式存储密码。叹息
baritoneuk

1
  • 切勿使用MD5或SHA1哈希算法。始终坚持使用SHA512等较新的产品
  • 始终使用强烈随机产生的盐
  • 永远不要通过电子邮件向用户发送密码,即使出现“忘记密码”的情况
  • 切勿使用mysql_ *函数。他们已长期贬值。坚持PDO
  • 切勿在会话或Cookie中存储密码

0

这里有一些建议:确保无法使用JS访问cookie以防止盗窃,请参阅Jeff Atwood的保护Cookies:HttpOnly文章。

...通过巧妙的构造,格式错误的URL设法设法掩盖了消毒剂。在浏览器中查看时,最终呈现的代码将从该远程服务器加载并执行脚本。

...加载此注入脚本的用户个人资料页面的任何人都无意间将其浏览器cookie传输到了邪恶的远程服务器!

正如我们已经建立的那样,一旦有人获得了给定网站的浏览器Cookie,他们实际上就会在其中拥有用于您的身份的王国的钥匙...

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.