0x00 本篇出场人物

  1. 小Z -> Rust/Lua/C/Lisp/Ruby
  2. 小E -> TS/JS/Go/Haskell/Ruby

0x01 不一样的字符串比较..

[[茶水间]]

Z: 哎? 小E? 你也来用茶水啊…

E: 哦.. 是呀~ 哎? 怎么看你愁眉不展的~ 是遇到什么难题了吗?

Z: emm.. 我今天在 PlayFramework中 看到一段 进行数据验证的代码,啊.. 其实就是两个字符串的比较,但是我一直没想明白~

E: 字符串比较? 能给我看看嘛~

Z: 嗯~ 好,你来我座位,我演示给你看~

[小E 踮着小碎步 跟着我到了我座位旁…]

[[我的座位]]

Z: 你看一下,就是这段代码. 我改用了Rust.

fn safe_equal(a: String, b: String) -> bool {
    if a.len() != b.len() {
        return false;
    }
    let mut equal = 0;
    for i in 0..a.len() {
        equal |= a.as_bytes()[i] ^ b.as_bytes()[i];
    }
    if equal == 0 {
        return true;
    }
    return false;
}

TIPS:: 同值异或为零. eg: 1 ^ 1 = 0 , 1 ^ 0 = 1, 0 ^ 0 = 0

E: 嗯~ (字符的比较使用了 异或 加速..) 所以你的疑问点在哪里?

Z: 你不觉得,如果发现字符不匹配,立刻返回false, 会更加合理嘛? 比如改成这样..

fn equal(a: String, b: String) -> bool {
    if a.len() != b.len() {
        return false;
    }
    let mut equal = 0;
    for i in 0..a.len() {
        equal |= a.as_bytes()[i] ^ b.as_bytes()[i];
        if equal != 0 {
            return false;
        }
    }
    return true;
}

E: 我了解了~ 其实呀框架的作者应该是故意这么写的,你没看到函数名叫 safe_equal 嘛,既有 safe, 那我们也尝试一下从 safe 的角度考量,如果按你这样写,到底哪里 unsafe 了呢?

Z: [啊~ 光顾着想这个疑点,没注意到函数名这个细微的信息.. 不愧 小E 观察真仔细呢..] 我想不到哎~ 这方面算是知识盲区…

E: 你思考一下,计算机执行比较是需要消耗时间的,那么采用你这段代码运行的话,字符串前端相同的字符长度越短,执行的时间也就会越短,就有可能被有心之人利用哦~

Z: 可是这能相差多少呢? 可能几微秒吧??

E: 你这样想,可不符合正确的安全观哦. 如果放大这个事,比如,在Web应用时,记录每个请求的返回所需时间[通常是毫秒级],我们重复50次甚至更多,就可以得到平均时间或p50的时间,进而我们可以了解到哪个字符返回的时间比较长,当某个尝试的字符串的时间比较长,就可以确定出这个这字符串的前面一段大概率是正确的..

Z: 哦~ 原来还有这种方法啊..

计时攻击[Timing Attack] 是一种旁道攻击[ Side Channel Attack, 简称SCA ].

旁通道攻击 是指基于从计算机系统的实现中获得的信息的任何攻击,而不是基于实现的算法本身的弱点[例如,密码分析和软件错误]. 时间信息,功耗,电磁泄漏甚至声音等等,都可以提供额外的信息来源,进而加以利用. 在很多物理隔绝的环境中[黑盒],往往也能出奇制胜.

这类新型攻击的有效性远高于传统的基于数学分析的密码分析方法.

PS: 企图通过社会工程学欺骗或强迫具有合法访问权限的人来破坏密码系统通常不被视为旁道攻击.