简体   繁体   English

通过PHP中的适当身份验证进行安全登

[英]Secure login with proper authentication in PHP

How do I write/put together a secure login in PHP? 如何在PHP中编写/组合安全登录? The website developer guide said I shouldn't roll my own, so referring to samples available via Google is useless. 网站开发者指南说我不应该自己动手,所以参考谷歌提供的样本是没用的。

How do you pros do it? 你是如何做到这一点的呢? Lets say you're building a world-class app in rails, would the same libraries / techniques be usable here? 假设您正在使用rails构建世界级应用程序,是否可以在此处使用相同的库/技术?

Thanks 谢谢

In Rails, one would generally use a pre-existing library. 在Rails中,通常会使用预先存在的库。 Authentication is easy to do wrong, and the problem's been solved so many times that it's rarely worth the effort to solve it again. 身份验证很容易出错,问题已经解决了很多次,很难再次解决它。 If you are interested in writing your own implementation, then I'll describe how modern authentication works. 如果您有兴趣编写自己的实现,那么我将描述现代身份验证的工作原理。

The naive method of authenticating a user is to store their password in a database and compare it to the password the user submits. 验证用户的简单方法是将其密码存储在数据库中,并将其与用户提交的密码进行比较。 This is simple but unbelievably insecure. 这很简单但令人难以置信的不安全。 Anyone who can read your database can view anyone's password. 任何能够读取您数据库的人都可以查看任何人的密码。 Even if you put in database access controls, you (and your users) are vulnerable to anyone who hacks around them. 即使你放入了数据库访问控制,你(和你的用户)也容易受到攻击他们的人的攻击。

Proper form is to use a cryptographic hash function to process the password when it is chosen and then every time it is submitted. 正确的形式是使用加密哈希函数在选择密码时处理密码,然后在每次提交时使用密码。 A good hash function is practically irreversible -- you can't take a hash and turn it back into a password. 一个好的哈希函数实际上是不可逆的 - 你不能把哈希值转回密码。 So when the user logs in, you take the submitted password, hash it, and compare it to the hash in the database. 因此,当用户登录时,您将获取提交的密码,对其进行哈希处理,并将其与数据库中的哈希值进行比较。 This way, you never store the password itself. 这样,您永远不会存储密码本身。 On the downside, if the user forgets their password, you have to reset it rather than send it to them. 在缺点方面,如果用户忘记了密码,则必须重置密码而不是将密码发送给他们。

Even this, however, is vulnerable to certain attacks. 然而,即便如此,也容易受到某些攻击。 If an attacker gets hold of your password hashes, and knows how you hash your passwords, then he can make a dictionary attack: he simply takes every word in the dictionary and hashes that word, keeping it with the original. 如果攻击者获取了您的密码哈希,并且知道如何散列您的密码,那么他就可以进行字典攻击:他只需将字典中的每个单词取出并散列该单词,并将其与原始单词保持一致。 This data structure is called a rainbow table. 该数据结构称为彩虹表。 Then, if any of the dictionary word hashes match a password hash, the attacker can conclude that the password is the dictionary word that hashes to that password. 然后,如果任何字典单词哈希值与密码哈希匹配,则攻击者可以断定密码是散列到该密码的字典单词。 In short, an attacker who can read your database can still log in to accounts with weak passwords. 简而言之,可以读取数据库的攻击者仍然可以使用弱密码登录帐户。

The solution is that before a password is hashed, it is combined (usually concatenated or xor'd) with a value called the salt which is unique to each user. 解决方案是在对密码进行哈希处理之前,将其与一个名为salt的值组合(通常连接或xor'd),该值对每个用户都是唯一的。 It may be randomly generated, or it may be an account creation timestamp or some such. 它可以是随机生成的,也可以是帐户创建时间戳或其他一些。 Then, an attacker cannot use a rainbow table because every password is essentially hashed slightly differently; 然后,攻击者无法使用彩虹表,因为每个密码基本上都有不同的哈希值; he would have to create a separate rainbow table for every single distinct salt (practically for each account), which would be prohibitively computationally expensive. 他必须为每一种不同的盐(实际上每个帐户)创建一个单独的彩虹表,这在计算上非常昂贵。

I will echo the advice of the other answerers: this is not simple stuff, and you don't need to do it because it's been done before, and if you do it yourself you stand a very good chance of making a mistake and inadvertently compromising your system's security. 我会回应其他回答者的建议:这不是简单的事情,你不需要这样做,因为它已经完成了,如果你自己这样做,你很有可能犯错并无意中妥协你系统的安全性。 But if, for whatever reason, you really, really want to write one yourself, I hope that I have provided an (incomplete!) outline of how it's done. 但是,无论出于何种原因,如果你真的想要自己写一个,我希望我提供了一个(不完整的)大纲,说明它是如何完成的。

