PHP 开发者如何做好密码保护 & Laravel 底层密码存储和验证实现
随着在线攻击的增多,密码安全越来越重要。作为开发者我们要担负起安全管理、计算哈希和存储用户密码的责任,不管应用是简单的游戏还是绝密商业文件的仓库,都要做到这一点。PHP内置了一些工具,让保护密码变得更加容易,本节我们就来讨论如何根据现代的安全措施来使用这些工具。
1、密码保护三原则
绝对不能知道用户的密码
我们绝对不能知道用户的密码,也不能有获取用户密码的方式,如果应用的数据库被黑,你肯定不希望数据库中有纯文本或能解密的密码。任何时候,知道的越少越安全。
绝对不要约束用户的密码
如果要求密码符合特定的模式,其实是为不怀好意的人提供了攻击应用的途径,如果必须约束密码,我建议只限制最小长度,把常用的密码或基于字典创建的密码加入黑名单也是好主意。
绝对不能通过电子邮件发送用户密码
如果你通过电子邮件给用户发送密码,用户会知道三件事:你知道他的密码,你使用纯文本或能解密的方式存储了他的密码,你没有对通过互联网发送纯文本的密码感到不安。
我们应该在电子邮件中发送用于设置或修改密码的URL,Web应用通常会生成一个唯一的令牌,这个令牌只在设定或修改密码时使用一次(比如修改密码),通常我们把这个令牌作为设置或修改密码URL的一个参数,当用户访问这个URL时,应用会验证令牌是否有效,如果有效则继续操作,操作完成后,令牌失效,不能重复使用。
2、密码存储算法
关于密码存储的最佳实践是计算密码的哈希值,而不是加密用户的密码。加密和哈希不是一回事,加密事双向算法,加密的数据可以解密,而哈希是单向算法,哈希后的数据不能再还原成原始值,而且相同的数据得到的哈希值始终相同。
在数据库中存储用户的密码,要先计算密码的哈希值,然后在数据库中存储密码的哈希值,如果黑客攻入数据库,只能看到无意义的密码哈希值,需要花费大量的时间和NSA资源才能破解。
哈希算法有很多种(如md5、SHA1、bcrypt和scrypt),有些算法速度很快,用于验证数据完整性;有些算法的速度则很慢,旨在提高安全性。生成密码和存储密码时要使用速度慢、安全性高的算法。
目前,最安全的算法当属bcrypt,与md5和SHA1不同,bcrypt故意设计得很慢,bcrypt会自动加盐(salt),防止潜在的彩虹表攻击,bcrypt算法会花费大量时间反复处理数据,生成特别安全的哈希值。在这个过程中,处理数据的次数叫工作因子,工作因子的值越高,破解密码所需的时间越长,安全性越好。bcrypt算法永不过时,如果计算机运算速度变快了,我们只需提高工作因子的值。
3、密码哈希API
通过前面的介绍,我们知道在处理用户的密码时要考虑很多东西,好在PHP 5.5.0原生的哈希API(http://php.net/manual/zh/book.password.php)提供了很多易于使用的函数,大大简化了计算密码哈希值和验证密码的操作,而且,这个密码哈希API默认使用bcrpt算法。
开发Web应用时,有两个地方会用到密码哈希API:注册用户和用户登录,下面我们以Laravel提供的用户注册和登录为例,看看PHP密码哈希API时如何简化这两个操作的。
注:Laraval框架内置的用户注册和登录功能正是使用了PHP哈希API实现密码的存储和验证。
注册用户
用户注册在AuthController中完成,新用户的创建在该控制器的create方法中实现:

可以看到这里使用了Laravel提供的辅助函数bcrypt对用户提交的密码进行哈希并保存到数据库。bcrypt函数定义如下:

这里我们可以看出实际上是调用了别名为hash的服务提供者实例上的make方法实现哈希密码,进入HashServiceProvider,在register方法中我们可以看到hash对应的类为BcryptHasher,在该类中我们找到了make方法:

这里的核心是调用了PHP提供的password_hash函数,该函数接收三个参数,第一个是用户输入的密码值,第二个参数是使用的哈希算法(更多算法查看:http://php.net/manual/zh/password.constants.php),第三个参数可选,包括salt和cost两个选项,分别表示干扰字符串(加盐)和前面提到的工作因子,工作因子可以随着硬件性能的提升而提升,不传的话使用随机加盐和默认工作因子(计算哈希值一般需要0.1~0.5s)。如果计算失败,抛出异常。
用户登录
在Larval中以在auth.php中使用session作为guards、eloquent作为providers实现用户登录认证为例(实际上默认设置就是这样),登录验证最终会走到EloquentUserProvider的validateCredentials方法:

$this->hasher对应的实现也是BcryptHasher类,我们来查看它的check方法:

其中传入的第一个参数是用户输入的密码,第二个参数是用户注册时保存的密码哈希值,如果哈希值为空直接返回false,否则调用php提供的password_verify函数,该函数用于验证密码(纯文本)和哈希值是否匹配,匹配返回true,否则返回false。
重新计算哈希值
通过上述步骤用户已经可以实现登录认证了,但是登录前我们还需要检查现有的密码哈希值是否已经过期,如果过期,需要重新计算密码哈希值。
为什么要重新计算呢?加入我们的应用创建于两年前,那时候使用的工作因子时10,现在使用的是20,因为计算机的速度更快了,黑客也更聪明了。可以有些用户的密码哈希值仍然是工作因子为10时生成的,这时,登录认证通过后,要使用password_needs_refresh函数检查用户记录中现有的哈希值是否需要更新,这个函数可以确保指定的密码哈希值是使用最新的哈希算法创建的。如果确实需要重新计算生成密码的哈希值,要使用make方法生成新的哈希值并更新数据库中的原密码。
Laraval中目前并没有使用这一功能,但是在BcryptHasher类中已经提供了对应的函数:

PHP 开发者如何做好密码保护 & Laravel 底层密码存储和验证实现的更多相关文章
- Laravel底层原理系列
Laravel 从学徒到工匠精校版 地址:https://laravelacademy.org/laravel-from-appreciate-to-artisan Advanced Applicat ...
- Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能
Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...
- opesntack 底层共享存储 迁移配置
底层共享存储在迁移配置: 每台compute 节点都需要配置一下 让nova用户可以登陆 usermod -s /bin/bash nova 设置nova 用户密码 echo "nova&q ...
- EXTJS 密码确认与验证
extjs 框架是一个非常优秀的前端框架,提供了丰富的功能与炫丽的界面展示,在用 extjs 创建表单时,特别是在注册或修改密码的时候,要对密码进行确认,这里就密码确认一致性验证和大家分享自己的心得与 ...
- Oracle11g R2创建PASSWORD_VERIFY_FUNCTION对应密码复杂度验证函数步骤
Oracle11g R2创建PASSWORD_VERIFY_FUNCTION对应密码复杂度验证函数步骤 运行测试环境:数据库服务器Oracle Linux 5.8 + Oracle 11g R2数据库 ...
- oracle 11g/12c 密码复杂度验证设置
############################################################################### ###### 11g ###### ## ...
- Python开发之用户密码存储
在各种线上应用中,用户名密码是用户身份认证的关键,它的重要性不言而喻.一方面,作为保护用户敏感数据的钥匙来说,一旦被破解,系统将敞开大门完全不设防.另一方面,密码这把钥匙本身就是非常敏感的数据:大多数 ...
- 密码存储中MD5的安全问题与替代方案
md5安全吗?有多么地不安全?如何才能安全地存储密码?... md5安全吗? 经过各种安全事件后,很多系统在存放密码的时候不会直接存放明文密码了,大都改成了存放了 md5 加密(hash)后的密码,可 ...
- Laravel 5.1 关掉csrf验证
Laravel 5.1 关掉csrf验证 说明: Laravel默认是开启了CSRF功能,有时可能不能传递验证token,就需要关闭. 方法一(全局关闭): 打开文件:app\Http\Kernel. ...
随机推荐
- C 字符串奇数位小写字母转大写
如题 C实现 #include<stdio.h> #include<string.h> #define COUNT 20 //最大接受字符串数,可以使用动态获取空间函数优化 v ...
- Java中的增强型for循环
下面是关于增强型for循环对一维数组与二维数组遍历的具体实现: public class NewForLoop { public static void main(String[] args) { i ...
- js读取txt文件并下载
//我在vue中测试, 可行, 这个只适用于google浏览器, ie需要xObject对象 // 下载txt if (/(txt)$/.test(name[1])) { var xhr = new ...
- UITextFIeld的输入格式问题 ----W.P
---恢复内容开始--- 1.银行卡的输入格式,四个数字,一个空格. 2.金额的输入格式限制,不能以"."开头,"."后精确两位. 注意:第三方输入软件(百度, ...
- Eclipse 堆栈和内存大小设置(转载)
1, 设置Eclipse内存使用情况 修改eclipse根目录下的eclipse.ini文件 -vmargs //虚拟机设置 -Xms40m -Xmx256m -XX:PermSize=128M ...
- ios 类别和扩展-赵小波
类别 @interface ClassName ( CategoryName ) // method declarations @end Category在iOS开发中使用非常频繁.尤其是在为系统类进 ...
- MySQL基本命令操作及数据库基本概念
MySQL基本命令操作及数据库基本概念 1.数据库的基本概念 2.主流数据库介绍 3.关系型非关系型数据库介绍 4.Mysql 安装方法 5.Mysql 基本操作命令 1.数据库的基本概念: 数据库的 ...
- JS 获取JSON返回的时间值转换为通常格式展示
var date = new Date(parseInt(数据源.slice(6))); //获取到时间 年月日时分秒 var result = date.getFullYear() + '/' ...
- Solution -「多校联训」消失的运算符
\(\mathcal{Description}\) Link. 给定长度为 \(n\) 的合法表达式序列 \(s\),其中数字仅有一位正数,运算符仅有 - 作为占位.求将其中恰好 \(k\) ...
- LRU缓存及实现
一.淘汰策略 缓存:缓存作为一种平衡高速设备与低速设备读写速度之间差异而引入的中间层,利用的是局部性原理.比如一条数据在刚被访问过只有就很可能再次被访问到,因此将其暂存到内存中的缓存中,下次访问不用读 ...