Post

비밀번호 유출 방지를 위한 알고리즘 선정 및 코드


비민번호는 복호화 되지 않도록 단방향으로 암호화 되어야 한다.

비밀번호를 단방향으로 암호화하는 이유는 복호화가 불가능하기 때문에, 개인 정보가 유출되어도 원본 비밀번호를 쉽게 알아낼 수 가 없기 때문이다.

사용할 수 있는 암호화 기법을 알아보자.

암호화 기술

MDC와 MAC가 있다. 이 둘의 차이는 뭘까

Modification Detection Code

메시지 변경 감지 코드로 메시지 변경이 일어났는지를 확인하는 코드로 데이터의 무결성을 검증한다.

Message Authentication Code

메세지가 외부에 노출되어도 상관없을 때 사용할 수 있는 인증 기법으로 메시지 인증, 디지털 인증에 주로 사용된다.
메시지가 내가 원하는 사람에게 왔는지 확인하려면 어떻게 해야 할까? 키를 주면 된다.

송신자와 수진자는 미리 공유된 키를 가지고 메시지 해쉬값을 만든다. 이것이 MAC으로 이것을 메시지와 함께 보낸다.그러면 수신자는 메시지와 MAC를 받아서 키와 이용해 해시값을 만든 후 비교한다.
결과가 같으면 원하는 송신자에게 온 것으로 판단한다.





비밀번를 암호화하는 데는 해시 알고리즘이 사용된다. 해시 함수는 임의의 평문을 입력하면 해시 값을 반환하는데, 동일한 값을 입력시 동일한 값(다이제스트)을 반환하는 특징이 있다.
이런 특징으로 패스워드 저장에 활용할 수 있다.

비밀번호를 암호화하는데는 주로 아래와 같은 알고리즘이 사용된다.

암호화 알고리즘

MD5(Message-Digest algorithm 5)

  • 128비트(16바이트) 해시 값을 생성하는 암호학적 해시 함수
  • 계산 속도가 빠르고 효율적이어서 과거에는 사용되었으나 현재는 안전하지 않아서 사용되지 않는다.
  • 서로 다른 입력이 동일한 해시 값을 생성할 수 있어 충돌 가능성이 있다.
  • SSL 인증서 변조와 같은 보안 위협 존재

SHA-1

  • 160비트의 해시 값을 생성하는 암호학적 해시 함수
  • 위와 동일하게 충돌 위험이 존재하며, 실제로 공격 받은 사례 존재

SHA-2(SHA-256) -> 권장

  • 256 비트의 해시 값
  • 충돌 저항성 제공





그러면 SHA-256 단방향으로 암호화하면 비밀번호는 안전할까?
아니다. 단방향 암호화의 경우 비밀번호를 복호화 불가능하게 하여 원본 비밀번호를 보호할 수는 있지만, 완전히 안전하다고 할 수 없다.

데이터를 SHA-256을 통해 암호화 하며서 다이제스트를 얻을 수 있다. 다이제트는 암호화된 데이터를 말한다.
다이제스트로 원본 데이터를 유추하기는 어렵지만, 데이터에 대한 다이제스트는 항상 같은 값을 갖는다.
다시 말해서 특정 평문 “12345”에 대한 다이제스트는 항상 같다는 의미이다.

이런 특징을 이용해 해시 함수의 해시 값을 정리한 테이블이 존재하는데, 이것을 레인보우 테이블이라고 한다.

이와 함께 해시 함수의 특징은 빠른 검색이 가능하다는 것이다. 이러한 장점은 공격자에게도 장점이 된다. 우리가 다이제스트를 빠르게 얻을 수 있는 것과 같이 공격자도 똑같이 빠르게 다이제스트를 얻을 수 있다. 공격자는 빠른 속도로 임의의 문자열의 다이제스트와 해킹할 대상의 다이제스트를 비교할 수 있다.





그러면 어떻게…하죠?
이러한 위험을 보호하기 위해 salt와 키 스트레칭 방식이 있다.

1. 키 스트레칭

key-stretching

키 스트레칭 방식은 해시 함수를 여러 번 수행하는 방법이다.
비밀번호를 해시함수를 통해 다이제스트를 얻고, 이렇게 생성된 다이제스트를 입력값으로 하여 또 다른 다이제스트를 얻는다. 이 방법을 반복하는 것이다. 해시 함수를 여러 번 돌리기 때문에 최종 다이제스트를 얻는데 그만큼의 시간이 더 소요된다.
해시를 여러 번 반복하여 시간을 늘려 무차별 대입 공격을 방지할 수 있다.


