Exp/보안

[보안] 단방향 해시 함수 PBKDF

kilog 2024. 4. 19. 01:59
728x90

안녕하세요 ki입니다.

이번 Exp는 암호화알고리즘 중 단방향 해시 함수입니다.

클라우드 솔루션 개발에 투입했을 때 비전공자인 저는 개발 지식 부족하여 암호 알고리즘에 대해 알지 못했습니다.

( 지금은 기사실기 준비를 하면서 알게 된 부분입니다 …ㅜ)

투입했던 클라우드 솔루션 기업은 해시함수 알고리즘인 SHA-256를 이용하였고 암호 알고리즘 개념을 몰랐던

저는 단방향 해시 함수부터 시작하여 PBKDF 등 알게 된 부분을 공유하려고 합니다.

 

 

단방향 해시 함수(One-way Hash Function)

 

단방향 해시 함수(One-way Hash Function)는 주어진 입력 데이터를 고정된 크기의 고정된 출력 값으로 변환하는 함수입니다. 이 함수는 입력 값에 대해 해시 값(해시 코드)을 생성하며, 이해할 수 없는 형태로 변환됩니다. 단방향 해시 함수의 특성은 아래와 같습니다.

  1. 단방향성(One-way): 입력 값을 해시 값으로 변환하는 것은 쉽지만, 해시 값을 다시 원래 입력 값으로 역으로 변환하는 것은 매우 어렵거나 거의 불가능. 단방향 함수라고 불리는 이유이다.
  2. 고정된 출력 크기: 입력 값의 상관없이 함수는 항상 일정한 크기의 출력을 생성. 예로 SHA-256 해시 함수는 항상 256비트(32바이트) 크기의 해시 값을 생성함
  3. 고유성(Uniqueness): 해시 값이 생성되는 충돌(Collision)이 최소화. 이는 해시 함수가 가능한 한 입력의 각 비트에 대해 출력의 각 비트에 영향을 주기 때문에 보장함
  4. 결정론적(Deterministic): 동일한 입력 값에 대해 항상 동일한 해시 값이 생성됨. 함수가 일관된 결과를 제공하고, 동일한 입력이 주어지면 항상 동일한 출력이 생성된다는 것을 의미
  5. 빠른 계산 속도: 대부분의 단방향 해시 함수는 입력 값에 대해 빠르게 계산되며, 거의 모든 애플리케이션에서 사용 가능

 

대표적인 단방향 해시 함수

 

대표적으로 MD5(Message Digest Algorithm 5), SHA-1(Secure Hash Algorithm 1), SHA-256, SHA-512 등이 있습니다.

이러한 해시 함수들은 주로 데이터 무결성 검증, 비밀번호 저장, 디지털 서명, 메시지 인증 등 다양한 보안 관련 작업에 사용됩니다. 단방향 해시 함수는 입력 값을 안전하게 보호하고 데이터의 무결성을 보장하는 데 중요한 역할을 합니다.

 

 

단방향 해시 함수로  불리는 이유

 

단방향 해시 함수는 수학적인 연산을 통해 원본 메시지를 변환하여 암호화된 메시지인 다이제스트를 생성합니다.

원본 메시지를 알면 암호화된 메시지를 구하기는 쉽지만 암호화된 메시지로는 원본 메시지를 구할 수 없어야 하며 이를 '단방향성'이라고 합니다.

예를 들어 사용자의 패스워드가 "hunter2"라면 이 문자열을 흔히 사용하는 해시 알고리즘인 SHA-256으로 인코딩하여 아래와 같은 값을 얻을 수 있습니다.

 

f52fbd32b2b3b86ff88ef6c490628285f482af15ddcb29541f94bcf526a3f6c7

 

 

대부분의 해시 함수는 입력 값의 일부가 변경되었을 때 다이제스트가 완전히 달라지도록 설계되어 있습니다.

"hunter3"라는 값의 SHA-256 다이제스트는 아래와 같으며 위의 "hunter2"와는 완전히 달라진 것을 확인할 수 있습니다.

 

fb8c2e2b85ca81eb4350199faddd983cb26af3064614e737ea9f479621cfa57a

 

이 특징을 avalanche 효과라고 하며, 사용자의 원본 패스워드를 추론하기 어렵게 만드는 중요한 요소입니다.

그러나 이것만으로는 패스워드 보안이 충분히 안전하다고 말할 수 없습니다.

아래는 충분히 안전하지 않는 단방향 해시 함수에 문제점은 아래와 같습니다.

 

 

 

