Go

Why should you compare secrets with crypto/subtle.ConstantTimeCompare instead of ==? How do you hash passwords in Go?

Question 538HardGo 1.22 to 1.25

== and bytes.Equal return as soon as they hit the first byte that differs. By measuring response times over many requests, an attacker can recover a token or HMAC byte by byte. That is a timing side channel. subtle.ConstantTimeCompare(a, b) returns 1 or 0, and for equal-length inputs its running time depends only on the length. Gotcha: it returns 0 immediately when the lengths differ, which leaks the length. For secrets of variable length, hash both sides first. For MACs, use hmac.Equal.

func validAPIKey(got, want string) bool {
	g := sha256.Sum256([]byte(got))
	w := sha256.Sum256([]byte(want))
	return subtle.ConstantTimeCompare(g[:], w[:]) == 1
}

// Passwords: slow, salted, adaptive hash (golang.org/x/crypto/bcrypt)
hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost) // cost 10; tune to ~100ms+
if err != nil {
	return err // includes bcrypt.ErrPasswordTooLong for >72 bytes
}
if err := bcrypt.CompareHashAndPassword(hash, []byte(attempt)); err != nil {
	return errInvalidCredentials // constant-time internally
}

// Or Argon2id (golang.org/x/crypto/argon2) - memory-hard, OWASP-preferred
salt := make([]byte, 16)
rand.Read(salt) // crypto/rand
key := argon2.IDKey([]byte(pw), salt, 1, 64*1024, 4, 32) // store salt+params+key

Never store passwords as plain SHA-256 or MD5. Those hashes are fast, so GPUs can brute-force them. bcrypt stores the salt and cost inside the hash string. What the interviewer is looking for: the timing leak, the length caveat, the difference between a password KDF (bcrypt, scrypt, Argon2id) and a plain hash, and the fact that crypto/pbkdf2 joined the standard library in Go 1.24, which helps with FIPS requirements.

More on More Standard Library Essentials

All 16 More Standard Library Essentials questions