배너 이미지

해싱 보안 이슈 및 강화 가이드

2025. 4. 26. 23:40·CS공부/기타 CS지식

해싱(Hashing)은 임의 길이 입력을 고정 길이 출력(해시값)으로 변환하는 일방향 함수다.
비밀번호 저장, 파일 무결성 검증, API 키·토큰 관리 등 다양한 보안 시나리오에서 필수적으로 활용된다.
그러나 단순 해싱만으로는 무차별 대입 공격, 레인보우 테이블 공격, 충돌 공격 등
여러 위협에 취약할 수 이다.
이 문서에서는 주요 해싱 공격 벡터를 깊이 있게 분석하고, 솔루션(솔트, 페퍼, KDF, 토큰 해싱)과
실제 적용 시 고려 사항을 설명해보겠다.


1️⃣ 해싱 공격 기법 상세 분석

1. 무차별 대입 공격 (Brute-Force Attack)

  • 원리
    모든 가능한 입력(예: 짧은 비밀번호 조합)을 해시 함수에 대입해 저장된 해시값과 비교
  • 실제 사례
    8자리 알파벳+숫자 비밀번호(62^8 ≈ 2.18e14)도 GPU 최적화된 환경에서 수시간 내 공격 가능
  • 방어 전략
    • 느린 해싱 함수(KDF): bcrypt, Argon2 등은 해시 계산 속도가 의도적으로 느림
    • 반복(iterations) 조정: 반복 횟수를 늘려 공격 비용 증가

2. 사전/레인보우 테이블 공격 (Dictionary / Rainbow‑Table)

  • 사전 공격
    자주 사용되는 비밀번호(“password”, “123456” 등)를 사전 목록으로 준비
  • 레인보우 테이블
    (원문 → 해시) 매핑을 미리 계산해 저장 → salt 없이 해시 저장 시 즉시 역추적
  • 방어 전략
    • Salt: 사용자별 고유 랜덤 값 추가 → 사전·테이블 모두 무효화
    • Salt 보관: 해시 저장 시 salt와 hashedPassword 모두 DB에 저장

3. 충돌 공격 (Collision Attack)

  • 원리
    서로 다른 두 입력이 동일한 해시값을 생성 → 데이터 위변조 가능
  • 취약 대상
    MD5, SHA‑1 등 오래된 해시 함수에서 수천 회 연산으로 충돌 발견
  • 방어 전략
    • SHA‑256, SHA‑3 및 SHA‑2 계열 사용
    • 비밀번호 용도 외 중요 데이터는 HMAC 적용

4. 길이 확장 공격 (Length Extension Attack)

  • 원리
    Merkle–Damgård 구조 해시(예: SHA‑256)에서 H(m)만으로도 H(m∥pad∥extra) 계산 가능
  • 위험
    API 서명 우회, 토큰 변조 등 악용 사례 보고
  • 방어 전략
    • HMAC 사용: HMAC-SHA256(token, key) 구조로 내부 패딩 노출 차단
    • SHA‑3 알고리즘 전환

2️⃣ Salt & Pepper 적용 방안

1. Salt(솔트)

  • 비밀번호 저장의 문제
    • 사용자 비밀번호를 그대로 저장하면, DB가 유출될 경우 모두 노출된다.
    • 단순 해시(예: SHA‑256)만 적용해도, 같은 비밀번호는 항상 같은 해시가 생성돼서
      공격자는 미리 계산된 테이블(레인보우 테이블)로 빠르게 복원할 수 있다.
  • Salt란?
    • '솔트'는 랜덤한 데이터를 비밀번호 앞뒤에 섞는 값이다.
    • 결과적으로, 같은 비밀번호라도 다른 해시가 만들어진다.
  • 적용 예제 (Java / BCrypt)
  • // 1) 솔트 생성: work factor(복잡도) 조정 가능 String salt = BCrypt.gensalt(12); // 12번 반복해서 솔트 생성 // 2) 비밀번호+솔트를 해시 String hashed = BCrypt.hashpw(password, salt); // 저장할 때는 salt와 hashed 둘 다 DB에 기록
  • 주의 사항
    1. 솔트는 항상 충분히 긴 랜덤 바이트를 사용한다. (최소 16바이트).
    2. 매번 해시할 때마다 새로운 솔트를 생성해야 한다.

2. Pepper(페퍼)

  • Salt만으로 충분하지 않은 이유
    • 솔트는 DB에 함께 저장되므로, DB가 유출되면 솔트도 함께 탈취된다.
  • Pepper란?
    • 솔트와 다르게, 서버(혹은 외부 키 관리 시스템)에만 저장하는 비밀값이다.
    • DB에 없으므로, 공격자가 DB를 훔치더라도 페퍼를 알 수 없다.
  • 적용 예제 (Java)
  • // 1) 시스템 환경 변수나 보안 저장소에서 가져온 페퍼 String pepper = System.getenv("PEPPER_KEY"); // 2) salt + 비밀번호 + pepper 조합 String combined = salt + password + pepper; // 3) 안전한 해시 함수(예: SHA-256)로 최종 해시 String secureHash = HashUtil.sha256(combined);
  • 관리 팁
    • AWS KMS, HashiCorp Vault 같은 키 관리 서비스에 페퍼 보관
    • 주기적 교체(롤링)을 통해 보안을 더욱 강화

3️⃣ 느린 해싱 함수(KDF, Key Derivation Function)

