"매 execution time 의 secret-dependent variation 의 leak". 1996 Kocher RSA timing paper origin — comparison/branch/cache 가 secret bit 에 dependent 면 attacker 가 multiple measurement 의 statistical analysis 로 secret 을 추출. Mitigation 의 핵심: constant-time code.
매 핵심
매 Vulnerable patterns
Early-exit string compare: == / strcmp returns on first mismatch byte.
Secret-dependent branch: if (key[i] == 0) 의 cache miss timing differs.
Secret-dependent table lookup: AES S-box → cache line timing.
Modular exponentiation: square-and-multiply 의 bit-dependent ops.
매 Mitigation
Constant-time compare: scan all bytes regardless, XOR-accumulate.
No secret-dependent branches: use bitwise mask instead.
No secret-dependent indices: scan full table or use bit-slicing.
Blinding: randomize input (RSA: blind w/ random r, decrypt, unblind).
매 응용
HMAC token comparison (auth bypass via timing).
Password hash compare (after bcrypt — still need constant-time).
JWT signature verify.
💻 패턴
Vulnerable: early-exit compare
// ❌ DON'T — leaks prefix length
functionbadCompare(a: string,b: string):boolean{if(a.length!==b.length)returnfalse;for(leti=0;i<a.length;i++){if(a[i]!==b[i])returnfalse;// early exit reveals match length
}returntrue;}// Attacker measures: "aaaa" vs "baaa" faster than "aaaa" vs "aaab"
Constant-time compare (Node.js)
import{timingSafeEqual}from'node:crypto';functionsafeCompare(a: string,b: string):boolean{constbufA=Buffer.from(a);constbufB=Buffer.from(b);if(bufA.length!==bufB.length){// length leak unavoidable — pad to fixed length OR accept
timingSafeEqual(bufA,bufA);// dummy compare to equalize timing
returnfalse;}returntimingSafeEqual(bufA,bufB);}// HMAC token check
import{createHmac}from'node:crypto';functionverifyToken(token: string,payload: string,secret: string):boolean{constexpected=createHmac('sha256',secret).update(payload).digest('hex');returnexpected.length===token.length&&timingSafeEqual(Buffer.from(expected),Buffer.from(token));}
Browser: SubtleCrypto + manual constant-time
// Web Crypto has no timingSafeEqual — implement carefully
functionctEqualBytes(a: Uint8Array,b: Uint8Array):boolean{if(a.length!==b.length)returnfalse;letdiff=0;for(leti=0;i<a.length;i++){diff|=a[i]^b[i];// XOR-accumulate, no branch
}returndiff===0;}asyncfunctionverifyHmac(msg: string,sig: ArrayBuffer,key: CryptoKey){constcomputed=awaitcrypto.subtle.sign('HMAC',key,newTextEncoder().encode(msg));returnctEqualBytes(newUint8Array(computed),newUint8Array(sig));}
Python constant-time compare
importhmacdefverify(token:str,expected:str)->bool:returnhmac.compare_digest(token,expected)# bcrypt — already constant-time via libraryimportbcryptdefcheck_password(plain:bytes,hashed:bytes)->bool:returnbcrypt.checkpw(plain,hashed)# safe internally
Go constant-time
import"crypto/subtle"funcverify(a,b[]byte)bool{returnsubtle.ConstantTimeCompare(a,b)==1}// Conditional copy without branchfuncctSelect(condint,a,b[]byte){subtle.ConstantTimeCopy(cond,a,b)}
Branchless conditional (C-style)
// Constant-time conditional select
functionctSelect(cond: number,a: number,b: number):number{// cond must be 0 or 1
constmask=-cond;// 0 or 0xFFFFFFFF
return(a&mask)|(b&~mask);}// Constant-time min/max without branch
functionctMin(a: number,b: number):number{constlt=(a-b)>>>31;// 1 if a<b
returnctSelect(lt,a,b);}
Remote timing (network attacks)
Lucky 13 (TLS 2013): MAC verify timing leaked plaintext.
CRIME / BREACH: compression length leaked secrets (different side channel).
Mitigation:
- Always run full computation regardless of input validity.
- Add randomized delay (debatable — may not help, can hurt).
- Rate-limit + monitoring for anomalous timing-probe traffic.
매 결정 기준
상황
Approach
Password hash check
bcrypt/argon2 lib (already CT)
HMAC/token compare
timingSafeEqual / hmac.compare_digest
AES on untrusted host
AES-NI (HW) or bit-sliced soft impl
Secret-dependent index
bitwise mask or full-table scan
RSA/ECDSA private op
use vetted lib (BoringSSL, libsodium) — never roll your own
기본값: never write your own crypto compare. Use language stdlib timingSafeEqual / compare_digest / subtle.ConstantTimeCompare.
언제: auth code review, HMAC/token compare, custom crypto routines, security audit.
언제 X: high-level app logic where no secrets are compared (UI rendering, business rules).
❌ 안티패턴
a === b for secrets: V8/JIT may early-exit, branch, or short-circuit.
Custom "constant-time" without testing: compiler can re-introduce branches via optimization. Test with dudect.
Throwing on mismatch: exception path differs in timing from success path.