谨以此文缅怀远去的耗子叔
防止计时攻击
计时攻击是边信道攻击(或称"侧信道攻击", Side Channel Attack, 简称SCA) 的一种,
边信道攻击是一种针对软件或硬件设计缺陷,走“歪门邪道”的一种攻击方式。
这种攻击方式是通过功耗、时序、电磁泄漏等方式达到破解目的。
在很多物理隔绝的环境中,往往也能出奇制胜,这类新型攻击的有效性远高于传统的密码分析的数学方法。
package main
import (
"fmt"
"time"
)
//时间复杂度恒定,均匀
func safeEqual(a, b string) bool {
if len(a) != len(b) {
return false
}
equal := 0
for i := 0; i < len(a); i++ {
equal |= int(a[i] ^ b[i])
}
return equal == 0
}
func unSafeEqual(a, b string) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if (a[i] ^ b[i]) != 0 {
return false
}
}
return true
}
func Compare(fn func(a,b string) bool, a, b string) int64 {
num := int(1e7)
start := time.Now()
for i := 0; i < num; i++ {
fn(a, b)
}
return time.Since(start).Nanoseconds()
}
func main() {
target := "hello"
key := []byte(target)
for i := 0; i < len(target); i++ {
key[i] = '-'
fmt.Printf("安全比较:[target %s | %s]->第%d位不同所用时间%d ns\n",target,string(key) ,i ,Compare(safeEqual,"hello",string(key))) // 45431600
key =[]byte(target)
}
fmt.Println("-----------------------------------")
key = []byte(target)
for i := 0; i < len(target); i++ {
key[i] = '-'
fmt.Printf("非安全比较:[target %s | %s]->第%d位不同所用时间%d ns\n",target,string(key) ,i ,Compare(unSafeEqual,"hello",string(key))) // 45431600
key =[]byte(target)
}
}
安全比较:[target hello | -ello]->第0位不同所用时间75467000 ns
安全比较:[target hello | h-llo]->第1位不同所用时间60944800 ns
安全比较:[target hello | he-lo]->第2位不同所用时间60506800 ns
安全比较:[target hello | hel-o]->第3位不同所用时间60753600 ns
安全比较:[target hello | hell-]->第4位不同所用时间60983700 ns
-----------------------------------
非安全比较:[target hello | -ello]->第0位不同所用时间30633900 ns
非安全比较:[target hello | h-llo]->第1位不同所用时间30279300 ns
非安全比较:[target hello | he-lo]->第2位不同所用时间40473700 ns
非安全比较:[target hello | hel-o]->第3位不同所用时间51045900 ns
非安全比较:[target hello | hell-]->第4位不同所用时间70867600 ns
选取一次比较均匀的结果,我们可以发现,safeEqual 函数,无论字符串哪一个位上的字符不一样,最终用时都非常均匀。
而unSafeEqaul函数,n位置上字符不同,函数整体运行的时长会有很大偏差。根据该偏差,可以使用精密的碰撞算法。攻击网站,从而拿到管理权限。
go
标准库提供了crypto/subtle
包来解决此类安全问题
func ConstantTimeByteEq(x, y uint8) int
func ConstantTimeCompare(x, y []byte) int
func ConstantTimeCopy(v int, x, y []byte)
func ConstantTimeEq(x, y int32) int
func ConstantTimeLessOrEq(x, y int) int
func ConstantTimeSelect(v, x, y int) int
但是safeEqual 真的就安全了吗? 其实不然
if len(a) != len(b) {
return false
}
长度判断时,会暴露我们key的长度。也就是说,攻击者可以穷举,从而拿到你密码的长度。