safeEqual & 计时攻击

谨以此文缅怀远去的耗子叔

防止计时攻击
计时攻击是边信道攻击(或称"侧信道攻击", 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的长度。也就是说,攻击者可以穷举,从而拿到你密码的长度。

参考
https://coolshell.cn/articles/21003.html

你可能感兴趣的:(Go,数据结构与算法,golang)