来自计时攻击的一课(或者,不要使用 MessageDigest.isEquals )

仅中文 | 中英文对照 | 仅英文 | 打印此文章
对于某些打算写一个安全的密码系统的人的角度来说,计时攻击相当可怕。它工作原理是依赖于程序员的最佳本能—不要做多余的工作,而这给攻击者得到一个统计101教科书(而这能很好的得到您应用程序的核心)。

这东西是怎么工作的呢?

简言之,一个计时攻击使用一个统计学的分析关于您的应用程序使用多长时间去做某些事情,这样就能知道关于应用程序操作的数据。对HMACs而言,这意味着使用一定数量的值(您的应用程序花掉的时间的值)和一个改定的计算值比较,这样就能得到这个计算值的信息。

瑞新
瑞新
翻译于 4年前
1人顶
顶 翻译得不错哦!
拿 最近Nate Lawson发现的Keyczar 漏洞 来说。他能够通过一个简单的冲突不等式算法(比较一个候选HMAC时间摘要和Keyczar计算时间摘要)破解Keyczar的事实。

这是攻击代码使用Python:

return self.Sign(msg) == sig_bytes
使用Java:

return Arrays.equals(hmac.doFinal(), sigBytes);
如加密计算摘要一样,如果没有字节就马上返回一个值;如果有15个字节,则返回15次比较后的一个值。虽然他们的不同是微秒级别的,但是已经能够得到足够的尝试-这通常容易在web应用程序中整理(有多少伴随错误会话cookies的请求)-随机的噪音会变得非常容易整理,会按照一定的规则分布倾斜,只留下真正的信号。

瑞新
瑞新
翻译于 4年前
1人顶
顶 翻译得不错哦!
这似乎并没有那么糟
但是恰恰相反。

我可以选择一个我想被授权的消息 —它描述的是一个特定用户的会话cookie—然后计算256位可能的值:

0000000000000000000000000000000000000000
0100000000000000000000000000000000000000
0200000000000000000000000000000000000000
... 省略 250 ...
FD00000000000000000000000000000000000000
FE00000000000000000000000000000000000000
FF00000000000000000000000000000000000000
我计算这些值,直到我找到—A100000000000000000000000000000000000000—这个值使用的时间明显比别的使用的时间超过一小部分的微秒值. 然后我马上直到这个消息的HMAC的第一个的字节很大可能A1. 重复这个过程计算余下的19个字节, 然后突然我使用登陆进去了你的账号。

瑞新
瑞新
翻译于 3年前
1人顶
顶 翻译得不错哦!
你不可能进行衡量,你能吗?

现在开始, 大部分人开始思考关于在web应用程序中衡量两个数组的比较的计时发生的几率, 考虑所有的路由, 解析器, 服务器, 代理, 等等.

根据 Crosby et al的 计时攻击的机会和限制:

我们已经证明, 尽管互联网会有很大的时间延迟, 我们还是可以可靠地分辨低至20µs远程时间差异. 如果局域网的话会有更小的时间延迟, 允许我们区分远程时间差异100ns(甚至更小). 时间差异只要有数百或数千次测量就能够更加精确.

瑞新
瑞新
翻译于 3年前
2人顶
顶 翻译得不错哦!
所以.几乎数百微秒就完成了成千上万的计算.谁敢断定网络延迟会变得更差而不是更好?

最糟糕的情况下进行一个HMAC 需要请求20×256×n 计算, 其他n 是计算请求计算需求确认的单个字节的数量. 所以需要 5,000,000倍数的请求. 在每秒10个请求的情况下(几乎难以察觉),你可以不到一个星期就可以完成.

这种攻击方式需要有一些计划和分析, 但是是可行的.

瑞新
瑞新
翻译于 3年前
1人顶
顶 翻译得不错哦!
好了,废话少说,现在怎么做呢?
您可以使用constant-time算法,用来替换variable-time算法比较加密. Lawson 提供了Python的实现如下:

def is_equal(a, b):
    if len(a) != len(b):
        return False

    result = 0
    for x, y in zip(a, b):
        result |= x ^ y
    return result == 0
Java 实现,如下:

public static boolean isEqual(byte[] a, byte[] b) {
    if (a.length != b.length) {
        return false;
    }

    int result = 0;
    for (int i = 0; i < a.length; i++) {
      result |= a[i] ^ b[i]
    }
    return result == 0;}

耶!问题解决了,是吗?
Oh, 如果仅仅是上面这样,那么是的。

检查在最近Java 6.0 Update 15更新的 java.security.MessageDigestas中的代码,如下:

/**  * Compares two digests for equality. Does a simple byte compare.  *  * @param digesta one of the digests to compare.  *  * @param digestb the other digest to compare.  *  * @return true if the digests are equal, false otherwise.  */public static boolean isEqual(byte digesta[], byte digestb[]) {
    if (digesta.length != digestb.length)
        return false;

    for (int i = 0; i < digesta.length; i++) {
        if (digesta[i] != digestb[i]) {
            return false;
        }
    }
    return true;}

瑞新
瑞新
翻译于 3年前
1人顶
顶 翻译得不错哦!

等待什么

Yep. 一个字节一个字节的比较;返回第一个不相等的. 但是不是我们需要的.

我在这里直接的说: 任何java程序(通过MessageDigest.isEqualis比较客户端提供数据和加密值比较的) 都是容易受到计时攻击的. 这里包括 HMACs, 解密结果, 等等.

在2009年7月22号,我把向sun提交了这个问题. 就是问题: # 6863503, 由于安全问题,这是不可以公开的.除了包含问题票号的自动回复邮件,我到目前为止没有从sun听到关于这个问题的任何回复. 在我们问题报告中, 我表达了我的意图:我将会遵守 RFPolicy, 内容:

如果维护者在5个工作日内没有和问题发现者进行任何联系, 这个问题发现者将会选择公开这个问题. 维护者负责定期提供状态更新 (关于解决这个问题) 最短每五天一次.

所以我在这里,完全公开在这个最大的编程平台上的一个相当大的漏洞. 如果我不说那就太可怕了.

瑞新
瑞新
翻译于 3年前
1人顶
顶 翻译得不错哦!
长话短说

像上面的例子那样,使用等值时间算法来取代您使用的MessageDigest.isEqual.

每次你比较两个值, 问问你自己: 如果有人知道这两个值,他们可以做什么? 如果答案是有意义的,使用等值时间算法来比较.

附注

如果更多的加密类库能够有更好的封装,这个问题会小的多. 一个HMAC不仅仅是一系列的字符和字节—为什么这么说? 为什么这么关键的加密算法会出现这种情况的纰漏?

谢谢

Nate Lawson的Keyczar促使我思考出怎么计时攻击我自己的代码.他的何时密码攻击 讲述了需要请给每个人查看编译器的请求.

瑞新
瑞新
翻译于 3年前
1人顶
顶 翻译得不错哦!
2009年8月13号更新

sophacles 在 黑客上指出, 我更多的重构推荐的 constant-time 算法, 并且通过返回状态为布尔值的更加简短表达式,介绍更多计时攻击漏洞的细节. 而这个算法的已经通过这次更新解决了.

2009年12月3号更新

关于MessageDigestwas的计时攻击漏洞已经在Java SE 6 Update 17更新中解决。

文章转载自 开源中国社区 [http://www.oschina.net]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值