# 密码安全
- 很多软件要求用户输入正确的密码,才能通过身份认证,有权执行某些操作。例如:
- Web 网站要求用户输入用户名、密码才能登录。
- 压缩文件用密码加密存储,要求用户输入密码才能解密。
- 一方面,软件的开发者应该保护用户的密码安全,避免冒名访问、泄露密码。
- 另一方面,用户也应该提高安全意识,例如:
- 不使用弱密码,否则容易被暴力破解。
- 不对别人分享密码,否则容易被冒名访问。
- 不在几个软件使用相同的密码,否则一个软件泄露了密码之后,其它软件也危险了。
# 破解密码
假设存在一个软件,一个用户,一个攻击者。用户输入自己的密码就能通过身份认证。
- 攻击者不知道用户的密码是什么,但可以尝试输入各种可能的密码,直到成功登录软件,从而猜出用户的密码。该过程称为破解密码。
破解密码的几种常见方式:
- 暴力破解(brute force attack)
- 原理:生成所有可能的密码字符串,逐个尝试。因此暴力破解又称为穷举法。
- 例如:假设已知用户的密码是 6 位数字,则攻击者可尝试 000000~999999 范围的所有可能密码。
- 优点:只要时间足够,一定能破解用户的密码。
- 缺点:需要尝试的次数太多,耗时久。
- 对策:当用户创建密码时,软件强制要求密码的长度至少为 8 ,不能是纯数字或纯字母。
- 例如使用大小写字母和数字,每位字符有 26+26+10=62 种可能性,密码长度为 12 时有 3*10e21 种可能性。即使每秒尝试 1 亿次,也要 100 万年才能穷举完所有可能的密码。
- 字典攻击(dictionary attack)
- 原理:将人们常用的密码保存为一个字典表,逐个尝试。
- 例如:统计表明,123456、abcdef 等密码简单易记,被很多用户使用,攻击者可尝试这些密码。
- 优点:需要测试的次数少,耗时少。
- 缺点:如果用户的密码不常见,则不会被破解。字典表会占用一定存储空间。
- 对策:软件事先准备一个字典表。当用户创建密码时,不允许创建在字典表中已有的密码。
- 撞库
- 原理:如果一个 Web 网站被入侵,泄露了数据库中存储的用户明文密码。则攻击者可用这些用户名、密码尝试登录其它 Web 网站,因为用户可能用同一用户名、密码注册了多个网站。
- 还会有人分析这些泄露的密码,找出被多个用户重复使用的密码,它们很可能被其他用户使用,因此加入字典表。
- 对策:服务器不应该将用户的明文密码存储在数据库中,而应该存储密码的哈希值。
- 暴力破解(brute force attack)
强密码(strong password):指不容易被破解的密码。
- 特点包括:长度至少为 8 ,包含大小写字母、数字等多种类型的字符,且不存在于已知的字典表。
- 理想的情况下,用户可以用软件随机生成一个长字符串,作为密码。这样很安全,但是难以记忆。
以下安全措施常被 Web 网站采用,而单机软件不能采用:
- 如果 Web 网站发现某个用户连续三次输错密码,则冻结其账号一段时间,禁止登录。或者只能通过更安全的身份认证方式登录,比如手机验证码。
- Web 网站要求用户在登录时输入图片验证码,确保用户是真人操作,使得攻击者难以用软件自动化尝试大量密码。
# 哈希密码
- Web 网站可以将用户密码的哈希值存储在数据库中,当用户下次输入密码时,就计算哈希值,如果与数据库存储的哈希值一致,则判断密码正确。
- 这种情况下,即使攻击者入侵了 Web 网站的数据库,也只能看到密码的哈希值,不知道对应的明文密码。
- 不过,即使只知道密码的哈希值,也可能通过以下方式破解出明文密码:
- 暴力破解
- 原理:穷举所有可能的密码,逐个计算哈希值。如果算出的哈希值与目标哈希值相同,则猜出了明文密码。
- 字典攻击
- 原理:事先记录人们常用的密码及其对应的哈希值。如果目标哈希值在字典中存在,则可以知道对应的明文密码。
- 彩虹表
- 原理:
- 先探明 Web 网站采用的是哪种 Hash 算法,记作函数 H() 。再拟定函数 R(),用于将哈希值 h 转换成另一个字符串 p 。然后选择一个字符串 p1 作为起点,依次计算出 h1=H(p1)、p2=R(h1)、h2=H(p2)、…… ,这样就得到了一条哈希链,记录其中的所有 p 。
- 选取多个 p1 ,就可以算出多条哈希链,得到一个彩虹表。
- 想破解某个哈希值 s 时,先计算 R(s) ,然后到彩虹表中寻找是否有与之相同的 p 值。假设 pn 的值与 R(s) 相同,则 s 对应的明文密码就是 pn-1 。
- 优点:彩虹表比字典攻击占用的存储空间少了一半,而且比暴力破解更快。
- 原理:
- 暴力破解
- 针对上述攻击方式,Web 网站可采用以下对策:
- 为每个用户计算密码的哈希值时,加上一个随机的、无法预测的值,称为盐。
- 服务器应该将每个用户的密码哈希值、盐值一起保存到数据库中。当客户端输入用户、密码时,服务器从数据库获取该用户的盐值,然后计算输入密码的哈希值,如果与数据库存储的哈希值一致,则判断密码正确。
- 这样即使攻击者知道了服务器采用的 Hash 算法,也不能事先准备一个字典表。
- 判断两个哈希值是否相同时,无论结果是 True 还是 False ,都应该控制在相同的耗时。
- 如果使用一般的字符串比较方法(从左到右逐位比较,遇到某一位的字符不同时就返回 False),就可能被攻击者根据处理时长的微小差异,逐位试出哈希值的每一位应该是什么值,大幅降低暴力破解的难度。
- 使用异或运算就可以控制在相同的处理时长。比如:a ^ b == 0
- 为每个用户计算密码的哈希值时,加上一个随机的、无法预测的值,称为盐。
# htpasswd
:Apache 提供的一个命令行工具,用于生成密码的哈希值。
- 安装:
yum install httpd-tools
- 用法:
htpasswd [username] [password] # 关于哈希算法 -d # 采用 CRYPT 算法 -B # 采用 BCRYPT 算法。推荐采用该算法,更安全 -m # 采用 MD5 算法(默认) -s # 采用 SHA-1 算法 -2 # 采用 SHA-256 算法 -5 # 采用 SHA-512 算法 # 关于输入 -b <password> # 通过命令参数输入密码。否则默认通过终端提示输入密码 -i # 从 stdin 读取密码 # 关于输出 -c <file> # 创建密码文件,如果该文件已存在则会被覆盖 -D <username> # 从文件中删除一个用户 -v <username> # 验证一个用户的密码 -n # 将加密结果输出到 stdout ,而不是文件
- 例:
[root@CentOS ~]# htpasswd -Bbn root 123456 # 生成密码的哈希值 root:$2y$05$8kv1HZMemYkavceQmrRC5umW6YJJokbixvgQn2sBsb.zrGBnxKs8O
[root@CentOS ~]# htpasswd -Bbc passwd root 123456 # 创建密码文件 passwd ,添加一个用户及密码的哈希值 Adding password for user root [root@CentOS ~]# htpasswd -Bb passwd admin 123456 # 再添加一个用户。如果该用户名已存在,则会覆盖其密码的哈希值 Updating password for user admin [root@CentOS ~]# htpasswd -B passwd admin # 再添加一个用户,默认通过终端提示输入密码 New password: Re-type new password: Adding password for user admin [root@CentOS ~]# cat passwd # 查看密码文件 root:$2y$05$DqIV6YHZ9Sw/UkpNFPTXFOW.DB2NmpEdhdzZO2GJkZ5FpEt.h7k3W admin:$2y$05$F9euMbpa.FNwOIJUcitzRu2k7oBv.3gmWG.eDmusFpsfskTe7Vj2i [root@CentOS ~]# htpasswd -D passwd admin # 删除一个用户 Deleting password for user admin
# hashcat
:一个命令行工具,用于破解各种哈希值的明文。
- 官方文档 (opens new window)
- 特点:
- 提供了 Linux、MacOS、Windows 等发行版。
- 支持 CPU、GPU 运算,但电脑需要安装驱动。
# 攻击方式
- 字典攻击(dictionary attack,代号为 0)
- :读取字典表中的每行 string 作为候选密码,分别尝试能否解密。
- 命令示例:
hashcat -m 0 hash.txt -a 0 dict.txt
- 组合攻击(combinator attack,代号为 1)
- :同时从多个字典表读取一行 string ,按先后顺序拼凑成候选密码,然后尝试。
- 命令示例:
hashcat -m 0 hash.txt -a 1 dict1.txt dict2.txt
- 掩码攻击(mask attack,代号为 3)
- :编写一个字符串模板,称为掩码,用于声明密码的每一位字符的取值范围。根据掩码生成候选密码,然后尝试。
- 命令示例:
hashcat -m 0 hash.txt -a 3 123?d?d?d # 候选密码的前 3 个字符是固定的 123 ,之后 3 个字符都来自字符集 ?d hashcat -m 0 hash.txt -a 3 -1 abc ?1?1?1?d?d?d # 候选密码的前 3 个字符来自自定义的字符集 ?1 ,之后 3 个字符来自字符集 ?d hashcat -m 0 hash.txt -a 3 -1 abc -2 ?l?u?d ?1?1?1?2?2?2 # 上例生成的候选密码的长度,固定等于掩码长度。下例是逐步增加候选密码的位数,从 3 位增加到 6 位 hashcat -m 0 hash.txt -a 3 123?d?d?d --increment --increment-min 3 --increment-max 5
- 掩码的语法像正则表达式。hashcat 提供了一些内置的字符集:还可用
?l # 小写字母 a-z ?u # 大写字母 A-Z ?d # 数字 0-9 ?s # 特殊字符 «space»!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ ?a # Ascii 字符集 ?l?u?d?s ?b # 十六进制值 0x00 - 0xff
-1 ...
、-2 ...
的格式创建自定义的字符集,然后用?1
、?2
的格式引用。最多创建 4 个自定义的字符集。 - 密码取值的可能性越多,称为密码空间(keyspace)越大。
- 用户为了方便记忆,通常用英文单词、电话号码组成密码,取值范围只是字母或数字。因此与暴力破解相比,使用掩码攻击可以缩小 keyspace ,提高破解效率。
- 如果掩码的 keyspace 太大,几乎不可能破解,则 hashcat 会报错:
Integer overflow detected in keyspace of mask
- 混合攻击(hybrid Attack,代号为 6 或 7 )
- :给字典表中每行 string 添加掩码作为前缀、后缀,生成候选密码,然后尝试。
- 命令示例:
hashcat -m 0 hash.txt -a 6 dict.txt ?d?d?d # -a 6 表示添加掩码作为后缀 hashcat -m 0 hash.txt -a 7 ?d?d?d dict.txt # -a 7 表示添加掩码作为前缀
- 规则攻击(rule-based attack)
- :根据几种规则来生成候选密码,然后尝试。比如只包含小写字母、首字母大写。
- 规则攻击比掩码攻击更复杂,但能缩小 keyspace 。
# 命令
命令语法:
hashcat [option]... <hash|hashfile> [dictionary|mask]... # 关于本机 -I # --opencl-info ,显示本机已识别的 opencl 设备。hashcat 默认会自动选用一个设备进行哈希运算,也可用 -D 1,2 -d 1 的格式指定一个设备 -D # --opencl-device-types ,允许使用哪些 opencl 设备类型,包括 CPU、GPU、FPGA -d # --opencl-devices ,允许使用哪些编号的 opencl 设备 -b # --benchmark ,运行性能测试,测试每秒能完成多少次哈希运算 # 关于输入 -m # --hash-type ,待破解的哈希值所属的算法编号,详见 https://hashcat.net/wiki/doku.php?id=example_hashes -a # --attck-mode ,攻击方式的代号,比如掩码攻击是 3 --username # 忽略输入的 hash 中的用户名,提取其中的哈希值进行破解 --session <name> # 指定当前的 session 名称,默认为 hashcat # 关于输出 --quiet # 隐藏输出 --force # 隐藏输出中的警告 # hashcat 默认会将所有 session 已破解的所有哈希值、明文记录到 potfile 文件。因此重复破解一个哈希值时,不会显示已破解的明文 --potfile-path=~/.hashcat/hashcat.potfile # 指定 potfile 文件的路径 --potfile-disable # 禁用 potfile 文件 --show # 不执行 session ,而是查询 potfile ,输出当前 hash 中已破解的哈希值、明文 --left # 不执行 session ,而是查询 potfile ,输出当前 hash. 中未破解的哈希值 -o out.txt # --outfile ,指定一个文件,用于记录当前 session 的输出
- hashcat 将破解任务称为 session 。在执行 session 时可以创建 checkpoint ,自动将 session 的执行进度记录到
~/.hashcat/sessions/<name>.restore
文件,以后可执行hashcat --session <name> --restore
来恢复执行 session 。
- hashcat 将破解任务称为 session 。在执行 session 时可以创建 checkpoint ,自动将 session 的执行进度记录到
命令示例:
apt install hashcat apt -I apt -b # 破解 md5 哈希值 echo -n 123456 | md5sum | awk '{print $1}' > hash.txt hashcat -m 0 hash.txt -a 3 ?d?d?d?d?d?d # 破解 bcrypt 哈希值 htpasswd -Bbn root 123456 > hash.txt hashcat -m 3200 hash.txt -a 3 ?d?d?d?d?d?d
命令的输出示例:
root@ubuntu:~# hashcat -m 0 hash.txt -a 3 ?d?d?d?d?d?d ... e10adc3949ba59abbe56e057f20f883e:123456 Session..........: hashcat # 当前 session 的名称 Status...........: Cracked # 当前 session 的状态 Hash.Type........: MD5 Hash.Target......: e10adc3949ba59abbe56e057f20f883e # 要破解的目标哈希值 Time.Started.....: Tue Jun 18 15:09:11 2022 (0 secs) # session 的开始时刻 Time.Estimated...: Tue Jun 18 15:09:11 2022 (0 secs) # session 的预计结束时刻 Guess.Mask.......: ?d?d?d?d?d?d [6] Guess.Queue......: 1/1 (100.00%) Speed.#2.........: 1659.6 MH/s (0.22ms) @ Accel:128 Loops:100 Thr:256 Vec:1 Recovered........: 1/1 (100.00%) Digests, 1/1 (100.00%) Salts # 输入的哈希值中,已破解的数量、总数 Progress.........: 1000000/1000000 (100.00%) # 已尝试的候选密码数量,即 session 的执行进度 Rejected.........: 0/1000000 (0.00%) # 被拒绝的候选密码数量,比如有的候选密码不符合哈希算法的长度限制,不必尝试 Restore.Point....: 0/10000 (0.00%) # checkpoint 所处的进度 Restore.Sub.#2...: Salt:0 Amplifier:0-100 Iteration:0-100 Candidates.#2....: 120123 -> 688373 # 当前尝试的候选密码的范围 Hardware.Mon.#2..: Temp: 37c Util:100% Core:1113MHz Mem:2999MHz Bus:16 Started: Tue Jun 18 15:09:06 2022 Stopped: Tue Jun 18 15:09:13 2022
在执行过程中,可按以下按键:
[s]tatus [p]ause [b]ypass [c]heckpoint [q]uit =>