단방향 해시 함수의 문제점

  1. 충돌(Collision): 두 개의 서로 다른 입력이 동일한 해시 값을 생성하는 충돌이 발생할 수 있습니다. 충돌이 발생하면 해시 함수의 안전성이 손상되고, 보안이 위협받을 수 있습니다.
  2. 레인보우 테이블 공격(Rainbow Table Attack): 해시 함수의 의해 동일한 메시지가 언제나 동일한 다이제스트를 갖는다면 값이 일정하므로, 공격자는 미리 계산된 해시 값과 일치하는 원본 값을 찾는 데 사용할 수 있는 레인보우 테이블을 생성할 수 있습니다. 이를 통해 공격자는 해시된 비밀번호를 식별하고 원본 값을 추측할 수 있습니다.
  3. 솔트(Salt) 필요성: 동일한 입력에 대해 항상 동일한 해시 값을 생성하는 해시 함수는 비밀번호와 같은 비슷한 입력 값들에 취약합니다. 솔트를 사용하여 해시 값에 추가적인 무작위성을 적용함으로써 이러한 취약점을 해결할 수 있습니다.
  4. 빠른 해시 함수 공격: 해시 함수는 빠르게 계산되는 경향이 있습니다. 그래서 패스워드가 충분히 길거나 복잡하지 않은 경우에는 그리 긴 시간이 걸리지 않는다. 그리고 대부분 사용자의 패스워드는 길거나 복잡하지 않을 뿐 아니라, 동일한 패스워드를 사용하는 경우도 많다는 것을 추측하여 공격자는 대규모의 입력 집합에 대해 빠르게 다양한 입력을 시도할 수 있습니다. 이러한 공격은 빠른 해시 함수의 특성 때문에 더욱 강력해집니다.
  5. 약한 해시 함수 사용: 일부 해시 함수는 보안 취약점이 있을 수 있으며, 암호학적으로 안전하지 않음. 따라서 보안 요구 사항에 맞는 강력한 해시 함수를 선택

위에 문제점들을 고려하여 보안 애플리케이션에서는 충돌 저항성이 높고 안전한 솔트 및 강력한 해시 함수를 사용하여 데이터 보안을 강화해야 합니다.

 

저는 이 개념을 모르고 암호화가 된다면 복호화가 된다고 생각하여 수석님께 암호화 알고리즘 바꿔야 되는 거 아니냐는 망언과 비슷한 질문을 드린 경험이 있습니다… 아직도 후회 중입니다.

(후에 Salt로 비밀번호가 True인지 False 인지 확인하여 로그인하는 걸 알게 됐습니다….)

 

 

 

 

 

단방향 해시 함수의 보완방법

솔팅(salting)

솔트(salt)는 단방향 해시 함수에서 다이제스트를 생성할 때 추가되는 바이트 단위의 임의의 문자열이다. 그리고 이 원본 메시지에 문자열을 추가하여 다이제스를 생성하는 것을 솔팅(salting)이라 합니다.

각각의 비밀번호에 무작위 솔트 값을 추가하여 해시 결과를 생성(다이제스)하는 것입니다. 이를 통해 동일한 비밀번호라도 각각 다른 해시 값이 생성되어 레인보우 테이블 공격을 방지할 수 있습니다.

예로 다음과 같이 "redfl0wer"에 솔트인 "8zff4fgflgfd93fgdl4fgdgf4mlf45p1"를 추가해 다이제스트를 생성할 수 있습니다.

패스워드 "redfl0wer"에 솔트를 추가해 다이제스트 생성

 

이 방법을 사용하면, 공격자가 "redfl0wer"의 다이제스트를 알아내더라도 솔팅된 다이제스트를 대상으로 패스워드 일치 여부를 확인하기 어렵습니다.. 또한 사용자별로 다른 솔트를 사용한다면 동일한 패스워드를 사용하는 사용자의 다이제스트가 다르게 생성되어 인식 가능성 문제가 크게 개선됩니다.

 

솔트와 패스워드의 다이제스트를 데이터베이스에 저장하고, 사용자가 로그인할 때 입력한 패스워드를 해시하여 일치 여부를 확인할 수 있습니다. 이 방법을 사용할 때에는 모든 패스워드가 고유의 솔트를 갖고 솔트의 길이는 32바이트 이상이어야 솔트와 다이제스트를 추측하기 어렵습니다.

(저도 API를 제공받아 패스워드가 일치하는지 여부를 체크하여 로그인 기능을 구현했습니다. 솔팅한 문자열을 API입력하고 일치하면  로그인되는 방식이었습니다.)

 

 

 

 

 

키 스트레칭(key stretching)

