长度扩展攻击

解释

对此类攻击脆弱的散列函数的常规工作方式是：获取输入消息，利用其转换函数的内部状态；当所有输入均处理完毕后，由函数内部状态生成用于输出的散列摘要。因而存在着从散列摘要重新构建内部状态、并进一步用于处理新数据（攻击者伪造数据）的可能性。如是，攻击者得以扩充消息的长度，并为新的伪造消息计算出合法的散列摘要。

示例

一个用于向指定地点的用户递送指定种类华夫饼（即下面代码中的waffle）的服务器，可处理如下格式的请求：
原始数据: count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo
原始签名: 6d5f807e23db210bc254a28be2d6759a0f5f5d99
当且仅当该用户给出的签名对于其当前请求（向指定地点的用户1递送10个Eggo华夫饼）而言合法时，服务器才会实际处理该业务。该签名是一个消息认证码（MAC），由某个攻击者不可知晓的密钥签发。（事实上，这个例子对于重放攻击同样脆弱，攻击者亦可能通过二次发送同样的请求和签名来实施攻击。）
攻击者可能篡改该请求，在上述例子中，假设某攻击者把华夫饼的种类从“eggo”改为“liege”，这可以借助于消息格式本身的灵活性达到：对于请求字符串中重复的参数域，总是处理最后的参数。这样的灵活性并不能算作是消息格式本身的安全漏洞，因为消息格式在设计之初并未以安全性为前提；安全性需要通过签名算法的辅助来达到。
攻击者希望篡改的新数据: count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo&waffle=liege
为了给此新信息加上合法的签名，攻击者通常需要知道用于签名信息的密钥，并由此密钥生成一个新的MAC来作为新签名。然而，借助于长度扩展攻击手段，攻击者可以将散列（如上给出的签名）传递给散列函数作为原始状态，而从原请求处开始继续处理，这只需知道原请求的长度即可；在该请求中，原密钥的长度为14字节，这可以通过试探不同长度的伪造请求、并检查何种长度的请求被服务器接受而得到。
传递给散列函数的信息通常是填充（Padding）后的，因为许多算法只接受长度为指定大小倍数的输入。填充的内容是由采用的散列函数决定的。攻击者在新信息中除了包含原信息和伪造信息之外，还应当包含必需的填充位。因而，攻击者利用填充规则可以构造出如下信息：
新数据: count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo\x80\x00\x00
          \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
          \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
          \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
          \x00\x00\x00\x02&waffle=liege
该信息包含了散列函数中添加到原消息的填充位（在此示例中，是一个0x80，随后是若干0x00，最后是消息长度）。攻击者知道原消息的散列密钥/消息对所对应的状态直到最后一个“&”为止，均和新消息是相同的；攻击者此时亦知道其散列摘要，这意味着散列函数的内部状态已经被完全伪造。此时，初始化一个散列算法就非常简单了，给定剩余的字符串作为输入，即可生成一个用于签名的新摘要，而根本无须知晓原密钥。
新签名: 0e41270260895979317fff3898ab85668953aaa2
通过连接新签名和新数据成为一个新请求，服务器将把该伪造的请求视作一个有效的请求，因为其签名和在知道密码情况下生成的签名完全等效。
该攻击主要用于伪造已签名的消息，但亦可能存在其他用途。

实现

当前实现该类型攻击的工具并不多。一个借助于OpenSSL实现了针对多种散列函数的攻击的工具是HashPump。该工具支持针对MD5、SHA1、SHA256和SHA512的长度扩展攻击。SHA224和SHA384受此攻击的影响相对较小，由于这两个函数的输出分别是更长的散列函数（分别是256及512位）的前224位和前384位，因此其输出并不包含散列内部状态的全部长度，不能直接使用散列值进行长度扩展攻击。然而，SHA224和SHA384毕竟输出了更长散列的很大一部分，因此攻击者仍然可以轻易地首先穷举得到剩下的部分（缺失长度仅为32位和128位）后，再实施长度扩展攻击。因此，不能依赖截取部分散列来实现消息验证码。