[웹 공부] 웹 보안 비밀번호 해싱(Hashing)

lheun·2022년 8월 30일
0

웹 공부

목록 보기
1/4
post-thumbnail

사용자의 비밀번호를 그대로 DB에 저장하면 엄청난 보안 취약점이 된다 (하면 안된다)

🚩 Hash

문자열을 되돌릴 수 없는 방식으로 암호화하여, hash 출력값을 이용해 사용자의 비밀번호를 알아낼 수 없다 (= 복호화가 불가능하다 )

단방향 암호화 방법으로 비밀번호를 잊어버렸을 때, 다시 알려주지 않고 비밀번호 재설정을 하게 되는 이유이다

로그인 시 전달된 비밀번호의 Hash 값DB에 저장된 값을 비교하여 로그인을 처리한다

✅ Hashing 방법

const hash = crypto.createHash('sha1'); //사용할 hash 알고리즘 설정
hash.update(password); //변환할 문자열_password를 'sha1'알고리즘으로 해싱
hash.digest("hex"); //인코딩할 알고리즘 입력 -> 변환된 문자열을 반환한다
  • Node.js의 기본제공 모듈인 crypto모듈을 사용해 hash값을 얻을 수 있다
    sha1 알고리즘 외에도 더 강력한 sha224, sha256 등의 알고리즘이 있다

  • hasing이 완료된 암호화된 데이터를 다이제스트(digest)라고 한다

    	

🚩 Hash 함수의 취약점

동일한 비밀번호에 대해 동일한 값을 갖는다
https://www.convertstring.com/ko/Hash/SHA256

  • qwerty1234 라는 문장에 대해 계속 해서 생성을 해도 동일한 값(digest)만이 출력된다

  • 해시 함수에 대한 입력값-출력값을 저장해 놓는 레인보우 테이블(Rainbow Table)을 통해 역으로 hashing 이전의 비밀번호를 알아낼 수도 있다 (무차별 대입 공격도 가능하다)

         

🚩 Hash 함수의 취약점 보완하기

레인보우 테이블(Rainbow Table)을 만들기 어렵게 하자

✅ 1. Salt

Hashing시에 Salt를 뿌려 비밀번호를 복호화 하는 것을 방해하는 방법

사용자의 기본 비밀번호에 추가 문자열(Salt)를 더해 Hashing하는 방식이다

  • 동일한 비밀번호를 가진 2명의 사용자끼리도 추가된 문자열(Salt)로 인해 다른 Hash값을 갖게 된다

  • 다른 한 명의 비밀번호가 유출되더라도 다른 사용자는 안전할 수 있다

    ex) 사용자A, B의 비밀번호 : 1234
    동일한 비밀번호에 대해 DB에 저장되는 digest를 비교해보면

  1. 사용자 A : 1234 + a_users_salt_random_dsjfkhsd
  2. 사용자 B : 1234 + b_users_salt_random_fdsjfsfkj

    동일한 비밀번호라도 salt값이 다르기 때문에 더 안전하게 될 수 있다

Salt값의 경우

  • 각 사용자 별로 고유의 값을 암호화 최초에 설정되도록 하고 로그인 시를 위해 해당 salt값을 잘 갖고 있어야 한다 (DB저장)

  • Salt의 길이가 32byte 이상이어야 Salt와 digest를 추측하기 어렵다


✅ 2. Key Stretching

비밀번호를 Hashing하는 과정을 반복하는 것이다

  • 비밀번호 + (Salt) → Hashing → Digest → 비밀번호 + (Salt) → Hashing → Digest (반복)
  • 비밀번호를 알아내기 위해 많은 시간이 소요되게 된다

🚩 암호화 방법

⏹ Adaptive Key Derivation Functions

adaptive key derivation function은 다이제스트를 생성할 때 솔팅과 키 스트레칭을 반복하며
솔트와 패스워드 외에도 입력 값을 추가하여 공격자가 쉽게 다이제스트를 유추할 수 없도록 하고
보안의 강도를 선택할 수 있다

adaptive key derivation function 중 주요한 key derivation function은 다음과 같다

  • pbkdf, scrypt, bcrypt

즉, 안전한 패스워드 저장을 위한 암호화 방법으로는 위의 세가지인 pbkdf, scrypt, bcrypt가 있다는 의미이다