느린 해싱이 왜 필요한가?

  • 빠르면 공격자에게만 유리
    보통 해시 함수는 빠르게 계산되지만, 공격자에게도 빠른 계산을 허용한다.
  • 느린 해싱은 해시 계산에 일부러 시간/메모리 비용을 추가해
    무차별 대입(brute-force) 공격의 속도를 크게 줄인다.

1. bcrypt (비크립트)

  • 원리: Blowfish 암호화를 여러 번 반복
  • 설정: work factor (rounds)로 반복 횟수를 조정한다.
  • 예제
  • // bcrypt 모듈이 솔트를 포함해 해시를 생성 String hashed = BCrypt.hashpw(password, BCrypt.gensalt(12)); // gensalt(12): 2^12 = 4096번 암호화 반복
  • 장점
    • 자동으로 솔트를 포함
    • 검증된 라이브러리 지원

2. Argon2 (아르곤투)

  • 원리: 메모리 비용과 CPU 비용을 모두 조절 가능
  • 특징: 메모리를 많이 사용해 GPU 공격 효율을 낮춤
  • 예제
  • Argon2Parameters params = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id) .withMemoryAsKB(65536) // 64MB 메모리 사용 .withIterations(3) // 3회 반복 .withParallelism(1) // 한 번에 1스레드 사용 .build(); Argon2BytesGenerator gen = new Argon2BytesGenerator(); gen.init(params); byte[] hash = new byte[32]; gen.generateBytes(password.getBytes(StandardCharsets.UTF_8), hash);
  • 권장 설정
    • 메모리: 최소 64MB
    • 반복: 3회 이상
    • 병렬성: 1~2 스레드

3. scrypt (스크립트)

  • 원리: 큰 메모리 사용 + CPU 연산
  • 특징: 메모리 비용이 높아 GPU 병렬 공격이 매우 느려진다.
  • 라이브러리 예
  • byte[] salt = SecureRandomUtil.generate(16); byte[] derived = SCrypt.scrypt(password.getBytes(), salt, 1<<14, 8, 1, 32);

4️⃣ 토큰 해싱 전략

1. Refresh Token 해싱 저장

// 1) 원본 Refresh Token 발급
String rawToken = UUID.randomUUID().toString();

// 2) Salt 생성
String salt = SecureRandomUtil.generate(16);

// 3) 해시화
String tokenHash = HashUtil.sha256(salt + rawToken);

// 4) Redis 저장
String key = "refresh:" + tokenHash;
redisTemplate.opsForValue().set(key, userId, Duration.ofDays(30));

2. 검증 과정

  1. 클라이언트가 rawToken 전송
  2. 서버: DB/Redis에서 salt 조회
  3. sha256(salt ∥ rawToken) 계산 → 키 매핑 여부 확인
  • 장점
    • DB/Redis 유출 시 원본 토큰 노출 방지
    • 토큰 회전/만료 구현 용이

5️⃣ 권장 설정 & 운영 팁

항목 권장값 / 기법
Salt 길이 ≥ 16 바이트 랜덤
Pepper 보관 KMS/Vault → 주기적 키 롤링
bcrypt Work Factor 12 이상
Argon2 메모리 비용 ≥ 64 MB
Argon2 반복 3~4회
scrypt N, r, p N=2^14, r=8, p=1 (보안 vs 성능 균형)
해시 알고리즘 SHA-256 이상, HMAC-SHA256 / SHA-3
토큰 만료 Refresh 토큰: 2-4주, Access 토큰: 15분-1시간
토큰 회전 정책 Refresh 토큰 사용 시 즉시 새 토큰 재발급 & 이전 만료

결론 및 학습 소감

해싱은 일방향성∙고정 길이∙빠른 계산이 특징이지만,
보안 관점에서는 단순히 해싱 함수만 사용하는 것으로는 부족하다.
Salt와 Pepper를 병합해 사전·레인보우 테이블을 무력화하고,
KDF로 해시 계산 비용을 인위적으로 높이며,
토큰 해싱으로 원본 노출을 방지하는 여러 방어 계층이 필요하다.
이러한 다층 방어 구조를 통해 해시 기반 인증·토큰 시스템의
강력한 보안성을 확보할 수 있다.

'CS공부 > 기타 CS지식' 카테고리의 다른 글

GitHub 대용량 파일 관리를 위한 Lens 사용법  (0) 2025.06.09
락(Lock)의 종류와 적용 전략: Optimistic vs Pessimistic  (0) 2025.05.14
SK텔레콤 유짐정보 해킹, BPFdoor 악성코드 사건 정리  (3) 2025.04.26
N+1 문제  (0) 2025.04.21
데드락(Deadlock)  (0) 2025.04.21
'CS공부/기타 CS지식' 카테고리의 다른 글
  • GitHub 대용량 파일 관리를 위한 Lens 사용법
  • 락(Lock)의 종류와 적용 전략: Optimistic vs Pessimistic
  • SK텔레콤 유짐정보 해킹, BPFdoor 악성코드 사건 정리
  • N+1 문제
quokkaST
quokkaST
  • quokkaST
    stquokka
    quokkaST
    • 개발자 (77)
      • n8n (2)
      • CS공부 (46)
        • Java & Spring (15)
        • 인프라 (7)
        • 운영체제 & 시스템 (9)
        • 기타 CS지식 (7)
        • 네트워크 (6)
        • 데이터베이스 (2)
      • 알고리즘 (16)
      • 프로젝트 (8)
        • 감정&금융챗봇 (8)
      • 리팩토링 (5)
        • horong (5)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
상단으로

티스토리툴바