반응형

 

해시 - 솔트치기

이번 카카오페이 사건을 통해 대충만 알고있던 솔트에 대해 정리하는 시간을 가져봤다.

암호화를 했는데 왜 문제인가?

보도자료(상세) | 보도자료 | 보도·알림 |

카카오페이에서 외부 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

+ Recent posts