메뉴 건너뛰기

app

양념 없는 암호의 위험: CWE-759 솔트 없는 해시 사용

suritam92026.01.17 23:01조회 수 0댓글 0

    • 글자 크기

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 대응 및 안전한 솔트 적용 자바 코드 예시

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;
 
public class PasswordEncryptionManager {
    private static final Logger logger = LoggerFactory.getLogger(PasswordEncryptionManager.class);
 
    // [CWE-759 조치] 무작위 솔트를 생성하고 해시 연산에 포함
    public String hashPasswordWithSalt(String password) {
        try {
            // 1. 보안상 안전한 무작위 솔트 생성
            SecureRandom random = new SecureRandom();
            byte[] salt = new byte[16];
            random.nextBytes(salt);
 
            // 2. SHA-256 해시 함수 준비
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            
            // 3. 솔트를 먼저 업데이트하여 해시 결과의 예측 불가능성 확보
            md.update(salt);
            byte[] hashedPassword = md.digest(password.getBytes());
 
            // 4. 검증을 위해 솔트와 해시값을 함께 인코딩하여 반환
            String saltStr = Base64.getEncoder().encodeToString(salt);
            String hashStr = Base64.getEncoder().encodeToString(hashedPassword);
 
            logger.debug("Password hashed successfully with a unique salt.");
            return saltStr + ":" + hashStr;
 
        } catch (Exception e) {
            // [CWE-754, CWE-390] 예외 처리
            logger.debug("Hashing failed: {}", e.getMessage());
            return null;
        }
    }
}

코멘트: 해시 함수는 암호화(Encryption)가 아닌 요약(Digest)입니다. 솔트가 없는 해시는 이름표가 없는 열쇠꾸러미와 같아서 공격자가 대조해보기 너무 쉽습니다. 프로젝트에서 직접 해시를 구현하기보다는 BCryptSCrypt 같은 검증된 라이브러리를 사용하면 솔트 관리와 반복 횟수(Iteration)를 자동으로 처리해 주므로 훨씬 안전합니다. logger.debug()를 통해 로직은 확인하되, 솔트 값이나 해시 결과가 로그에 노출되지 않도록 필터링하는 센스도 잊지 마세요.

 
    • 글자 크기
외부 코드의 위험한 초대: CWE-494 무결성 검사 없는 코드 다운로드 (by suritam9) 해커를 위한 친절한 설명서: CWE-615 주석 내 중요 정보 노출 (by suritam9)

댓글 달기

첨부 (0)
위로