해시 함수란?
임의 길이의 입력값을 받아 고정 길이의 출력으로 반환하는 함수.
왜 쓰는가?
- 비밀번호 저장 (원본 없이 검증)
- 무결성 검증 (데이터 변조 확인)
- 데이터 식별 (Git 커밋 ID, 중복 제거)
- 디지털 서명, HMAC 등 더 큰 암호 시스템의 빌딩 블록
※ 흔히 "단방향 암호화"라고 부르지만 엄밀히는 암호화 아님.
복원 불가능한 변환이라 정확한 용어는 "암호학적 해시 함수".
핵심 성질
-
결정성 (Deterministic)
- 같은 입력은 항상 같은 출력.
- 해시값 비교로 사용자 인증이 가능한 이유.
-
단방향성 (One-way)
- 출력에서 입력을 역산할 수 없음.
- 비밀번호를 해시값으로 저장하는 이유.
-
충돌 저항성 (Collision Resistance)
- 다른 입력이 같은 출력을 내는 경우 = 충돌.
- 입력은 무한, 출력은 고정 길이라 충돌은 이론적으로 존재.
- 하지만 좋은 해시 함수는 그 충돌을 "찾는 것"이 사실상 불가능해야 함.
- 충돌이 쉬우면 디지털 서명 위조, 인증 우회 같은 공격이 가능해짐.
- MD5(2004), SHA-1(2017)은 이미 깨짐. 현재는 SHA-256 이상 사용 권장.
레인보우테이블
특정 입력값에는 특정 해시값이 나오는것을 미리 계산하여 저장해둔 테이블이다.
공격자들은 레인보우테이블을 미리 만들어 해시값을 유추하여 해킹공격을 할 수 있었다.
이러한 공격을 어렵게 만들기 위한 방법이 salting이다.
Salt
Salt란 해시값을 꼬기 위해 입력값에 추가로 붙이는 랜덤 값.
해시할_값 = {사용자 비밀번호} + salt
효과
-
레인보우 테이블 무력화
- 사용자마다 고유한 salt를 부여하면, 공격자는 사용자 수만큼
별도의 테이블을 만들어야 함 → 비용이 비현실적으로 커짐
-
같은 비밀번호 사용자 식별 방지
- 사용자 A와 B가 우연히 같은 비밀번호를 써도, salt가 다르면
DB에 저장된 해시값이 완전히 달라짐
- 한 명이 뚫려도 다른 사람은 영향 없음
중요: Salt는 비밀이 아님
- DB에 평문으로 저장됨 (검증 시 다시 사용해야 하므로)
- Salt의 목적은 "비밀 유지"가 아니라 "각 비밀번호를 유일하게 만드는 것"
대표 해시 함수
- 안전: SHA-256, SHA-512, SHA-3
- 깨짐: MD5, SHA-1 (보안 용도 사용 금지)
비밀번호 저장 전용
- 일반 해시 함수는 너무 빠름 → 무차별 대입에 취약
- bcrypt, scrypt, Argon2 같은 의도적으로 느린 함수 사용