입력한 패스워드의 다이제스트를 생성하고, 생성된 다이제스트를 입력 값으로 하여 다이제스트를 생성하고, 또 이를 반복하는 방법으로 다이제스트를 생성할 수도 있습니다. 이렇게 하면 입력한 패스워드를 동일한 횟수만큼 해시해야만 입력한 패스워드의 일치 여부를 확인할 수 있다. 이것이 기본적인 키 스트레칭 과정입니다. 이를 통해 해시 함수를 실행하는 데 필요한 시간을 늘리고, 공격자가 빠른 해시 함수 공격을 어렵게 만듭니다.

 

잘 설계된 패스워드 저장 시스템에서는 하나의 다이제스트를 생성할 때 어느 정도(일반적인 장비에서 0.2초 이상)의 시간이 소요되게 설정합니다. 이는 억지 기법 공격(brute-force attack)으로 패스워드를 추측하는 데 많은 시간이 소요되도록 하기 위한 것이다. 다음 그림은 솔트를 추가한 패스워드에 여러 단계의 해시 함수를 적용하여 다이제스트를 생성하는 과정을 나타낸 것이다.

그림 2 솔팅과 키 스트레칭을 적용하여 다이제스트 생성

 

 

앞에서 설명한 바와 같이 솔팅과 키 스트레칭으로 구성된 암호화 시스템을 구현하려고 한다면 이미 검증된 암호화 시스템을 사용할 것을 권장합니다. 널리 알려진 검증된 시스템을 사용하면, 암호화 시스템을 잘못 구현해서 발생하는 위험을 피할 수 있습니다.

 

PBKDF

 

PBKDF는 Passsword-Based Key Derivation Function의 약자로 비밀번호 기반 키 도출 기능이라고 직역하고 비밀번호를 기반으로 키를 파생시키는 함수라고 말할 수 있습니다. PBKDF는 사용자의 비밀번호를 입력으로 받아 이를 해시 함수에 적용하여 안전한 키를 생성합니다. 이렇게 생성된 키는 주로 데이터 암호화나 인증 프로토콜에서 사용됩니다.

 PBKDF는 PBKDF1과 PBKDF2 두 가지 주요 버전으로 나뉩니다. PBKDF2는 가장 보편적으로 사용되며, HMAC을 사용하여 안전한 키 파생을 제공합니다.

 

PBKDF2

 

PBKDF2(Password-Based Key Derivation Function 2)는 암호 기반 키 파생 함수로, 주로 암호화된 비밀번호를 보호하는 데 사용됩니다. PBKDF2는 해시 함수를 기반으로 하며, 입력된 비밀번호와 솔트(salt)를 조합하여 안전한 키를 생성합니다. 이 과정을 여러 번 반복하여 보안 강도를 높이는 것이 특징입니다.

PBKDF2는 암호학적으로 안전한 비밀번호 보호를 위해 주로 사용되며, 주로 데이터베이스나 웹 서비스에서 사용자 비밀번호를 저장할 때 사용됩니다. PBKDF2는 암호화된 비밀번호를 해독하기 위한 공격에 대비하여 안전성을 높이는 데 효과적입니다.

 

 

 

PBKDF와 PBKDF2의 차이점

 

 

PBKDF1과 PBKDF2는 모두 비밀번호 기반 키 파생 함수이지만, 주요한 차이점이 있습니다.

  1. 해시 함수 사용:
    • PBKDF1은 단방향 해시 함수로는 MD2, MD5, SHA-1 등을 사용합니다.
    • PBKDF2는 HMAC(Hierarchical Key Derivation Function for Message Authentication Code)을 사용하여 안전한 해시 함수를 사용합니다. HMAC은 MD5, SHA-1, SHA-256, SHA-512 등을 사용할 수 있습니다.
  2. 반복 횟수 설정:
    • PBKDF1은 반복 횟수를 설정할 수 없습니다.
    • PBKDF2는 반복 횟수를 조절할 수 있습니다. 이는 보안 강도를 높이기 위해 반복 횟수를 늘릴 수 있습니다.
  3. 보안 강도:
    • PBKDF1은 보안 강도가 낮아 더 이상 권장되지 않습니다. 이유는 해시 함수의 취약점과 반복 횟수를 조절할 수 없는 점 등이 있습니다.
    • PBKDF2는 HMAC을 사용하여 안전한 해시 함수를 사용하고 반복 횟수를 조절할 수 있기 때문에 보안 강도가 더 높습니다.

PBKDF2는 PBKDF1의 약점을 보완하고 보안 강도를 높이기 위해 개발되었으며, 일반적으로 더 안전하고 권장되는 방법입니다. PBKDF1은 오래된 표준이며 보안에 취약할 수 있으므로 사용을 피하는 것이 좋습니다.

 

 

 

 

PBKDF2보다 발전된 PBKDF

 