2. salt

salt 솔트는 해시함수를 돌리기 전에 평문에 임의의 문자열을 덧붙이는 방식이다. 소금을 츄츄 뿌리는 것처럼~
이렇게 하면 다이제스트를 알아냈다고 하더라도 비밀번호를 알아내기는 어렵다. 사용자마다 다른 salt를 사용한다면 같은 비밀번호이더라도 다이제스트의 값은 달라지기 때문이다.
따라서 한 명의 패스워드가 유출되더라도 같은 패스워드를 사용하는 다른 유저는 안전할 수 있다.

기존에는 일반적인 장비로 1초에 50억 이상의 다이제스트를 비교할 수 있지만. 키 스트레칭을 적용하여 동일 장비에서 1초에 5번 정도만 비교할 수 있도록 한다. GPU(Graphics Processing Unit)을 사용하더라도 수 백 ~ 수 천번 정도의 비교만 가능하다.


SHA 알고리즘은 비교적 단순한 연산을 사용하기 때문에 CPU, GPU에서 빠르게 수행할 수 있다. 또한 해시계산을 병렬로 수행할 수 있기 때문에 GPU와 같은 병렬 처리 장치에서 빠르다. 그렇기 때문에 계산 속도가 빠르고 대량의 데이터를 처리하는데 유리하지만, 빠른 속도는 공격자에게 브루트포스 공격을 할 기회를 제공한다. 따라서 보안이 중요한 상황에서는 상대적으로 속도가 느린 알고리즘이 권장된다.






Bcrypt

Bcrypt는 Blowfish cipher에 기반을 둔 패스워드 해시 함수이다. salt 기법을 적용하여 레인보우 테이블 공격에 대비할 수 있다. 반복을 통해 임의로 프로세스를 느리게 만들어 브루트포스 공격에 저항한다.

스크린샷 2024-11-05 오후 4 04 29

  • $2a$: bcrypt 알고리즘을 나타낸다.
  • 12: cost factor (2^12의 해시 연산 수행, 4096 rounds), 숫자가 높을수록 연산이 느려지고 보안이 높아진다.
  • R9h/cIPz0gi.URNNX3kh2O: salt 값이 Base64로 인코딩 됨.
  • PST9/PgBkqquzi.Ss7KIUgO2t0jWMUW: Abcrypt가 생성한 24바이트 길이의 해시에서 처음 23바이트만 Base64로 인코딩하여 사용.

bcrypt는 해시를 Base64 인코딩하여 표현하는데, Base64로 인코딩된 24바이트는 32자이지만, 23바이트만 인코딩하여 31자로 유지한다. 이를 통해 bcrypt의 전체 해시 포맷 (총 60자)를 일관되게 유지하도록 한다.


구현

Spring Security에서는 BCryptPasswordEncoder 클래스를 제공한다. BCryptPasswordEncoder 클래스에는 암호화 기능 및 입력한 비밀번호와 저장된 비밀번호의 일치여부를 확인하는 메서드를 제공한다.

Bcrypt example

BCryptPasswordEncoder의 encode 메서드를 사용하면, 위와 같이 암호화된 값이 들어가는 것을 볼 수 있다.


소스 코드

스크린샷 2024-11-05 오후 4 27 45 Bcrypt 알고리즘을 사용하여 비밀번호를 해시하는 메서드로 salt를 생성한 후, Bcrypt 알고리즘을 사용해 비밀번호와 salt를 결합해 해시된 비밀번호를 생성한다.


스크린샷 2024-11-05 오후 5 08 52 strength가 cost factor(라운드 힛수)인데 따로 정의하지 않으면 기본으로 10으로 설정한다.


스크린샷 2024-11-05 오후 4 28 39 salt를 생성하는 메서드로 주어진 접두사, 로그 라운드 수, 그리고 보안 랜덤 생성기를 사용하여 salt 값을 한다.

스크린샷 2024-11-05 오후 4 33 34 스크린샷 2024-11-05 오후 4 33 48

주어진 비밀번호와 salt를 사용하여 Bcrypt 해시를 생성하는 메서드이다.





https://en.wikipedia.org/wiki/Bcrypt

This post is licensed under CC BY 4.0 by the author.