哈希长度延展攻击

一、长度延展攻击

我们先来看看什么是长度延展,这样有利于你理解长度延展攻击

假设我们现在有两段数据 S 和 M,以及一个单向散列函数 h,我们把这两段数据合并起来,再计算合并后的散列值,这就是单向散列函数的长度延展

于是,问题来了,是 S 放在前面再计算 h(SM),还是 M 放在前面再计算 h(MS)?既然,我们说散列值是无法预测的,那么,数据的先后排列顺序还有意义吗?

如果 S 和 M 都是公开信息,顺序并不重要,可如果 S 是机密信息,M 是公开信息,那么,这两段数据的先后排列顺序就至关重要了,因为如果机密信息在前,就会有“长度延展攻击”的风险

弄清楚了长度延展,长度延展攻击就很好理解了,我们可以利用已知数据的散列值,计算原数据外加一段延展数据后的散列值,也就是说,如果我们知道了 h(SM),我们就可以计算 h(SMN),其中,N 就是在原数据基础上追加的延展数据

如果 S 和 M 都是公开信息,即便计算出延展数据的散列值也没什么要紧的,但是,如果 S 是机密数据,比如说,因为没有人知道我拥有的机密数据 S,所以,当我给定一段公开信息 M 后,只有我自己才能计算 SM 的散列值,通过验证 SM 的散列值,我就知道一个给定的散列值是我计算、派发出去的,还是别人伪造的

比如下面的这段数据:

key_id=44fefa051fc1c61f5e76f27e620f51d5&perms=read&hash_sig=38d39516d896f879d403bd327a932d9e

其中,key_id 表示机密数据的编号,perms 表示操作权限,hash_sig 是使用机密数据 key 对 perms 的签名,签名的计算使用的是单向散列函数,即 hash_sig = h(key|perms)

由于使用了机密数据 key,按照设想,这段数据只能由机密数据的持有者(请求者)生成,然后派发出去,供授权的人使用,机密数据的持有者(授权响应者)接收到这样的数据后,重新计算数据签名,然后对比请求数据里的签名,如果两个签名相同,表示这是一个合法的请求,就可以授予请求的权限

不过,这个设计有“长度延展攻击”的风险,攻击者并不需要知道机密数据,就可以通过一个已知的 URL,构造出一个新的合法的 URL,从而获得不同的授权,伪造的数据看起来像下面的样子:

key_id=44fefa051fc1c61f5e76f27e620f51d5&perms=read\0x80\0x00...\0x02&delete&hash_sig=a8e6b9704f1da6ae779ad481c4c165a3

在这段伪造的数据中,0x80 到 0x02 之间的数据是数据块补齐数据,并且新添加了删除权限,而且重新计算、替换了数据签名

其中,数据签名需要使用机密数据,而攻击者并不知道机密数据,那么攻击者如何伪造数据签名呢?要解决这个疑问,我们需要先看看单向散列函数的构造,一个典型的单向散列函数,应该由四个部分组成:数据分组、链接模式、压缩函数和终结函数

二、如何有效避免长度延展攻击

三、示例

#include <stdio.h>
#include <openssl/md5.h>

int main(int argc, const char *argv[])
{
  MD5_CTX c;
  unsigned char buffer[MD5_DIGEST_LENGTH];
  MD5_CTX c2;
  unsigned char buffer2[MD5_DIGEST_LENGTH];
  MD5_CTX c3;
  unsigned char buffer3[MD5_DIGEST_LENGTH];
  int i;

  MD5_Init(&c);
  MD5_Update(&c, "secret", 6);
  MD5_Update(&c, "data", 4);
  printf("A: %08x\n", c.A);
  printf("B: %08x\n", c.B);
  printf("C: %08x\n", c.C);
  printf("D: %08x\n", c.D);
  MD5_Final(buffer, &c);
  for (i = 0; i < MD5_DIGEST_LENGTH; i++)
    printf("%02x", buffer[i]);
  printf("\n");
  printf("A: %08x\n", c.A);
  printf("B: %08x\n", c.B);
  printf("C: %08x\n", c.C);
  printf("D: %08x\n", c.D);

  MD5_Init(&c2);
  MD5_Update(&c2, "secret", 6);
  MD5_Update(&c2, "data"
    "\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"
    "\x50\x00\x00\x00\x00\x00\x00\x00"
    "append", 64);
  MD5_Final(buffer2, &c2);
  for (i = 0; i < MD5_DIGEST_LENGTH; i++)
    printf("%02x", buffer2[i]);
  printf("\n");

  MD5_Init(&c3);
  MD5_Update(&c3, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 64);
  c3.A = htonl(0x6036708e); /* <-- This is the hash we already had */
  c3.B = htonl(0xba0d11f6);
  c3.C = htonl(0xef52ad44);
  c3.D = htonl(0xe8b74d5b);
  MD5_Update(&c3, "append", 6); /* This is the appended data. */
  MD5_Final(buffer3, &c3);
  for (i = 0; i < MD5_DIGEST_LENGTH; i++)
    printf("%02x", buffer3[i]);
  printf("\n");

  return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈希长度m的确定需要考虑以下几个因素: 1. 数据规模:哈希表用于存储一定量的数据,因此m的大小应该能够覆盖数据的数量。如果数据量较大,那么m的取值也应该较大,以保证哈希表分布均匀,避免冲突发生的概率过高。 2. 存储空间:哈希表的长度m决定了底层数组的大小,直接影响存储空间的大小。如果m取值过小,可能导致大量的哈希冲突,浪费存储空间;反之,如果m取值过大,可能会造成存储空间的浪费。因此,需要根据实际的存储需求和空间限制,选择适当的m值。 3. 哈希函数的设计:哈希函数的设计能够辅助决定哈希长度m的大小。一个良好的哈希函数应该能够将数据均匀地映射到哈希表中,减少哈希冲突的发生。在确定哈希函数的设计时,可以考虑数据类型、数据分布等因素,进而决定哈希长度m。 4. 时间复杂度和性能:哈希表的长度m直接关系到哈希表的性能。较小的m值可能会导致哈希冲突频繁发生,从而影响哈希表的性能;较大的m值可能会导致查找、插入、删除的操作时间复杂度增加。因此,需要在时间复杂度和性能之间做一定的平衡,选择适当的m值。 综上所述,哈希长度m的确定需要综合考虑数据规模、存储空间、哈希函数设计以及时间复杂度和性能等因素。在实际应用中,可以通过实验和测试,不断调整m的大小,以达到最佳的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值