해시 - 솔트치기
이번 카카오페이 사건을 통해 대충만 알고있던 솔트에 대해 정리하는 시간을 가져봤다.
암호화를 했는데 왜 문제인가?
카카오페이에서 외부 api(알리페이)와 통신할 때 사용자의 개인정보 값에 대한 암호화 처리를 제대로 하지 않았다.
보도자료의 랜덤값 없이 단순하게 해시처리(암호화)
라는 말은 암호화 작업을 하긴 했지만 암호화 작업의 기본인 랜덤값(솔트)을 넣지 않았다고 하는 것인데, 이 작업을 진행하지 않고 단순 해시처리를 한다면 레인보우 테이블 공격
에 취약하다.
이번 주제에서 설명하는 해시(암호화)는 단방향 암호화인 MD5, SHA256 등의 암호화 즉 디코딩 할 수 없는 값을 기준으로 설명 할 것이다.
암호화 테스트 링크 : https://emn178.github.io/online-tools/sha256.html
해시
우리가 hello
를 회원가입 할 때 입력받아 암호화 하여 데이터베이스에 저장한다고 가정해보자.
회원가입 할 때 받은 비밀번호를 단순 해시화 하여 값을 저장했을 때
SHA256(hello)
→ 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
의 값으로 단방향 해시되어 데이터베이스에는 암호화 된 값을 저장할 것이다. 그래야 데이터베이스를 탈취당해도 유저의 실제 암호는 알 수 없기 때문인데
사실은 값을 찾아낼 수 있다.
레인보우 테이블
복호화를 하지 못하는데 값을 어떻게 찾을 수 있는지 의문이 들지만 해시된 값이 그저 단순 문자열로 이루어진 고정 값이라면 레인보우테이블 기법을 통해 해시된 값을 알아낼 수 있다.
레인보우 테이블이란 기존 문자들을 미리 암호화 처리하여 사전처럼 해시값 기준으로 원래의 문자열을 알아낼 수 있는 형태의 테이블이다.
레인보우 테이블이란 암호화된 해시값을 키로 두고, 키에 대한 원조 값을 찾아낼 수 있는 값을 value로 두는 사전이라고 볼 수 있는데 예시 테이블을 참고해서 보자면
key | value |
---|---|
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 | hello |
8f434346648f6b96df89dda901c5176b10a6d83961dd3c1ac88b59b2dc327aa4 | hi |
5d5b09f6dcb2d53a5fffc60c4ac0d55fabdf556069d6631545f42aa6e3500f2e | sha256 |
3ebff31b62c0637c54d4ffa990d5c100ea359994b35f4b342ff49797542148cd | md5 |
만약 이러한 테이블을 저장한 레인보우 테이블이 있을 때 해커는 hello의 값을 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
해시 검색을 통해 알아낼 수 있을 것이다.
코드 예시
import java.util.*;
public class main
{
public static void main(String[] args)
{
// 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824의 값이 저장됨
String passwordSave = sha256("hello");
//만들어져 있는 레인보우 테이블이라고 가정
Map<String, String> rainbowTable = new HashMap<>();
rainbowTable.put(sha256("hello"), "hello");
//얻은 해시값
System.out.println(rainbowTable.get("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"));
}
public static String sha256(String needEncodingString){
//해싱처리
//hello -> sha256 -> 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
return "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824";
}
}
---------
출력 값 : hello
솔트 치기
솔트를 치는 이유는 보안 강화이며, 단순한 해시화는 앞에서 보았던 것 처럼 쉽게 예측이 가능한 것을 보았다.
해시작업을 할 때 기존의 문자열만으로 해시하는 것이 아니라 기존 문자열과 함께 서버에서만 알 수 있는 문자열을 부여해서 두 문자를 합친 해시값을 만드는 것이다.
예를 들면 sha256(hello + "qwerafaad" )
와 같이 암호와 관련이 없는 문자열을 해시하기 전 추가해서 전혀 다른 해시값을 만드는 것이다. 이 때 암호와 관련이 없는 문자열을 솔트(소금)이라고 부른다.
또 솔트를 칠 때 주의해야 할 점이 있는데 이에 대해 알아보자
솔트값
소금 값은 짧지 않은 값으로 생성되어야 한다.
평범한 비밀번호로 구성되어있는 룩업 테이블(레인보우 테이블) 등의 경우 837G 만으로 전체 룩업 테이블을 구성할 수 있다. 이 837GB에서 “asd” 라는 문자가 추가되는 테이블을 만들었다고 했을 때 해커 입장에서는 부담스럽지 않은 값으로 해당 테이블의 조합을 만들어 낼 수 있을 것이다.
때문에 소금 값은 보통 해시 알고리즘을 사용해서 완성되는 해시길이로 생성한다.
ex ) SHA256 알고리즘은 32바이트 이기에 소금 값도 랜덤한 32바이트
유저별로 랜덤한 문자열을 생성
랜덤한 문자열을 만들어놓고 그 값을 그대로 재사용 해서는 안된다. 소금 값의 의미가 없어지는데, 예를 들어 하나의 유저의 비밀번호가 뚫렸다고 생각해보자
- “hello”(비밀번호) + “adad”(소금)
해커는 hello라는 비밀번호에 대해 adad의 소금 값을 알아냈다.
- 모든 유저의 비밀번호를 하나의 소금값으로 사용
- 비밀번호가 유출된 유저가 비밀번호 재설정을 했을 때
위의 두가지 상황에서 해커는 소금값을 알기 때문에 쉽게 찾아낼 수 있을 것이다.
위와 같은 이유로 소금을 칠 때는 반드시 두가지 규칙(짧지 않은 소금 값, 재사용 금지)을 따라야 한다.
정리하면 다음과 같다.
- 소금값(Salt) 생성
- 사용자 계정을 생성하거나 비밀번호를 변경할 때, 새로운 임의의 랜덤 소금값을 생성합니다. 이 소금값은 절대 재사용하지 않으며, 충분히 길고 다양한 값을 가지도록 해야 한다. 일반적으로 소금값의 길이는 해시 함수의 출력 길이와 동일하게 해야한다.
- 비밀번호 해싱
- 사용자가 입력한 비밀번호에 생성된 소금값을 추가(일반적으로 결합)한 후, 그 결합된 값을 해시 함수에 전달하여 해시된 비밀번호를 생성한다.
- 저장
- 생성된 소금값과 해시된 비밀번호를 사용자 계정 테이블에 저장합니다. 이때 소금값은 별도로 저장되며, 해시된 비밀번호와 함께 보관.
참조
https://starplatina.tistory.com/entry/비밀번호-해시에-소금치기-바르게-쓰기
레인보우 테이블 : https://en.wikipedia.org/wiki/Rainbow_table
'공부 > CS' 카테고리의 다른 글
언어별 연산 속도 (0) | 2024.12.30 |
---|---|
Context Switching(문맥 교환) (0) | 2024.09.19 |
CGI, FastCGI (1) | 2024.07.23 |
Web Server (0) | 2024.07.22 |
HTTP통신 과정 (0) | 2024.07.01 |