시간이 지나며 기술이 발전하듯이 bcrypt와 scrypt라는 PBKDF2보다 더 발전된 비밀번호 기반 키 파생 함수(PBKDF)가 등장했습니다. 

 

bcrypt

bcrypt는 패스워드 저장을 목적으로 설계되었다. Niels Provos와 David Mazières가 1999년 발표했고 현재까지 사용되는 가장 강력한 해시 메커니즘 중 하나입니다. bcrypt는 보안에 집착하기로 유명한 OpenBSD에서 기본 암호 인증 메커니즘으로 사용되고 있고 미래에 PBKDF2보다 더 경쟁력이 있다고 여겨집니다.

 

scrypt

 

scrypt는 PBKDF2와 유사한 adaptive key derivation function이며 Colin Percival이 2012년 9월 17일 설계했습니다..

scrypt는 다이제스트를 생성할 때 메모리 오버헤드를 갖도록 설계되어, 억지 기법 공격(brute-force attack)을 시도할 때 병렬화 처리가 매우 어렵습니다. 따라서 PBKDF2보다 안전하다고 평가되며 미래에 bcrypt에 비해 더 경쟁력이 있다고 여겨집니다. scrypt는 보안에 아주 민감한 사용자들을 위한 백업 솔루션을 제공하는 Tarsnap에서도 사용하고 있습니다.. 또한 scrypt는 여러 프로그래밍 언어의 라이브러리로 제공받을 수 있습니다.

 

 

 

 

PBKDF2보다 좋은점?

 

 

PBKDF2는 많은 장점을 가지고 있지만, bcrypt와 scrypt는 일반적으로 보안적으로 더 강력하고 공격에 대해 더 안전한 알고리즘으로 평가됩니다.

  1. 비밀번호 보안 강화: bcrypt와 scrypt는 PBKDF2보다 비밀번호 보안을 더욱 강화합니다. 이들은 키 파생을 위해 CPU나 메모리 등을 사용하여 암호화 강도를 높이는 과정을 포함
  2. 고급 하드웨어 공격에 대한 보안성: bcrypt와 scrypt는 고급 하드웨어 공격에 대해 더 강력한 방어 기능을 제공합니다. 특히 scrypt는 대규모 메모리 사용에 매우 강력한 알고리즘
  3. 최신 기술 및 보안 취약성 대응: bcrypt와 scrypt는 PBKDF2보다 더 최근에 개발되었으며, 보다 최신의 기술과 보안 취약성에 대응하여 설계

위에 특징을 생각하여 보안에 중점을 두는 시스템에서는 PBKDF2 대신 bcrypt 또는 scrypt를 사용하는 것이 일반적으로 더 바람직할 수 있습니다.

 

 

 

차이점은?

두 알고리즘은 사용하는 환경과 요구 사항에 따라 장단점을 살펴보고 상황에 맞게 선택해야 합니다.

 

bcrypt:

  • 단순하고 대중화된 알고리즘입니다.
  • 비용 요소를 조정하여 보안 수준을 조절
  • 일반적으로 많은 애플리케이션에서 사용되며, 잘 알려져 있고 검증되어 있습니다.

scrypt:

  • 메모리 집약적인 알고리즘으로, 고급 하드웨어 공격에 강력
  • 많은 메모리를 요구하기 때문에, 대규모 서버에서는 성능에 영향줄 가능성 있음
  • 더 안정적이고 강력한 암호화를 원하는 경우에 유용

 

이러한 차이로 인해 bcrypt와 scrypt 중 어떤 것을 선택할지는 사용 사례에 따라 다를 수 있습니다. 보안이 중요한 시스템에서는 보다 안전한 옵션으로 scrypt를 사용하는 것이 좋을 수 있습니다. 그러나 bcrypt는 더 간단한 구현과 낮은 리소스 요구 사항으로 인해 여전히 많은 사용자들에게 인기가 있습니다.

 

 

 

제가 경험한 부분은 SHA-256이었지만 PBKDF2에 대하여 공부하기도 하였고 블로그를 작성하면서 bcrypt와 scrypt를 알게 됐습니다. 점차 발전하는 만큼 다양한 시스템공격과 그것을 막는 보안기술도 발전하니 항상 관심을 갖고 공부를 해야겠다고 다짐하게 됐습니다.

 

**저는 암호화 알고리즘을 이해하기 위해 암호이용활성화 사이트를 이용하여  공부를 했습니다.

매뉴얼을 함께 제공하여 개발하기 전 참고하고 이용하는데 도움이 됐습니다.

 

https://seed.kisa.or.kr/kisa/reference/EgovSource.do

 

 

 

참조