The Zend Framework has an 'Auth' module which would be a good place to start. Zend Framework有一个'Auth'模块 ,这是一个很好的起点。 Or, if your site will be hosting an install of WordPress or PHPBB, there are ways of leveraging those technologies' authentication modules to sign in to other pages of a site. 或者,如果您的站点将托管WordPress或PHPBB的安装,则有一些方法可以利用这些技术的身份验证模块登录站点的其他页面。

One thing to look at when you are trying to authenticate is what is your real goal. 当您尝试进行身份验证时,要注意的一件事是您的真正目标是什么。

For example, on SO I use my google login, and that works, as they just need to know who I am, and they can trust that Google has an idea. 例如,在SO上我使用我的谷歌登录,这是有效的,因为他们只需要知道我是谁,他们可以相信谷歌有一个想法。 So, if that model will work for you, then look at using OpenID, as there are various tools for that. 因此,如果该模型适合您,那么请查看使用OpenID,因为有各种工具可供使用。

If you must do your own, then there will be various tests to ensure that it is secure, again, depending on how paranoid you want to be. 如果你必须自己做,那么将会有各种测试,以确保它是安全的,这取决于你想要的偏执。

  • Never trust anything from the user, unless you have used some strict verification. 除非您使用了一些严格的验证,否则永远不要相信用户的任何信息。
  • Use https to help protect the password of the user, you owe them that much. 使用https来帮助保护用户的密码,你欠他们那么多。

I will end my response here as Thom did a fantastic response. 我会在这里结束我的回应,因为Thom做了很棒的回应。

by Soulmerge: 由Soulmerge:

I think the accepted answer in your other question states it pretty well. 我认为你在另一个问题中接受的答案很清楚。 Hash the passwords with a salt. 用盐哈希密码。 Other than that, there are some security ideas on the transport layer: 除此之外,传输层有一些安全性想法:

  • Use https when sending passwords. 发送密码时使用https。 This makes sure nobody can catch them on the wire ( man-in-the-middle attack or the client uses an evil proxy) 这确保没有人可以在线上捕获它们( 中间人攻击或客户端使用恶意代理)
  • An alternative is to hash the password using javascript when the login form is submitted. 另一种方法是在提交登录表单时使用javascript对密码进行散列。 This makes sure that the password is never transported in plaintext. 这可确保密码永远不会以明文形式传输。 You should hash the hashed value again with a salt on the server. 您应该使用服务器上的salt再次散列哈希值。 ( md5($_POST['postedPwHash'] . $salt) ) md5($_POST['postedPwHash'] . $salt)

a good method to somewhat secure the client-server transaction (if no ssl is available) is to use a one-time random key to create a unique hash from the credentials, then only send that unique hash to the server. 有点保护客户端 - 服务器事务(如果没有可用的ssl)的好方法是使用一次性随机密钥从凭证创建唯一的哈希,然后只将该唯一哈希发送到服务器。 the server then compares this hash to its own generated hash instead of comparing it to the real credentials. 然后,服务器将此哈希与其自己生成的哈希进行比较,而不是将其与真实凭证进行比较。 this would provide a good defense against the man-in-the-middle attack. 这将为中间人攻击提供良好的防御。 the downside is that to do this the user must have JS enabled (at least i dont know of a good method to encrypt client-side data without it). 缺点是要做到这一点,用户必须启用JS(至少我不知道加密客户端数据的好方法,没有它)。 this means that you will need a sufficient fallback when it isn't on. 这意味着当它没有开启时你需要足够的后备。 you can even create the form in JS to make sure its enabled. 你甚至可以在JS中创建表单以确保它已启用。

this library is a simple library i wrote once that does the procedure i described, though it probably needs some improvements. 这个库是我写过的一个简单的库,它执行我描述的过程,尽管它可能需要一些改进。

note that this is in addition to using "salting" methods and other server-side security measures. 请注意,这是使用“salting”方法和其他服务器端安全措施的补充。 it is also quite vulnerable to dictionary attacks as the entire hashing process is by definition procedural, predictable and visible to the user (as JS always is). 它也非常容易受到字典攻击,因为整个哈希过程根据定义是程序性的,可预测的并且对用户可见(如JS一样)。

My answer is "Don't do it" 我的回答是“不要这样做”

This is a very complex area, full of potential security gotcha's. 这是一个非常复杂的领域,充满了潜在的安全问题。 If you are not an expert in this field, then you are really just asking for trouble and problems down the road. 如果你不是这个领域的专家,那么你真的只是在寻找麻烦和问题。

I would recommend looking at getting an existing solution to do. 我建议看一下现有的解决方案。 Sadly I don't know any that I would be happy to recommend, other than openid. 可悲的是,我不知道除了openid之外我会乐意推荐。 I'm sure you will get some good suggestions here though... 我相信你会在这里得到一些好的建议......

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

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