# 密码安全

  • 很多软件要求用户输入正确的密码,才能通过身份认证,有权执行某些操作。例如:
    • Web 网站要求用户输入用户名、密码才能登录。
    • 压缩文件用密码加密存储,要求用户输入密码才能解密。
  • 一方面,软件的开发者应该保护用户的密码安全,避免冒名访问、泄露密码。
  • 另一方面,用户也应该提高安全意识,例如:
    • 不使用弱密码,否则容易被暴力破解。
    • 不对别人分享密码,否则容易被冒名访问。
    • 不在几个软件使用相同的密码,否则一个软件泄露了密码之后,其它软件也危险了。

# 破解密码

  • 假设存在一个软件,一个用户,一个攻击者。用户输入自己的密码就能通过身份认证。

    • 攻击者不知道用户的密码是什么,但可以尝试输入各种可能的密码,直到成功登录软件,从而猜出用户的密码。该过程称为破解密码。
  • 破解密码的几种常见方式:

    • 暴力破解(brute force attack)
      • 原理:生成所有可能的密码字符串,逐个尝试。因此暴力破解又称为穷举法。
      • 例如:假设已知用户的密码是 6 位数字,则攻击者可尝试 000000~999999 范围的所有可能密码。
      • 优点:只要时间足够,一定能破解用户的密码。
      • 缺点:需要尝试的次数太多,耗时久。
      • 对策:当用户创建密码时,软件强制要求密码的长度至少为 8 ,不能是纯数字或纯字母。
        • 例如使用大小写字母和数字,每位字符有 26+26+10=62 种可能性,密码长度为 12 时有 3*10e21 种可能性。即使每秒尝试 1 亿次,也要 100 万年才能穷举完所有可能的密码。
    • 字典攻击(dictionary attack)
      • 原理:将人们常用的密码保存为一个字典表,逐个尝试。
      • 例如:统计表明,123456、abcdef 等密码简单易记,被很多用户使用,攻击者可尝试这些密码。
      • 优点:需要测试的次数少,耗时少。
      • 缺点:如果用户的密码不常见,则不会被破解。字典表会占用一定存储空间。
      • 对策:软件事先准备一个字典表。当用户创建密码时,不允许创建在字典表中已有的密码。
    • 撞库
      • 原理:如果一个 Web 网站被入侵,泄露了数据库中存储的用户明文密码。则攻击者可用这些用户名、密码尝试登录其它 Web 网站,因为用户可能用同一用户名、密码注册了多个网站。
      • 还会有人分析这些泄露的密码,找出被多个用户重复使用的密码,它们很可能被其他用户使用,因此加入字典表。
      • 对策:服务器不应该将用户的明文密码存储在数据库中,而应该存储密码的哈希值。
  • 强密码(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

:一个命令行工具,用于破解各种哈希值的明文。

# 攻击方式

  • 字典攻击(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 。
  • 命令示例:

    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 =>