✅ 1. PBKDF2

  • 가장 많이 사용되는 함수(key derivation function) 이다
  • 해시 함수의 컨테이너인 PBKDF2는 솔트를 적용한 후 해시 함수의 반복 횟수를 임의로 선택할 수 있다
  • 아주 가볍고 구현하기 쉬우며, SHA와 같이 검증된 해시 함수만을 사용한다
  • NIST(National Institute of Standards and Technology, 미국표준기술연구소)에 의해서 승인된 알고리즘이다
DIGEST = PBKDF2(PRF, Password, Salt, c, DLen)

// PRF: 난수(예: HMAC)_해시함수(sha256..)
// Password: 패스워드
// Salt: 암호학 솔트
// c: 원하는 iteration 반복 수
// DLen: 원하는 다이제스트 길이

✅ 2. Bcrypt

  • 1999년 발표했고 현재까지 사용되는 가장 강력한 해시 메커니즘 중 하나이다
  • bcrypt는 보안에 집착하기로 유명한 OpenBSD에서 기본 암호 인증 메커니즘으로 사용되고 있다
  • bcrypt에서 "work factor" 인자는 하나의 해시 다이제스트를 생성하는 데 얼마만큼의 처리 과정을 수행할지 결정한다
    • "work factor"를 조정하는 것만으로 간단하게 시스템의 보안성을 높일 수 있다
    • 반복횟수를 늘려 연산속도를 늦출 수 있으므로 연산 능력이 증가하더라도 brute-forece 공격에 대비할 수 있다
  • .NET 및 Java를 포함한 많은 플랫폼,언어에서 사용할 수 있다
  • 다만 PBKDF2나 scrypt와는 달리 bcrypt는 입력 값으로 72 bytes character를 사용해야 하는 제약이 있다
bcrypt.hashpw(password, bcrypt.gensalt())

✅ 3. Scrypt (어렵다,,)

  • pbkdf 방식 보다는 scrypt방식이 더 안전하다고 하다
  • 하지만 알고리즘이 복잡한 만큼 해시와 이터레이션에 사용되는 시간이 더 오래 걸려, 오히려 해커가 브루트 포스같은 공격을 해대면 뚫리지는 않을지라도 서버에 엄청난 부하가 가해지는 역효과가 발생 할 수도 있다
DIGEST = scrypt(Password, Salt, N, r, p, DLen)

// Password: 패스워드
// Salt: 암호학 솔트
// N: CPU 비용
// r: 메모리 비용
// p: 병렬화(parallelization)
// DLen: 원하는 다이제스트 길이

⭐ 최종 정리

MD5, SHA-1, SHA-256, SHA-512 등의 해시 함수는 메시지 인증과 무결성 체크를 위한 것이다

  • 이것을 패스워드 인증을 위해 사용하면 (인식 가능성과 빠른 처리 속도에 기인하는) 취약점이 존재한다

  • 이를 해결하기 위해서는 위에서 언급한 key derivation function을 사용하는 것을 권장한다

  1. PBKDF2
    • ISO-27001의 보안 규정을 준수하고, 서드파티의 라이브러리에 의존하지 않으면서 사용자 패스워드의 다이제스트를 생성하려면 PBKDF2-HMAC-SHA-256/SHA-512을 사용하면 된다
  1. Bcrypt
    • 매우 강력한 패스워드 다이제스트를 생성하는 시스템을 쉽게 구현하고 싶다면 Bcrypt를 사용하는 것이 좋다. 대부분의 프로그래밍 언어에서 라이브러리를 사용할 수 있다
  1. Scrypt
    • 구현하려는 시스템이 매우 민감한 정보를 다루고, 보안 시스템을 구현하는 데 많은 비용을 투자할 수 있다면 scrypt를 사용하면 된다

👏Reference

[NODE] 📚 crypto 모듈 (단방향 / 양방향) 암호화 원리 & 사용법
[NODE] 📚 bcrypt 모듈 암호화 원리 & 사용법
Naver D2_안전한 패스워드 저장
안전한 패스워드 만드는 방식
안전한 패스워드 보안(패스워드 암호화 저장법 /bcrypt, scrypt, pdkdf2 )
패스워드의 암호화와 저장 - Hash(해시)와 Salt(솔트)
Node.js :: Bcrypt로 비밀번호 해싱(Hashing)하기

profile
🛫

0개의 댓글