简介
Basic Auth 用于服务端简单的登录认证, 通常使用服务器 Nginx,Apache 本身即可完成. 比如我们要限定某个域名或者页面必须输入用户名, 密码才能登录, 但又不想使用后端开发语言, 此时 Basic Auth 就派上用场了.
Basic Auth 使用 htpasswd 工具进行生成 http 基本认证的密码文件.
流程
首先说一下 Basic Auth 使用流程.
创建认证文件
新建一个文件 auth_basic_user_file, 例如:
- # 创建目录
- sudo mkdir -p /usr/local/nginx/
- # 生成文件
- sudo touch /work/yphp/nginx/nginx-htpasswd
文件名就是 nginx-htpasswd.
生成密码
使用 htpasswd 工具生成密码文件:
- # 安装 htpasswd
- sudo apt-get install apache2-utils
- # 生成密码
- sudo htpasswd -c -d /work/yphp/nginx/nginx-htpasswd yujc
- New password:
- Re-type new password:
- Adding password for user yujc
- # 查看文件内容
- cat //work/yphp/nginx/nginx-htpasswd
- yujc:sBoB9G5lTLvPk
这里解释说明一下:
htpasswd 是开源 http 服务器 apache httpd 的一个命令工具, 所以本机如果没有该命令, 需要先安装.
htpasswd 命令最后一个参数是用户名, 也就是需要登录的用户名. 命令运行后, 会要求输入该用户登录时的密码, 这里我输入了 123. 最终我们发现会往 / work/yphp/nginx/nginx-htpasswd 文件添加了一行内容.
其中冒号前面的就是用户名, 后面是加密的密码.
如果没有 htpasswd 工具怎么办呢? 也可以借助在线的工具生成: http://tool.oschina.net/htpasswd . 加密方式选择 crypt. 点击生成后, 把生成结果追加到 nginx-htpasswd 文件里即可:
- cat /work/yphp/nginx/nginx-htpasswd
- yujc:sBoB9G5lTLvPk
- yujc2:RF8ulInobr21M
大家会发现, 相同的密码, 每次生成的结果都不一样, 没关系, 只要密码没变, 最终都能登录的. 原因在后面再说明.
Nginx 配置 Basic Auth
- server {
- listen 80;
- server_name test.com;
- auth_basic "登录认证";
- auth_basic_user_file /work/yphp/nginx/nginx-htpasswd;
- root /mnt/html/www;
- index index.html index.php;
- }
重启 Nginx 服务后, 访问 test.com 就会要求输入用户名, 密码.
备注: 一定要注意 auth_basic_user_file 路径, 如果文件不存在, 会不厌其烦的出现 403.
如果只想某一个页面支持 Basic Auth, 可以将 auth_basic 配置到 location 里:
- location /test {
- auth_basic "登录认证";
- auth_basic_user_file /work/yphp/nginx/nginx-htpasswd;
- }
htpasswd 加密方式
MD5: 使用 MD5 加密密码. 在 Windows, Netware 和 TPF 上, 这是默认的加密方式.
crypt: 使用 crypt() 加密密码. 在除了 Windows, Netware 和 TPF 的平台上, 这是默认的. 虽然它在所有平台上可以为 htpasswd 所支持, 但是在 Windows, Netware 和 TPF 上不能为 httpd 服务器所支持.
SHA: 使用 SHA 加密密码. 它是为了方便转入或移植到使用 LDAP Directory Interchange Format (ldif) 的 Netscape 而设计的.
plain: 不加密, 使用纯文本的密码. 虽然在所有平台上 htpasswd 都可以建立这样的密码, 但是 httpd 后台只在 Windows, Netware 和 TPF 上支持纯文本的密码.
通常我们使用 crypt 加密方式. 如果你使用 PHP 语言, 内置的 crypt() 函数即可实现加密.
crypt
crypt() 返回一个基于标准 UNIX DES 算法或系统上其他可用的替代算法的散列字符串.
这里以 PHP 的 crypt 为例子说明. 该函数原型:
string crypt ( string $str [, string $salt ] )
salt 参数是可选的. 然而, 如果没有 salt 的话, crypt() 创建出来的会是弱密码. php 5.6 及之后的版本会在没有它的情况下抛出一个 E_NOTICE 级别的错误. 为了更好的安全性, 请确保指定一个足够强度的盐值.
我们使用该函数生成密码的 hash 值, 使用不同的 salt 值:
- php> echo crypt("123", "123456");
- 12IbR.gJ8wcpc
- php> echo crypt("123", "abcde");
- abLEFxdWWYR3c
然后复制到 / work/yphp/nginx/nginx-htpasswd:
- #yujc:sBoB9G5lTLvPk
- yujc2:12IbR.gJ8wcpc
- yujc3:abLEFxdWWYR3c
输入 123 均能登录成功.
注意: 测试新用户需要将已登录用户注释掉, 无需重启 nginx.
为什么验证的时候我们并没有告诉 nginx 的 salt 是多少, 但是还能验证? 为什么同一密码不同 salt 产生的 hash 都能验证?
我们看下面的例子:
- $ php -a
- Interactive mode enabled
- php> echo crypt("123", "12IbR.gJ8wcpc");
- 12IbR.gJ8wcpc
- php> echo crypt("123", "abLEFxdWWYR3c");
- abLEFxdWWYR3c
- php> echo crypt("123", "12test");
- 12IbR.gJ8wcpc
大家应该发现了什么. 我们把加密后的 hash 当做 salt 再次使用 crypt 函数, 生成的 hash 竟然与传入的 salt 相同. 然后我们把 salt 前 2 位保持不变, 后面的改成其他的, 再使用 crypt 函数, 生成的 hash 没有变化. 这说明 crypt 函数只与 salt 的前几位有关系: 只要前几位不变, 生成的 hash 是一样的.
我们既没有指定使用的算法, 也没有指定盐值, crypt 是怎么知道使用什么算法和盐值的呢? 其实 crypt 是根据 $salt 参数来判断使用哪种哈希算法. 也就是说,$salt 本身就包含了算法的类型以及哈希时实际用到盐值.
实际使用的算法判断有下面这几种情况:
标准 DES(Standard DES)
在没有匹配到其他算法的情况下, 则使用标准 DES 算法, 此时取前两个字符为盐值 (不足两个字符则返回 * 0). 盐值的字符必须是./0-9A-Za-z 里的字符.
- php> echo crypt("123", "t123");
- t1ZzgDe4z3qWE
- php> echo crypt("123", "123");
- 12IbR.gJ8wcpc
- php> echo crypt("123", "t");
- *0
扩展 DES(Extended DES)
以下划线_开头, 后面紧接着 4 字节的迭代次数和 4 字节的盐值. 也就是取前 9 位, 后面是什么值无所谓.
- php> echo crypt("123", "_12345678");
- _12345678VaI36zUn7Jk
- php> echo crypt("123", "_12345678t");
- _12345678VaI36zUn7Jk
- php> echo crypt("123", "_testtest");
- _testtest4v7fH1Er0Ng
- php> echo crypt("123", "_testtest1");
- _testtest4v7fH1Er0Ng
- MD5
以 $1$ 开头, 然后是 12 个字符以内的盐值. 只要前 12 位相同, 生成的 hash 相同.
- php> echo crypt("123", '$1$');
- $1$$GmbL3iXOMZR57QuGDLv.L1
- php> echo crypt("123", '$1$2');
- $1$2$WOzAAwhejT62wplMg6rEE1
- php> echo crypt("123", '$1$23');
- $1$23$0ZjnChzzaj90xZQJQKHFS1
- php> echo crypt("123", '$1$123456789');
- $1$12345678$tRy4cXc3kmcfRZVj4iFXr/
- php> echo crypt("123", '$1$1234567890');
- $1$12345678$tRy4cXc3kmcfRZVj4iFXr/
注意: PHP 里双引号里面的字符串如果包含 $ 会被认为是变量.
lowfish
以 $2a$,$2x$ 或者 $2y$ 开头, 然后是用于 cost 参数的两位数字, 紧接着一个 $ 字符, 最后是 22 位./0-9A-Za-z 范围里的字符.
- php> echo crypt("123", '$2a$07$usesomesillystringforsalt$');
- $2a$07$usesomesillystringforeN7/2NBfGxbAuv02IPrTFBImFJd5PJ1m
- php> echo crypt("123", '$2a$07$usesomesillystringforsalt$1');
- $2a$07$usesomesillystringforeN7/2NBfGxbAuv02IPrTFBImFJd5PJ1m
使用这个算法的时候,$str 最长支持 72 个字符, 超过会被截掉.
SHA256
以 $5$ 开头, 然后是 16 个字符的盐值, 盐值之前还可以使用 rounds=$ 的格式表明哈希的循环次数 (N).
- php> echo crypt("123", '$5$rounds=5000$usesomesillystringforsalt$');
- $5$rounds=5000$usesomesillystri$BYJncGl82VuZ6T61c4wSpXT.xoDSuz9aF4JyE9F08U4
rounds 的默认值为 5000, 范围是 1000 到 999,999,999, 如果 N 不在这个范围里, 会被截取到最接近的范围里.
SHA512
以 $6$ 开头, 然后是 16 个字符的盐值, 盐值之前还可以使用 rounds=$ 的格式表明哈希的循环次数 (N).
- php> echo crypt("123", '$6$rounds=5000$usesomesillystringforsalt$');
- $6$rounds=5000$usesomesillystri$YPNvueKNHmPrzbloaqIomo1gPrVo8aLnqwrKlhlfThu2wzo73efrh/FCR4CAUf/GFe7gF6vuLWTMyFNb7jfnT1
rounds 的默认值为 5000, 范围是 1000 到 999,999,999, 如果 N 不在这个范围里, 会被截取到最接近的范围里.
PHP 里我们使用 crypt 函数, salt 直接传第一个参数的 base64_encode 即可:
$hashed_password = crypt ( 'mypassword', base64_encode('mypassword') );
参考
1,Nginx 配置 Basic Auth 登录认证 - 简书
https://www.jianshu.com/p/b4a78af4e266
2,PHP 笔记 -- crypt 方法 - 个人文章 - SegmentFault 思否
- https://segmentfault.com/a/1190000009219416
- 3,PHP: crypt - Manual
- http://php.net/manual/zh/function.crypt.php
来源: https://www.cnblogs.com/52fhy/p/9657293.html