1. 솔트 없는 해시의 위험성
CWE-759는 비밀번호와 같은 민감한 정보를 해시 함수(SHA-256 등)로 변환할 때, 매번 고유한 추가 데이터인 **솔트(Salt)**를 섞지 않을 때 발생합니다. 해시 함수는 동일한 입력에 대해 항상 동일한 결과를 내놓는 '결정론적' 특성이 있습니다. 공격자는 이를 악용해 미리 계산된 해시 값 테이블인 **레인보우 테이블(Rainbow Table)**을 사용하여, 유출된 해시 값을 순식간에 원래의 비밀번호로 되돌릴 수 있습니다. 또한, 서로 다른 사용자가 같은 비밀번호를 쓸 경우 해시 값이 같게 나타나 정보 노출의 실마리를 제공합니다.
2. 흔히 발생하는 취약한 패턴
단순히 "암호화했다"는 사실에 안주할 때 주로 나타납니다.
• 단순 해시 적용: hash(password)와 같이 입력값만으로 해시를 생성하는 경우.
• 고정된 솔트 사용: 모든 사용자의 비밀번호에 동일한 문자열(예: "company_name")을 솔트로 섞는 경우. 이는 솔트의 의미를 퇴색시키며, 공격자가 해당 고정 솔트 기반의 레인보우 테이블을 새로 만들면 무용지물이 됩니다.
• 취약한 알고리즘 결합: MD5나 SHA-1처럼 충돌 공격에 취약한 알고리즘을 솔트 없이 사용하는 경우.
3. 실무적 대응: 사용자별 고유 솔트와 키 스트레칭
비밀번호 저장의 정석은 '예측 불가능성'을 높이는 것입니다.
• 사용자별 고유 솔트 생성: 사용자가 가입하거나 비밀번호를 바꿀 때마다 SecureRandom을 사용하여 최소 16바이트 이상의 무작위 솔트를 생성합니다.
• 솔트와 함께 저장: 생성된 솔트는 해시 값과 함께 DB에 저장되어야 나중에 로그인 검증 시 사용할 수 있습니다.
• 키 스트레칭(Key Stretching): 해시 연산을 수천 번 반복하여 공격자의 무차별 대입 속도를 늦춥니다. (PBKDF2, BCrypt, Argon2 등의 알고리즘은 이 기능을 내장하고 있습니다.)
4. CWE-759 대응 및 안전한 솔트 적용 자바 코드 예시
코멘트: 해시 함수는 암호화(Encryption)가 아닌 요약(Digest)입니다. 솔트가 없는 해시는 이름표가 없는 열쇠꾸러미와 같아서 공격자가 대조해보기 너무 쉽습니다. 프로젝트에서 직접 해시를 구현하기보다는 BCrypt나 SCrypt 같은 검증된 라이브러리를 사용하면 솔트 관리와 반복 횟수(Iteration)를 자동으로 처리해 주므로 훨씬 안전합니다. logger.debug()를 통해 로직은 확인하되, 솔트 값이나 해시 결과가 로그에 노출되지 않도록 필터링하는 센스도 잊지 마세요.
댓글 달기