PHP/MySQL 安全登录和会话

5

我有一个登录服务与我的当前网站相关,我想知道是否有任何特定的方法可以称之为最安全的?

让我更好地解释一下我的系统:

我目前拥有一个PHP MySQL数据库,其中包含一个用户表。用户名和密码都存储为VARCHAR(对于密码来说并不是最好的方式)。

在注册表单方面,我通过仅允许a-Z 0-9输入并限制字符数来控制密码和用户名的选择。在登录表单方面,我使用mysql_real_escape_string停止攻击,并使用POST到iFrame而不是AJAX。

我觉得我正在尽力防止来自表单方面的攻击,但没有从数据库方面进行保护。我知道您可以更改密码存储类型以在输入时加密,但我不明白如何查询此加密字符串。

根据我描述的情况,您会建议增加哪些安全性措施以及为什么?您选择什么方法来预防黑客和攻击?您能否看到我所描述的任何明显的安全漏洞?最重要的是,鉴于我在Web开发游戏中的经验不足,我该怎么做才能纠正这些问题?

(请注意,我不是创建用于存储机密或引人注目数据的系统)


1
最安全的方式是SSL +个人证书。虽然我怀疑你的网站真的需要最安全的方式。 - Your Common Sense
2
另外要提的一件事是,正如某位聪明的SOer所说,安全不是可以随意添加的东西。它不是一个东西,而是一个过程。 - Your Common Sense
但是,即使任何过程都可以分解为一系列操作指令。当系统完成时,它将成为一个过程。 - Dan Hanly
1
建议不要使用md5,而是使用SHA1,并且一定要使用盐值。这样即使你的数据库被破解了,也能够增加密码破解难度,防止彩虹表攻击。 - DMin
1
@Daniel Hanly: 用户的盐值与用户数据(用户名等)一起存储,因此您需要知道登录的是哪个用户(通常登录表单同时传递用户名和密码,因此这不是问题)。有关该主题的稍微高级一些的阅读,请参见此处:http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html - Piskvor left the building
显示剩余3条评论
4个回答

5

首先,不要在数据库中存储明文密码。因此,在注册新用户时,请将其密码作为哈希值插入。MD5最常用于此。我建议使用盐来增加安全性。因此,用于存储的内容如下:

$password = md5($yoursalt . $_POST['password']);

现在当用户想要登录时,您再次拥有他们的密码。现在像之前一样进行相同的哈希操作,并在数据库中搜索此哈希(使用其用户名)。这样您就不会存储他们的实际密码。


2
对于“至少使用哈希和盐”,加1分;对于“使用MD5”,扣1分。现在是2010年,对于密码哈希来说,MD5已经过时了。请至少使用SHA-2函数之一。 - Piskvor left the building
谢谢,这真的很有帮助。当用户输入密码并从数据库中获取加密密码后,我的思维过程停止了,所以我没有考虑加密输入的密码并进行比较。谢谢。 - Dan Hanly
另外,$yoursalt 应该是每个用户独有的,而不是每个站点共用的;这一点在你的回答中没有明确提到。 - Piskvor left the building

3
将密码哈希为MD5或SHA1是Web安全的重要部分。匹配密码的方式是首先使用提供的用户名从数据库中检索哈希密码,然后将用户提供的密码进行哈希处理,再将两个哈希值进行比较。由于MD5和SHA1是单向哈希,这意味着如果用户忘记了密码,则无法检索当前密码。需要重新创建一个新的密码。您可以通过使用PHP的mcrypt()函数来解决此问题。
您还应该允许在密码中使用特殊字符。这只会增加暴力攻击的复杂性。
您还可以在3次错误尝试后锁定帐户一段时间,甚至可以在10次尝试后锁定IP地址。虽然后者属于“tar pits”类别。但这会让黑客的攻击变得更加困难。

谢谢。我现在正在尝试使用一些加密技术,就像我在上面的回答中所说的那样,我的想法没有延伸到加密输入的密码并进行比较:(感谢您非常清晰地回答问题,很多人会向我扔很多术语,而且由于经验不足(我只有22岁),这不是最好的学习方式。 - Dan Hanly
使用mcrypt对密码进行加密非常危险,因为它是可逆的。任何一种密码保护方法都不应该是可逆的。 - MRW

1

我在生成密码时总是使用用户特定的盐,因此我的“用户”表有两个字段“encrypted_salt”和“password”

它的核心是我使用sha1(encrypted_salt + 'password');

在生成encrypted_salt时,我会使用sha1(unqid(true));它使用自己的额外熵(盐)将时间精确到毫秒级别,生成几乎100%唯一的盐。 几乎不可能出现重复。

所以:

$salt = sha1(unqid(true));
$password = sha1($_POST['password']);

存储在数据库中为:

$pw = sha1($salt.$password); or sha1($password.$salt);

这并不重要。

SHA1的美妙之处在于它总是会生成一个40个字符的字符串。你能够为字符串添加更多的盐,这样做越好,特别是如果你能够为每个密码生成一个唯一的盐。


我考虑将盐基于用户的加密版本的用户名+用户ID,您是否也认为这是可行的? - Dan Hanly
是的,由于用户的ID(pc)应该始终是唯一的,作为最佳实践,我总是在哈希中使用某种时间戳,因此您可以使用microtime(true)或time()或unqid(),因为PHP不能处理多线程,所以两个用户在完全相同的毫秒数添加的可能性很小。但是,用户名.id也可以,特别是经过哈希处理后。 - MRW
使用哈希算法,我认为sha1和md5是个人偏好。我总是使用sha1(),但md5同样出色! - MRW
我无法强调不使用全局盐的重要性。始终使用基于用户的盐。 - MRW
如果黑客获取了盐值,就能够获得访问权限。因此我会构建一个漂亮的安全盐值,为每个用户提供独特的保护。 :) - Dan Hanly

1
就你的 SQL 来说,不要使用像 mysql_real_escape_string 这样的字符串操作函数,因为在某些情况下它仍然会允许注入攻击发生。使用参数化查询,从而确保数据库始终将所有用户提供的字符串视为数据,而不是可能的 SQL 代码片段。
PHP 中的 MySQLi 和 PDO 库都支持参数化查询。

谢谢,我该如何将这些库实现到我的代码中?我该如何执行参数化查询? - Dan Hanly
这方面有大量的文档和教程。这是其中一个 http://forum.codecall.net/php-tutorials/12442-php-5-mysqli-prepared-statements.html - Cheekysoft

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接