쏠트

From Hidden Wiki
Jump to navigation Jump to search
필독 사항 유닠스 계열 저작물, 성인물, 도박 웹 써버 보안 프로그래밍 그래핔 파싱
필독 사항 고스트BSD 표면 웹 싸이트 제작 리눅스 마스터 파이썬 트킨터 뷰티펄 숲
수학 아이투피 마약, 아청물, 해킹 웹 싸이트 보안 웹 프로그래밍 데이터 분석 게임 제작
통계학 뮤와이어 다크넽 싸이트 제작 정보 보안 기사 쟁고우 팬더즈 파이게임

개요

소금(salt) 또는 쏠트암호학(cryptography)에서 해시 함수(hash function)로 해시 값(hash value)을 만들 때 원래의 비밀번호에 첨가하는 임의의 문자열이나 숫자이다. 즉, 일방향함수(주로 해시 함수)에서 비밀번호에 덧붙여져서 무작위 대입 공격(brute-force attack)이나 레인보우 테이블(rainbow table)로 원래의 비밀번호를 유추하기 어렵게 만들기 위해 집어넣는 무작위 데이터이다. 해커들은 자주 사용되는 비밀번호에 대해 사전(dictionary)을 만들어두고, 이 사전의 값들을 해시 함수에 넣어 만든 해시 값들의 목록인 레인보우 테이블(rainbow table)을 가지고 있다. 비밀번호에 소금을 첨가한 상태에서 해시 함수를 돌리면 전혀 다른 해시 값이 나오기 때문에 기존의 무지개 표를 무력화시킬 수 있다. 따라서 사이트 운영자는 해커가 웹 사이트해킹하여 비밀번호 데이터베이스를 빼낼 경우에 대비하여 모든 비밀번호를 해시 값으로 만들어 보관하고 절대 비밀번호 원문을 보관하지 말아야 할 뿐 아니라 비밀번호를 해시 함수에 넣기 전에 반드시 소금을 첨가해야 한다.


비밀번호를 입력할 때마다 임의의 솔트를 매번 새로 생성하여 사용자마다 다른 소금을 첨가한다. 그렇지 않으면 해커가 소금 값을 알아낼 수도 있어서 소금이 무력화될 수도 있다. 또한 매번 같은 소금을 사용하면 같은 비밀번호를 사용하는 사람은 같은 해시 값을 갖기 때문에 위험하다. 동일인이 비밀번호를 변경할 경우에도 이전과는 다른 소금을 첨가한다.


salt는 암호 처리 기법이다. 유래는 소금(salt). 소금이 기본 양념이듯 원문에 가미하여 암호문을 다른 값으로 만드는 것이다. 이것이 가장 많이 쓰이는 곳은 일정한 결과값을 내며, 원문 복원이 불가능한 해시 함수. 유사하게 후추(pepper)라는 것도 있다. http://en.wikipedia.org/wiki/Pepper_(cryptography)

이것을 쓰지 않으면 어떠한 암호화 해시로도 탈탈 털릴 여지가 생긴다. 원문으로 복원이 불가능하기에 해시값을 비교하는 식으로 검증을 하며, 어지간한 해시 함수알고리즘을 공개하기 때문. 이를 악용한 것이 사전 공격의 일종인 레인보우 테이블 공격 [[1]]. 사전 공격에서 쓰이는 문자열을 해당 해시값으로 변환해놓은 것으로 이해하면 된다.

고로 이를 방지하기 위해 원문을 약간 변조하는 것이 솔트인데, 문자열의 앞뒤에 특정 문자열을 끼워넣은 상태로 해시를 돌려 버리면 된다. 암호는 대부분 아스키 코드 내의 문자로 입력받으므로, 이 영역 밖의 문자를 솔트 문자열로 하면 좋다. 쐐기 문자라던가, 마작에 사용되는 패 등... 단, 예시로 든 문자들은 유니코드BMP 영역을 벗어나기 때문에 DB에 따라 지원하지 않는 경우도 있다. 대표적으로 MySQL이 5.4 이하 버전에서는 BMP를 넘어가는 영역을 지원하지 않는다. ~~그리고 해커들은 멘탈붕괴.~~ 눈사태 효과적절하게 이용한 것이라고 볼 수 있다. 물론 깊게 파고들자면 사람이 인식할 수 있는 문자로 할 경우 암호의 안전성을 담보할 수 없기에 난수생성을 통해 작성된 수천 비트 단위의 바이너리 문자열을 사용하는 것을 권장하고 있다. 이렇게 생성된 문자열은 별도의 공간에 두어 인증 시에만 사용된다.


소금(salt). 주성분인 염화나트륨(소듐클로라이드, 염화소듐)의 화학식은 NaCl이다. 정확히 하자면 Salt = Sodium + Chlorine.


과학 하는 사람들에게 영어로 "솔트"라고 하면 일반적인 을 뜻한다. 원서 같은 데 다짜고짜 salt라고 나와 있다면 절대 소금이 아니고 염이라는 뜻이다. 과학 분야 원서에서는 당연히 소금sodium chloride라고 쓴다.


비밀번호에 소금 치는 방법

소금을 사용해서 새로운 비밀번호를 저장하고, 사용자가 입력한 비밀번호와 소금이 쳐진 비밀번호가 같은 지 검증하는 방법은 다음과 같다.


비밀번호 저장하기

1. CSPRNG(Cryptographically Secure Pseudo-Random Number Generator)[1]를 사용해서 임의의 소금값을 생성한다.

2. 소금값을 비밀번호의 앞이나 뒤에 덧붙이고 SHA256 같은 표준 암호화 해시 함수를 사용해서 해시한다.

3. 소금값과 해시값을 사용자 계정 테이블에 저장한다.


비밀번호 유효성 검사

1. 사용자의 소금값과 비밀번호 해시값을 데이터베이스에서 찾는다.

2. 입력한 비밀번호에 소금값을 덧붙이고 비밀번호 해싱에 사용했던 동일한 해싱함수를 사용하여 해싱한다.

3. 입력한 비밀번호로 생성한 해싱값과 저장되어 있는 해싱값과 비교해서 일치하는지 확인하고 동일 하면 비밀번호가 정확한 비밀번호를 입력한것이고 아니면 잘못된 비밀번호를 입력한 것이다.

느린 해시 함수 사용

또한 느린 해시 함수를 사용해 컴퓨터가 해시 함수를 푸는데 시간이 오래 걸리게 하여 무작위 대입 방법으로는 비밀번호 해킹하는것을 어렵게 만들 수도 있다. 이 방법은 트루크맆트에선 없던 것으로 베라크맆트에서 도입되었다. 베라크맆트에서는 해시 함수를 보통 1,000번 정도 반복 연산하여 속도를 느리게 한다.

소금 값은 룩업 테이블이나 레인보우 테이블 처럼 해시 되어 있는 값에서 비밀번호를 찾는 방식이 통하지 않게 해준다. 하지만 단어 사전 공격이나 무차별 입력 공격같은 것은 미리 방어 하는게 불가능하다. 높은 성능의 그래픽카드(GPUs)나 직접 제작된 특별한 장비들은 1초에 수십억개의 해시를 만드는게 가능하고 이러한 공격은 여전히 유효하다. 이러한 공격들을 무용하게 만들려면 key stretching이란 기술에 대해 알고 있어야 한다.

고성능의 GPU와 커스텀 장비를 사용한 단어 사전 공격무차별 대입 공격을 방어하는 방법으로 해시 함수를 느리게 하는 방법이 있다. 이 방법을 완성 하기 위해서는 위 공격들에 대해서는 해시 함수가 느리게 동작하도록 하고 실제 사용자에게는 불편함이 없는 속도로 제공해야 된다.

Key stretching은 CPU를 많이 사용하는 특별한 해시 함수를 사용해서 구현된다. 별도로 해시 함수를 구현 할려고 하지 말고 표준 알고리즘인 PBKDF2(Password-Based Key Derivation Function 2)나 bcrypt를 사용하라.

이 알고리즘들은 보안 요소 나 반복 횟수를 인자로 받는데 이 값들은 해쉬 함수를 어느 정도 느리게 할것인지 결정하는데 사용된다.

베라크맆트에서는 1,000번 정도 반복하여 트루크맆트보다 비밀번호를 깨기가 힘들다.


코챈 password = utils.sha256(rawPassword);

  • #12593 encryption 2019-6-26 오전 1:10 [삭제]

코챈에 입력하는 암호 hash function 돌려서 hash value로 저장하냐, 아니면 그냥 plaintext로 저장하냐? SHA 256 이상이면 그냥 하나의 비번 쓰려고.

지금은 혹시 비번 누출돼서 프로파일링 당할까봐 랜덤 비번 써서 수정을 못 함.


1: 수정 -> 삭제. 그래서 중복으로 두번 올리는 경우도 있고.

        [삭제] 2019-6-26 오전 1:11


2: 아직도 애미가 로리 영상에 출연한 운영자 새끼들이 MD5로 암호화하는 경향이 있음.

        [삭제] 2019-6-26 오전 3:13


3: >2 복호화가 안 되니까 암호화는 아니고 해시화

        [삭제] 2019-6-26 오전 3:17


5: >2 우리 엄마는 근친 영상에 출연했음

   -Admin     [삭제] 2019-6-26 오전 3:37


6: >5 코챈 운영자 근친충이었냐?

        [삭제] 2019-6-26 오후 10:09


4: password = utils.sha256(rawPassword);

   -Admin     [삭제] 2019-6-26 오전 3:34


7: 솔트 없이 해시화 하면 같은 암호 = 같은 해시니까 유출 됐을 때 프로파일링 되는 거야 똑같지

        [삭제] 2019-6-28 오전 0:40


8: >7 그러게. salt 안 넣으면 똑같네.

        [삭제] 2019-6-28 오전 4:24


9: >8 쏠트 http://hwikis25cffertqe.onion/wiki/index.php?title=%EC%8F%A0%ED%8A%B8

        [삭제] 2019-6-28 오전 4:34

http://jqu6my2mlqp4zuui.onion/p?id=12593

비밀번호 해시에 소금치기 - 바르게 쓰기

https://starplatina.tistory.com/entry/비밀번호-해시에-소금치기-바르게-쓰기

https://crackstation.net/hashing-security.htm

Java/이론..2014.05.23 14:02

웹 서핑중에 비밀번호 암호화 관련해서 좋은 글이 있어서 번역해 봤습니다.

개인 프로젝트를 하던 다른 서비스 프로젝트들 하던 사용자의 비밀번호를 암호화하는것은 중요한데요. 암호화 하기 위해서 보통 해싱 함수를 사용해서 비밀번호를 해싱하고 여기에 "소금을 친다(Adding Salt)" 라는 방법을 사용합니다. 이 내용에 대한 설명이 전반적으로 잘 되어 있네요.

노력은 했지만 많이 부족하니... https://crackstation.net/hashing-security.htm 원문 읽어 보시는 것도 강추 드립니다.

비밀번호 해싱이란 무엇인가, 해싱값이 어떻게 해킹되나?, 소금 치기 에 대한 내용입니다.


만약 웹 개발자라면 사용자 계정이 포함된 시스템을 만들어본 경험이 있을 것이다. 이 시스템에서 가장 중요한 점은 사용자의 패스워드가 어떻게 보호되고 있는가 이다. 사용자 정보를 가지고 있는 데이터베이스는 자주 해킹 당하고 만약 보호책이 없다면 반드시 비밀번호를 보호 하도록 해야 한다. 암호를 보호하는 가장 좋은 방법은 소금을 친 해싱을 사용하는 것이다.(소금을 치다 -> 패스워드를 보호하기 위해 특별한 값(소금)을 추가 하는 것) 이 페이지에서는 이 방법을 왜 수행하는지 설명할 것이다.

제대로 비밀번호 해시를 수행하는 방법에 대한 여러 상충하는 아이디어들과 오인들이 존재 한다. 아마 웹에 존재하는 잘못된 정보들 때문일 것이다. 비밀번호 해싱은 아주 간단한 것인데 많은 사람들이 오해 하고 있다. 이 페이지를 통해 해싱을 올바르게 사용하는 방법과 왜 이렇게 해야 되는지에 대해서 설명할 것이다.

중요한 경고! 만약 자신만의 비밀번호 해싱 방법을 가지고 있다면 그렇게 하지 말아라! 그 방법은 망가지기 쉽다. 만약 암호학을 전공하고 있다고 해도 이 경고를 무시해서는 안된다. 이 경고는 모두에게 적용된다. 절대 자신만의 암호화 방법을 만들지 말라.비밀번호 저장에 관련된 문제는 이미 해결 되어있다.


소금에 관해 아래 여섯 단락에서 다룰 것이다.

1. 비밀번호 해싱이란 무엇인가?

2. 해쉬가 해킹 되는 방법

3. Adding Salt(소금 치기)

4. 효과없는 해싱 방법

5. 올바른 방법 : 훌륭한 방법으로 해싱 하기

6. 자주 묻는 질문

비밀번호 해싱이란 무엇인가?

해싱 알고리즘은 단방향성을 가지고 있고 고정된 길이의 "fingerprint" 값을 제공한다. 위의 예제 처럼 한글자만 변경되도 전혀 다른 해쉬 값을 생성한다. 이 방법은 비밀번호가 인코딩 되어 저장될때 디코딩할 수 없기 때문에 비빌번호를 보호하는데 아주 좋은 방법이다. 이 방법과 동시에 사용자가 입력한 패스워드가 동일한지도 검증을 해야 한다.


해쉬를 기본으로 사용하는 계정관리 시스템에서 사용하는 사용자 등록 및 인증 관련 흐름은 아래와 같다.

1. 사용자가 계정을 생성한다.

2. 사용자의 비밀번호는 해싱되어 데이터베이스에 저장된다. 원본 패스워드는 하드 디스크 어디에도 기록되지 않는다.

3. 사용자가 로그인을 시도 할 때 사용자가 입력한 패스워드의 해시값이 데이터베이스에 저장된 값과 동일 한지 비교 한다.

4. 만약 해시값이 동일하면, 사용자는 로그인에 성공하고 아니면 잘못된 값을 입력했다고 알려준다.

5. 로그인을 계속 시도 하는경우 3~4번 과정을 반복한다.


4번째 과정에서, ID가 잘못됬는지 입력한 비밀번호가 잘못되었는지는 절대로 알려주지 않는다. 항상 "사용자 ID 및 비밀번호 가 일치 하지 않습니다" 메시지를 노출 시켜야 한다. 이 방법은 암호를 모르는 상태에서 유효한 아이디를 가지고 비밀번호를 무작위로 입력할 수 있는 방법을 방어 할 수 있다.

암호를 보호하기 위해 생성된 해시 함수는 데이터 구조학 강좌에서 사용되는 해쉬 함수와 동일한 것이 아닌것을 알아야 한다. 해쉬 함수는 보안을 위해서 고안된 것이 아니라 데이터 구조학에서 해쉬 테이블을 빠르게 사용하기 위해서 만들어진 것이다. 암호화된 해쉬 함수 만이 비밀번호를 해싱 하는데 사용되어야 할 것이다. 암호화 해쉬 함수로는 SHA256, SHA512, RipeMD, WHIRLPOOL 같은 것들이 있다.

암호화 해쉬 함수를 통해서 비밀번호를 관리하면 사용자들의 비밀번호는 안전할 것이라고 생각할 수 있다. 이것은 현실과는 꽤 다른데 아주 빠르게 일반 해시 암호를 찾아 낼 수 있는 방법이 있다. 하지만 이 공격 방법에 덜 영향을 받는 효과적인 몇가지 방법이 존재한다. 이러한 기술의 필요성에 대한 동기를 부여하기 위해서 웹사이트를 생각해보자. 메인 화면에서 해킹된 비밀번호 해쉬 값들을 전달해보고면 이 결과가 1초도 안되서 표시되는것을 볼 수 있다.(해킹된 비밀번호 해독 해서 입력 하는것 말하는 듯..) 확실히 비밀번호를 간단하게 해싱하는 것만으로는 보안에 대한 요구사항을 충족 시킬 수 없다.

다음 섹션에서는 해킹된 일반 해싱 비밀번호를 사용한 일반적으로 알려진 공격에 대해서 논의할 것이다.

해쉬가 해킹 되는 방법

단어 사전 입력 공격 및 무차별 대입 공격

해쉬를 해킹하는 가장 쉬운 방법은 비밀번호를 여러가지로 예측해보고 반복해서 입력해보는 것이다. 가장 유명한 패스워드 예측 방법은 단어 사전을 통한 공격과 무차별 대입 공격이다.

단어 사전 공격은 단어나 일반적인 비밀번호 등 비밀번호로 쓰일만한 단어들을 가지고 공격을 하는 것이다. 각각 단어들을 먼저 해싱해 놓고 해싱 되어 있는 비밀번호와 비교한다. 해쉬 값이 일치 하면 바로 그 단어가 비밀번호가 된다. 이 단어 사전 파일을 텍스트들에서 추출하여 구성되고 있기도 하고 심지어는 실제 데이터베이스에서도 추출하여 구성되어 있기도 한다.

무차별 대입 공격은 주어진 비밀번호의 길이에 맞춰 가능한 모든 글자의 조합을 사용하는 것이다. 이 방법은 계산 비용이 비싸고 효율이 가장 좋지는 않지만 결국 비밀번호를 찾는데 성공할 것이다.

단어 사전 공격이나 무차별 대입 공격에 대해 방어할 방법은 없다. 이 방법들이 비 효율적이긴 하지만 예방할 방법이 없다. 만약 당신의 비밀번호 해싱 시스템이 확실히 보안되어 있다면 해시를 해킹할 수 있는 방법은 단어 사전 공격이나 무차별 대입 공격을 사용하는 수 밖에 없다.

Lookup tables

룩업 테이블은 매우 빠르게 동일한 유형의 해시를 해킹하는데 매우 효과적인 방법이다. 일반적으로 비밀번호 사전에서 해쉬값들을 미리 추출해 놓고 비밀번호를 여기에서 검색 한다. 룩업 테이블의 장점은 초당 백개 정도의 비밀번호를 검색할 수 있고 해시 데이터가 수십억개가 넘더라도 사용할 수 있다.

역 룩업 테이블

먼저 공격자들은 추출한 사용자 정보를 가지고 동일한 비밀번호를 사용자끼리 그룹핑을 한다. 공격자들은 다양한 추측 비밀번호를 입력하여 검색하고 해당 추측 비밀번호와 일치하는 사용자 목록을 가지고 온다. 이 방법은 일반적으로 많은 사용자가 동일한 비밀번호를 사용하기 때문에 매우 효율적이다.

레인보우 테이블

레인보우 테이블은 시간과 메모리 사이의 선택 사항이다. 룩업 테이블과 비슷 하지만 해쉬를 해킹하는 속도 향샹을 위해 룩업 테이블보다 더 작게 구성되어 있다. 더 작기 때문에 같은 용량의 디스크에 많은 해쉬값들을 저장할 수 있어서 더 효율적으로 사용할 수 있게 한다. 레인보우 테이블은 md5 값이나 8자 까지 해킹 할 수 있다.

다음으로 룩업 테이블과 레인보우 테이블로 해쉬 해킹을 불가능하게 만들수 있는 소금치기 라고 불리우는 기술에 대해서 알아보자


레인보우 테이블(rainbow table)은 해시 함수(MD5, SHA-1, SHA-2 등)을 사용하여 만들어낼 수 있는 값들을 왕창 저장한 표이다. 물론 해시 함수는 입력이 무제한이라서 모든 내용을 넣는 게 아니고, 이를테면 영어 소문자와 숫자 조합으로 일정 길이까지의 모든 문자열에 대해서 계산한다거나, 하는 것이다. 이걸 그대로 저장하면 거듭제곱의 위력을 확실하게 체험할 수 있기 때문에 (문자열에 한 글자 추가하면 아무리 적어도 30배, 문자 조합이 많으면 200배 정도로 커진다!) 적절한 가공 과정을 거친다. 이건 후술.

이렇게 무식하게 값을 때려박는 테이블의 특성상, 작은 테이블도 기본 100GB는 거뜬히 넘어주신다. 영어 대문자+소문자+숫자 조합까지 가면 완전히 헬게이트. 올라가는 것도 테라바이트 단위로 용량이 커진다. 이러한 특성 때문에 기업, 단체에서 주로 사용한다. 개인이 사용하기엔 자원이 너무나도 많이 들어가 어느정도 돈이 많지 않은 이상 힘들기 때문이다.

만들어진 이유

만들어진 이유는 여러가지가 있다.

브루트 포스 공격시 더 빠르게 비밀번호를 시도해 보기 위해서

브루트 포스 항목에도 나와 있듯이, 무식하게 대입을 하는 데에는 시간이 엄청나게 걸린다. 10자리 영문 소문자+숫자 조합을 초당 1억번 대입을 한다 하더라도 1년 이상 걸리는데, 해쉬를 계산하느라 초당 대입량이 적어지면... 이러한 문제를 조금이나마 해결하기 위해 나온 것이 첫 번째 이유이다. 해쉬를 일일히 계산해 내느니 차라리 미리 만들어진 테이블에서 해쉬값만 쏙쏙 뽑아다가 집어넣으면 해쉬 계산하는 시간이 절약되니 공격 과정을 좀 더 빨리 끝낼 수 있다. 일종의 사전 공격.

해쉬에서 평문을 추출해내기 위해서

해쉬는 기본적으로 역함수가 존재하지 않는 함수로 계산해낸다. 즉, 암호화-복호화 과정이 불가능하다는 말이다. 그래서 --할일 없는-- 사람들이 고안해낸 방법은 수학적으로 아예 역함수가 존재하지 않는 함수 가지고 씨름할 바에 차라리 가능한 모든 경우의 수를 다 써놓고 거기서 찾아내자!이다. 그래서 데이터베이스와 같은 자료에서 뽑아낸 해쉬값을 레인보우 테이블과 대조하여 평문을 찾아내는 것이다.

아이디어

그냥 가능한 모든 값들을 저장하면 되는 걸 굳이 왜 "레인보우 테이블"이라고 부르냐 하면, 레인보우 테이블은 계산 시간을 희생해서 공간을 압도적으로 줄일 수 있다. 이를테면 암호 하나를 찾는 시간을 10만배 늘리는 대신 테이블을 10만분의 1로 줄일 수 있는 것. 어차피 해시 함수는 10만배 느려져 봤자 밀리초 단위기도 하고, 레인보우 테이블에 저장되는 값 숫자 대비 찾을 암호 숫자가 매우 적기 때문에 쓸만한 것이다.

https://upload.wikimedia.org/wikipedia/commons/thumb/7/78/Rainbow_table1.svg/600px-Rainbow_table1.svg.png ([이미지], CC-by-sa 2.5로 배포됨)

레인보우 테이블에는 해시 함수 H 말고도 이 해시의 결과를 레인보우 테이블의 가능한 입력으로 변환해 주는 함수 R이 더 들어간다. 물론 R은 입력에 따라 다르고, 해시 함수와는 달리 원래 가능한 입력에 적절히 대응만 되기만 하면 된다. 다르게 생각하면, 레인보우 테이블은 H를 깨는 게 아니라 f(x) = R(H(x))인 f를 깬다. H와는 달리 f는 입력과 출력이 같기 때문에 다루기가 쉽다.

처음에 테이블을 만들 때는...

1. 가능한 입력 중 하나를 잡아서 H를 적용한다.
1. 함수의 결과를 R로 변환한다.
1. 이 과정을 필요한 숫자(k회라고 하자)만큼 반복한다.
1. 이제 입력 x와 마지막에 변환된 값 y = R(H(...R(H(x))...)) = f^^k^^(x)를 저장한다.
1. 이 작업을 최대한 많은 입력에 대해 반복한다.

여기서 중요한 점은, x와 y만 저장해도 계산만 좀 더 하면 f(x), f(f(x)), ..., f^^k-1^^(x)의 다른 k-1개의 입력이 커버된다는 것이다. 중간에 저장해야 할 게 k개에서 1개가 되었으니 공간이 1/k가 된다!

이 테이블을 써 먹으려면, 입력 a = H(b)가 있을 때 R(a), R(H(R(a))), ...를 계산해서 테이블의 y에 겹치는 게 있는지 보면 된다. 겹치는 게 있다면 대응되는 x로부터 최대 k번 f를 적용해서 b가 나오는지 확인해 보면 된다. (f(b) = f(c)인 c가 있을 수 있으므로 확인이 실패할 수 있다.) 안 나오면 그냥 한 번도 커버가 안 된 거고 나오면 잘 된 거다. 참 쉽죠?

...실은 뻥이고, 이 방법은 치명적인 문제가 있는데 입력이 많이 들어가면 많이 들어갈 수록 f(x), f(f(x)) 등이 서로 충돌할 가능성이 높아진다. 한 번 충돌이 나는 입력이 나오면 거기서부터는 커버되는 입력들이 똑같으니까 공간 절약이 1/k에서 확 줄어들 수 있는데, 이런 (x, y) 쌍들을 걸러낼 수 없다! 예를 들어 "뿌뿌뽕"을 f에 세 번 돌린 것과 "용개"를 f에 다섯 번 돌린 게 같은 값이 나온다고 하자. 그러면 이 두 값이 커버하는 입력의 개수는 2k개가 아니라 k+5개[* "뿌뿌뽕", f("뿌뿌뽕") .. f^^2^^("뿌뿌뽕"), "용개", f("용개") .. f^^4^^("용개"), f^^3^^("뿌뿌뽕") = f^^5^^("용개"), .., f^^k^^("뿌뿌뽕") = f^^k+2^^("용개").] 밖에 안 되니까 도움이 안 된다. 이럴 바에는 둘 중 하나를 날려 버리면 좋겠는데 (x, y) 쌍만 가지고는 이걸 알 수 없다.

https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Rainbow_table2.svg/600px-Rainbow_table2.svg.png (원 이미지, CC-by-sa 2.5로 배포됨)

그래서 진짜 레인보우 테이블은 각 단계별로 서로 다른 R을 쓴다(위 그림들에서 첨자가 붙어 있는 게 이 때문). 서로 다른 R을 쓰기 때문에 앞에서 "뿌뿌뽕"과 "용개"가 우연히 같은 값이 나왔어도 같은 위치에서 충돌이 난 게 아니면 서로 다른 입력을 커버하게 된다. 이 경우 커버하는 입력의 개수는 2k개에서 2k-1개로 줄어드는 수준으로, k가 크다면 무시해도 될 수준. 같은 위치에서 충돌이 나오면 그 뒤로는 같은 입력이 생성되긴 할텐데, 이러면 y가 겹칠테니 그냥 제거하면 된다.

이런 레인보우 테이블을 만들 가능성 자체를 없애기 위해 오늘날 암호를 저장할 때는 bcryptscrypt와 같이 한 번 해시 함수를 적용하는 데 더럽게 오래 걸리는 함수를 써야 한다. ~~현실은 해시 함수라도 쓰면 다행.~~ 2000년대 전후에 생성된 웹사이트는 여전히 비밀번호를 DB에 평문으로 저장하는 곳이 비일비재하다.


Adding Salt(소금 치기)

룩업 테이블과 레인보우 테이블은 비밀번호가 해킹할 해쉬와 동일한 방법으로 해싱되어 있어야 해킹이 가능하다. 만약 두 사용자가 동일한 비밀번호를 사용한다면 이들은 동일한 해싱 비밀번호를 가지게 된다. 이는 각 해시들을 무작위 구성되게 함으로써 예방할 수 있고, 만약 같은 비빌번호가 두번 해싱된다면 이 값은 서로 동일하지 않게 된다.

소금이라고 불리는 무작위 문자열을 비밀번호를 해싱하기 전에 붙여서 해쉬 값을 무작위로 만들 수 있다. 상단의 예제를 보면 같은 비밀번호인데도 결과로 생성된 해시값은 매번 다른것을 볼 수 있다. 인증을 진행할 때 비밀번호가 동일한지 확인을 하기 위해서는 소금값이 필요 한데 이 값은 보통 사용자 계정을 저장하는 데이터베이스에 비밀번호 해쉬값과 같이 있거나 해쉬값으로 변환 되어 저장하고 있다.

소금 값은 비밀로 관리 하지 않아도 된다. 그냥 룩업 테이블과 역 룩업 테이블, 레인보우 테이블이 효과를 볼 수 없게 해시를 무작위로 사용하면 된다. 공격자는 소금 값이 뭐가 될지 알 수 없고 룩업 테이블과 레인보우 테이블 값을 미리 생성해 놓을 수가 없다. 만약 각각 사용자마다 다른 소금 값으로 해싱되어 있다면 역방향 룩업 테이블도 동작하지 않을 것이다.

다음 섹션에서는 소금 값에 대해서 일반적으로 잘못 구현된 상황에 대해서 알아 보도록 할 것이다.

효과없는 해싱 방법

잘못된 방법 : 짧은 소금 값 & 소금 값 재사용

소금값을 잘못 사용하는 가장 흔한 경우는 같은 소금값을 여러 해시에 사용하거나 너무 짧은 소금 값을 사용하는 것이다.

소금 값 재사용

가장 흔한 실수는 각 해쉬마다 같은 소금값을 사용하는 것이다. 어느 소금값은 프로그램내에 하드 코딩 되어 있거나 랜덤으로 한번 생성해서 사용하기도 한다. 이것은 효과가 없는데 만약 두 사용자가 같은 비밀번호를 사용할 경우 그들은 여전히 같은 해쉬값을 가지게 된다. 해커들은 여전히 역 리버스 룩업 테이블을 사용해 단어 사전 공격을 시도 할 수 있다. 그들은 비밀번호를 해싱하기 전에 사용될만한 소금 값들을 추가한 후 해싱을 한다. 만약 소금 값이 유명한 제품의 이름으로 되어 있다면 룩업 테이블과 레인보우 테이블은 이 소금값을 사용해 만들어서 쉽게 비밀번호를 얻어 낼 수 있다.

사용자 계정을 새로 만들거나 비밀번호를 변경할 때는 반드시 무작위로 생성된 소금값을 사용해야 된다.

짧은 소금 값

만약 소금 값이 너무 짧으면 해커는 가능한 소금값들을 활용해서 룩업 테이블을 만들 수 있다. 예를 들어 만약 소금값이 아스키 문자 3자로 되어 있다면 소금 값으로95*95*95=857,375개의 값이 사용 가능하다. 이 값이 많아 보일 수도 있지만 각각의 룩업 테이블들이 1메가 정도의 평범한 비밀번호로 구성되어 있는 경우 837G 만으로 전체 룩업 테이블을 구성할 수 있고 요즘 1000GB 하드 디스크는 채 100달러도 하지 않는다.

같은 이유로 사용자 이름 역시 소금 값으로 사용할 수 없다. 혼자 독립적으로 운영되는 서비스의 경우는 사용자 이름이 유니크할 수 있지만 다른 서비스에서도 똑같이 자주 사용된다. 해커들은 평범한 사용자 이름을 사용해 룩업 테이블을 구성하고 이를 사용해서 사용자이름이 소금값으로 사용된 해시값을 생성한다.

해커가 사용가능한 소금값을 가지고 룩업 테이블을 생성하는 것을 불가능 하게 할려면 소금 값은 반드시 길게 만들어야 된다. 좋은 방법은 해쉬 함수를 사용해서 생성된 길이와 동일하게 만드는 것이다. 예들 들어 해시 값이 SHA256 알고리즘을 사용해서 256 비트(32 바이트)로 생성 한다면 소금 값 역시 랜덤으로 생성된 32바이트로 만들면 된다.

잘못된 방법 : 이중 해싱 및 엉뚱한 해쉬 함수

이 섹션에서는 엉뚱한 해시 알고리즘 조합 같은 잘못된 해싱 방법에 대해서 살펴본다. 다른 해싱 함수들을 조합해서 사용할 수 있으니 그 결과가 더 안전할 것이라고 생각하기 쉽다. 실제로 이를 수행함으로써 아주 작은 이득이 있다. 하지만 이 방법은 상호 운영성에 대한 문제가 발생하고 가끔 해쉬 값을 덜 안전하게 만들기도 한다. 절대로 자신만의 암호화 방식을 만들지 말고 항상 고수들에 의해 생성된 표준을 사용하도록 해라. 여러개의 해싱 함수를 사용하면 해싱 작업을 수행하는 이 느려지고 그래서 해킹하는 것도 느릴것이라고 주장 하지만 해킹 시간을 느리게 하는 더 좋은 방법이 있고 나중에 보게 될 것이다.

여기 내가 웹 포럼에서 추천하고 있는걸 본 허접한 해싱 함수가 있다. - md5(sha1(password)) - md5(md5(salt) + md5(password)) - sha1(sha1(password)) - sha1(str_rot13(password + salt)) - md5(sha1(md5(md5(password) + sha1(password)) + md5(password))) 이중에 아무것도 사용하지 말아라.

주의 : 이 부분에 대한 논란은 이미 검증된 것이다. 나는 이 허접한 해싱 함수들이 해커들이 어떤 해싱함수들을 사용했는지 알수 없고 엉뚱한 해시 함수들을 사용해서 레인보우 테이블을 구성하는 해커들은 적은 수이고 이 방법이 해싱함수를 수행하는데 시간이 더 오래 걸릴것이기 때문에 좋은 방법이고 주장하는 메일들을 여러통 받았다.

해커들은 알고리즘에 대해 알지 못할 경우 해시에 대한 공격을 시도 하지 않는다. 하지만 케르크호프스의 원리(키를 제외한 시스템의 다른 모든 내용이 알려지더라도 암호쳬게는 안전해야 한다)에 따르면 해커들은 소스 코드에 접근할 수 있고(특별히 무료거나 오픈 소스 소프트웨어일 경우) 비밀번호-해시 방법을 사용한 시스템이 목표가 되고, 이 알고리즘을 리버스 엔지니어링하는것은 어렵지 않다. 병렬화하기가 매우 어렵게 디자인된 알고리즘을 사용하는것이 좋다. 그리고 레인보우 테이블 문제를 해결하기위한 정확한 방법은 소금 치기 기법을 사용하는 것이다.

HMAC같이 표준화된 이상한 해시 함수를 사용한다면 괜찮다. 하지만 해시 작업을 느리게 하는것이 이유라면 key stretching에 대한 색션을 먼저 읽어 보기 바란다.

실수로 안전하지 않은 해싱 함수를 만드는 경우와 상호 운영성에 문제가 있는 엉뚱한 해싱함수를 사용해서 얻는 작은 이득에 대해 비교해보자. 확실하게 잘 테스트된 표준 방식을 사용하는것이 가장 좋은 방법이다.

해시의 충돌

해시 함수는 고정길이의 문자열로 이루어 지도록 되어 있으므로 같은 입력에 대해서는 동일한 해쉬를 가지고 가지게 된다. 암호화 해시 함수는 이렇게 동일한 해쉬를 가지고 있는것을 찾기 어렵도록 설계 되었다. 암호학자들은 해쉬가 충돌하는 것을 찾아 낼 수 있고 최근에 MD5 해시 함수를 사용했을 때 해시 충돌을 활용한 공격이 발생하기도 했다.

해쉬 충돌은 취약한 해시 함수인 MD5를 사용할 경우에도 이를 찾아 내는데 많은 컴퓨터 리소스를 필요로 한다. 실제 환경에서는 거의 발생할 일이 없고 대부분 테스트를 하는 과정에서 우연히 발생한다. MD5와 소금값을 사용하여 해시를 하는경우 SHA256과 소금값을 사용하여 해쉬하는 것 만큼 안전하긴 하지만 가능한 SHA256, SHA512, RipeMD, WHIRLPOOL 같은 더 안전한 해시 함수를 사용하는것이 좋다.

올바른 방법 : 훌륭한 방법으로 해싱 하기

이 섹션에서는 암호 해싱에 대한 정확한 방법을 설명한다. 첫번째로 기본 해시에 대해서 알아보고 두번째로 이 기본 해시를 가지고 해킹을 어렵게 하는 방법에 대해서 알아본다

기본 : 소금값과 함께 해싱하기

이전 섹션에서 악의적인 목적을 가진 해커가 룩업 테이블과 레인보우 테이블을 사용해서 일반 해시를 해킹하는것을 살펴보았다. 이 문제를 해결하는 방안으로 소금값을 랜덤으로 생성하여 해싱할 때 같이 사용하는것을 배웠지만 소금값을 어떻게 생성하고 비밀번호에 이를 어떻게 적용할 것인가?

소금 값은 암호학적으로 안전한 난수 생성기에 의해 생성(Cryptographically Secure Pseudo-Random Number Generator, CSPRNG)되어야 한다. CSPRNG은 C언어의 rand() 함수처럼 일반 난수생성기와 매우 다르다. 이름을 통해 짐작하듯이 CSPRNG는 암호화를 사용하도록 설게되어 있고 이 것은 완벽히 예측 불가능 한것을 의미한다. 소금값은 예측가능한 것을 사용할수 없기 때문에 반드시 CSPRNG를 사용해야 된다. 아래 표에서는 유명한 프로그래밍 언어에서 제공하는 CSPRNG 목록이다.


Platform CSPRNG

PHP mcrypt_create_iv, openssl_random_pseudo_bytes

Java java.security.SecureRandom

Dot NET (C#, VB) System.Security.Cryptography.RNGCryptoServiceProvider

Ruby SecureRandom

Python os.urandom

Perl Math::Random::Secure

C/C++ (Windows API) CryptGenRandom

Any language on GNU/Linux or Unix Read from /dev/random or /dev/urandom


소금값은 사용자와 비밀번호 별로 유일한 값을 가져야 한다. 사용자 계정을 생성할때와 비밀번호를 변경할때마다 새로운 임의의 랜덤 소금값을 사용해서 해싱 해야 된다. 소금값은 절때 재사용 하지 말아야 되고 길게 만들어야 되기 때문에 다양한 값을 생성할 수 있다. 소금값은 해쉬 함수의 출력 값 만큼 길게 만들고 사용자 계정 테이블에 같이 저장되도록 한다.

비밀번호 저장하기

1. CSPRNG를 사용해서 임의의 소금값을 생성한다.

2. 소금값을 비밀번호 앞에 덧붙이고 SHA256 같은 표준 암호화 해시 함수를 사용해서 해시한다.

3. 소금값과 해시값을 사용자 계정 테이블에 저장한다.

비밀번호 유효성 검사

1. 사용자의 소금값과 비밀번호 해시값을 데이터베이스에서 찾는다.

2. 입력한 비밀번호에 소금값을 덧붙이고 비밀번호 해싱에 사용했던 동일한 해싱함수를 사용하여 해싱한다.

3. 입력한 비밀번호로 생성한 해싱값과 저장되어 있는 해싱값과 비교해서 일치하는지 확인하고 동일 하면 비밀번호가 정확한 비밀번호를 입력한것이고 아니면 잘못된 비밀번호를 입력한 것이다.

항상 서버에서 해시를 해야 된다.

웹 애플리케이션에서는 항상 서버에서 해시를 해야 된다.

만약 웹 애플리케이션을 개발중이라면 해쉬를 어디서 할 것인지 고려해봐야된다. 만일 사용자의 브라우저에서 자바스크립트를 사용해 해쉬 되거나 이 해쉬된 값을 서버에 안전하게 전성되었을 경우 이를 사용해야 될까?

자바스크립트로 비밀번호를 해싱 했을때 조차도 서버에서 해시작업을 해야 된다. 사용자 브라우저에서만 해쉬를 하고 서버에서 해쉬를 하지 않을 경우를 고려해 보라. 사용자를 인증하기 위해 웹 사이트에서 생성된 해쉬를 만들고 이를 데이터베이스에 조회해서 동일한 값을 찾을 것이다. 사용자의 암호가 서버로 전송되지 않기 때문에 서버에서 해쉬작업을 하는것 보다 조금 더 안전한 것처럼 보이지만 그렇지 않다.

문제는 클라이언트 쪽에서 사용자의 비밀번호가 해쉬된다는 것이다. 모든 사용자들이 서버에 비밀번호를 확인해야 된다. 해커가 이 해쉬 값을 얻은 경우 이 값을 사용해서 사용자 인증을 진행할 수 있다. 만약 해커가 이 웹사이트의 비밀번호 해쉬가 담긴 데이터베이스를 해킹한다면 암호를 추측해서 사용할 필요도 없이 바로 모든 사용자의 계정에 접속 할 수 있다.


브라우저에서 해시를 할수 없다는 뜻은 아니지만 만약 브라우저 해쉬를 사용해야 된다면 서버 해쉬 작업도 반드시 진행해야 된다. 브라우저에서 해싱을 하는것은 좋은 아이디어이긴 하지만 구현을 위해 아래 사항을 고려해야 한다.

- 클라이언트 암호 해시는 HTTPS(SSL/TLS)를 대신할 수는 없다. 브라우저와 웹서버가 보안 통신으로 연결되어 있지 않다면 있다면 중간에서 이를 가로체 사용자의 비밀번호를 알아낼 수 있다.

- 몇몇 웹 브라우저들은 자바스크립트를 지원하지 않고 몇몇 사용자들은 브라우저에서 자바스크립트 기능을 꺼놓기도 한다. 최대한 호환성을 지원하기 위해 브라우저가 자바스크립트를 지원하는지 잘 감시 해야 되고, 클라이언트 해쉬가 동작하지 않을 경우 서버에서 해시 작업이 수행될 수 있도록 해야 된다.

- 클라이언트 쪽에서도 소금 값을 사용할수도 있다. 클라이언트 스크립트에서 서버를 통해 사용자의 소금값을 확인하는 것아 해결책 이긴 하지만 이를 사용해서는 안된다. 왜냐하면 악의적인 사용자들이 중간에서 이를 가로채 사용할 수 있기 때문이다.

서버에서도 해싱 및 소금값을 사용한다면 사용자 이름(또는 이메일)을 사이트 정보(도메인 이름)과 함께 클라이언트 소금값으로 사용하는것은 괜찮다.

느린 해시 함수를 사용하기

느린 해시 함수를 사용해 비밀번호를 해킹하는것을 어렵게 만들기

소금 값은 룩업 테이블이나 레인보우 테이블 처럼 해시 되어 있는 값에서 비밀번호를 찾는 방식이 통하지 않게 해준다. 하지만 단어 사전 공격이나 무차별 입력 공격같은 것은 미리 방어 하는게 불가능하다. 높은 성능의 그래픽카드(GPUs)나 직접 제작된 특별한 장비들은 1초에 수십억개의 해시를 만드는게 가능하고 이러한 공격은 여전히 유효하다. 이러한 공격들을 무용하게 만들려면 key stretching이란 기술에 대해 알고 있어야 한다.

고성능의 GPU와 커스텀 장비를 사용한 단어 사전 공격와 무차별 대입 공격을 방어하는 방법으로 해시 함수를 느리게 하는 방법이 있다. 이 방법을 완성 하기 위해서는 위 공격들에 대해서는 해시 함수가 느리게 동작하도록 하고 실제 사용자에게는 불편함이 없는 속도로 제공해야 된다.

Key stretching은 CPU를 많이 사용하는 특별한 해시 함수를 사용해서 구현된다. 별도로 해시 함수를 구현 할려고 하지 말고 표준 알고리즘인 PBKDF2(Password-Based Key Derivation Function 2)나 bcrypt를 사용하라.

이 알고리즘들은 보안 요소 나 반복 횟수를 인자로 받는데 이 값들은 해쉬 함수를 어느 정도 느리게 할것인지 결정하는데 사용된다. 데스크탑 소프트웨어나 스마트폰 앱에서 어떤 변수를 사용할지에 대한 결정은 작은 벤치마킹을 한번 수행해 보는 것이다. 이 방법 대로면 사용자는 사용환경 변화를 느낄수 없고 프로그램은 가능한한 안전할 것이다.

웹 애플리케이션에서 key Stretching을 사용한다면 큰 볼륨의 인증 요청을 처리하기 위해서 컴퓨터 자원이 많이 필요할 수 있고 이 key stretching은 웹사이트를 쉽게 DoS 공격 할 수 있기 때문에 주의해야 하지만 낮은 반복 횟수를 사용한다면 key stretching을 사용하는것을 추천한다. 서버 자원을 얼마나 사용할 수 있는지 및 최대 인증 요청 횟수에 따라 반복 횟수를 결정할 수 있다. 로그인 할때마다 CAPTCHA(랜덤 문자 입력 방식)을 사용해서 Dos 위협을 해결할 수 있다. 시스템을 설계할때 반복횟수가 증가 또는 감소 될 수 잇도록 시스템을 설계한다.

시스템 부하에 대해 걱정이 된지만 key stretching을 사용하고 싶다면 사용자의 브라우저에서 자바스크립트를 통한 key stretching을 사용하는 것을 고려할 수 도 있다. 자바스크립트 표준 암호화 라이브러리는 PBKDF2에 포함되어 있다. 반복 횟수는 모바일 장비같은 느린 환경에서도 사용할 수 있도록 충분이 낮게 설정해야 되고 사용자의 브라우저가 자바스크립트를 지원하지 않을 경우 서버에서 처리 할 수 있도록 해준다. 사용자측에서 하는 key stretchin은 서버측의 해싱을 삭제할 필요가 없다. 클라이언트가 비밀번호를 해시하는 것과 동일하게 생성된 해시를 서버에서도 해시 해야 된다.

키 해시 및 하드웨어 비밀번호 해싱

해킹이 불가능한 해시 : 키 해시 및 하드웨어 비밀번호 해싱

비밀 키를 해시에 추가 하고 이를 알고 있는 사람만이 비밀번호가 유효한지 확인이 가능하다. 이것은 두가지 방법으로 수행될수 있는데, AES같은 암호화 모듈을 사용하여 암호화 하거나 비밀 키를 HMAC(Hash-based message authentication code)같은 키를 사용한 해싱 알고리즘에 포함하여 해시에 사용할 수 있다.

이 방법은 생각보다 쉽지 않다. 키는 해커로 부터 안전하게 보호되어야 한다. 만약 해커가 시스템에 사용할 수 있는 모든 권한을 얻어 냈을때 저장 위치에 상관 없이 키를 갈취 할 수 있다. 키는 반드시 물리적으로 분리되고 인증 시스템을 가지고 있는 외부 시스템에 저장 되거나 YubiHSM같은 특별한 물리장비에 저장 되어야 한다. 십만명 이상의 사용자가 있을 경우에만 이렇게 하는 것을 추천한다.

만약 물리서버를 분리할수 없거나 특수 장비를 사용할 수 없는 경우에는 일반 웹 서버에서도 키 해시에 대한 이점을 사용할 수 있다. 대부분의 데이터베이스는 SQL Injection 공격에 취약한 부분이 있는데 해커들이 이를 사용해서 local 파일 시스템에 접근하지 못하도록 한다. 만약 랜덤 키를 생성한 후 소금치는 해싱 작업을 한 후 웹에서 접근할 수 없는 파일에 저장 한다면 데이터베이스가 SQL Injection 공격하는 경우에도 괜찮다. 키 값은 소스 코드에 하드코딩 하지 말고 애플리케이션을 설치할 때 무작위로 생성 되도록 한다. 이 방법은 장비를 분리하는 것만큼 안전하지는 않지만 아무것도 하지 않는것보다는 좋다.

키 해시 방법을 사용할때 소금 값을 지울 필요는 없다. 영리한 해커들은 결국엔 키 값을 찾아 낼 것이기 때문에 해쉬 값들은 소금 값과 key stretching에 의해 보호 되고 있어야 한다.

다른 보안 조치

비밀번호 해싱은 비밀번호가 보안을 위반할때도 보호 되어야 한다. 전체 응용 시스템에 대한 보안작업을 해야 되는것은 아니지만 비밀번호 해시를 해킹 당하는 것은 가장 먼저 예방해야된다.

숙련된 개발자가 보안 관련 애플리케이션을 개발할 때도 보안사항에 대해서는 교육을 받아야 한다. 웹 애플리케이션 취약점에 대한 공부자료로는 The Open Web Application Security Project(OWASP)가 있다. 이 10개의 취약점 목록을 참고 하라. 이 리스트에 있는 모든 취약점에 대해 이해 하지 않는한 민감한 데이터를 다루는 웹 애플리케이션을 개발 할려고 하지 말아라. 모든 개발자가 보안 관련 교육을 보장 받는것은 전부 고용주의 책임이다.

외부업체를 통한 취약점 검사를 받는것은 좋은 방법이다. 최고의 프로그래머 조차도 가끔 실수를 만들어 낼 수 있으므로 보안 전문가가 잠재적인 보안 이슈를 확인 해야 된다. 신뢰할 수 있는 기관이나 직원을 고용하여 정기적으로 코드를 리뷰 하도록 해라. 보안 검토 프로세스는 애플리케이션 개발을 시작할때 부터 계속 진행되어야 한다.

만약 웹사이트 취약점 공격에 대한것이 발견 된다면 전체 서버를 모니터링 하는것이 중요하다. 서버에 대한 공격을 감지하고 보안 침해에 대응할 직원을 최소 한명이상 고용하는 것을 추천한다. 만약 해킹에 대해 감지 하지 못한다면 해커는 악성코드를 사용자에게 감염 시킬 수 있기 때문에 취약점에 대해서 감시하고 신속하게 대응하는것은 매우 중요하다.

자주 묻는 질문

무슨 알고리즘을 사용해야 되나?

사용 해도 되는 것

- The PHP source code, Java source code, C# source code or the Ruby source code at the bottom of this page.

- OpenWall's Portable PHP password hashing framework

- Any modern well-tested cryptographic hash algorithm, such as SHA256, SHA512, RipeMD, WHIRLPOOL, SHA3, etc.

- Well-designed key stretching algorithms such as PBKDF2, bcrypt, and scrypt.

- Secure versions of crypt ($2y$, $5$, $6$)

사용하지 말아야 되는것

- Outdated hash functions like MD5 or SHA1.

- Insecure versions of crypt ($1$, $2$, $2x$, $3$).

- Any algorithm that you designed yourself. Only use technology that is in the public domain and has been well-tested by experienced cryptographers

MD5 및 SHA1에 대한 암호 공격이 없다고 하더라도 이것들은 해킹 하기가 쉽고 오래되고 비밀번호를 저장하는데 사용되지 않는 해쉬 함수이기 때문에 이것을 사용하는 것을 추천 하지 않는다. 이 규칙에 대한 예외로 PBKDF2가 있는데 내부 해시 함수를 사용하여 구현한 SHA1을 사용할 경우다.


사용자들이 비밀번호를 잃어 버렸을때

사용자들이 비밀번호를 잃어 버렸을때 언제 비밀번호를 초기화 할 수 있게 해야 하나?

내 개인적인 의견은 요즘 사용되는 모든 비밀번호 초기화 방법은 안전하지 않다는 것이다. 만약 암호화된 서비스를 위해 높은 수준의 보안을 적용해야 한다면 사용자가 비밀번호를 리셋할 수 없게 해라.

대다수 웹사이트들이 사용자가 비밀번호를 잃어 버렸을때 이메일 인증을 사용한다. 이 작업을 하기 위해 무작위로 생성된 일회성 토큰이 생성되고 비밀번호를 리셋하는 url에 토큰을 포함하여 사용자에게 비밀번호 초기화 이메일을 보낸다. 인증 토큰이 포함된 비밀번호 초기화 링크를 클릭 하면 새로운 패스워드 입력 화면을 표시한다. 이 일회성 토큰은 사용자 별로 별도로 생성되기 때문에 해커들이 이를 다른 사용자의 비밀번호를 리셋하는데 사용할 수 없다.

토큰은 반드시 사용하거나 생성된지 15분이 지나면 반드시 만료 처리 되도록 해야 된다. 사용자가 암호를 다시 기억해내서 로그인 하거나 다른 리셋 토큰을 요청한 경우에도 이미 생성된 것은 만료 처리를 해야 된다. 만약 토큰 만료 처리가 안된다면 사용자의 비밀번호를 해킹하는데 지속적으로 사용될 수 있다. 이메일은 일반 텍스트 프로토콜이고 웹상에는 많은 악의적인 코드들이 존재한다. 이를 통해 이메일이 노출 될 수 있으므로 토큰 만료 기능을 꼭 추가 해야 된다.

해커들이 토큰을 조작할 수 있으므로 사용자 계정 정보나 만료 시간 정보 같은것은 포함되지 않도록 하다. 토큰은 반드시 예측 불가능한 이진 BLOB 형태로 데이터베이스에 기록되도록 해야 된다.

절대 사용자에게 신규 비밀번호를 메일로 보내지 말아라. 비밀번호를 재설정할때 새로운 소금값을 사용하고 이전에 사용했던 값은 재사용하지 말라.

만약 사용자 계정 데이터베이스가 해킹

만약 사용자 계정 데이터베이스가 해킹되었을땐 어떻게 해야 되나?

가장 먼저 처리해야 될 일은 시스템이 어떻게 해킹 되었고 해커가 사용한 취약점을 어떻게 패치해야될지 정하는 것이다. 만약 이런 해킹에 대한 경험이 없다는 외부 보안 담당자에게 의뢰 하는것을 강력하게 추천한다.

해킹 당한 것에 대해 감추고 아무도 이를 알아내지 않았으면 할수도 있다. 하지만 이를 감추려고 한다면 상황은 더 악화된다. 왜냐하면 사용자의 비밀번호와 개인 정보가 노출되고 있음을 사용자들에게 알리지 않음으로써 더 큰 위험요소를 만들어 내고 있을수 있기 때문이다. 가능한 빨리 사용자들에게 이 내용을 알려야된다(이 해킹 내용에 대해 정확이 인지하고 있지 않더라도). 웹 사이트 메인페이지에 이를 공지하고 상세 정보를 확인할 수 있는 링크를 걸어 놓고 모든 사용자들에게 이를 안내하는 메일을 보내도록 한다.

사용자들에게 비밀번호가 어떻게 안전하게 보관되고 있는지 설명해야되고(소금 값을 사용했기를 바라며) 비밀번호가 소금값으로 해시되어 있지만 악의적인 해커들은 단어 사전이나 무차별 공격으로 이를 해킹할 수 있다. 악성 해커들은 사용자들이 다른 웹사이트에 동일한 비멀번호를 사용했기를 기대하고 해킹한 비밀번호을 사용해서 다른 웹사이트에 로그인을 시도할 것이다. 이러한 위험성에 대해 사용자들에게 공지하고 비슷한 비밀번호를 사용하는 다른 웹사이트의 비밀번호를 변경하도록 제안한다. 사용자들이 시스템에 로그인할때 강제로 패스워드를 변경 하도록 하고 대부분의 사용자들이 이전 비밀번호를 빠르게 변경하기 위해서 이전 비밀번호와 동일하게 설정 할려고 하는데 이를 방지하는 작업도 해야 된다.

소금값과 함께 늦은 해쉬를 사용하더라도 해커들은 취약한 비밀번호들에 대해 매우 빠르게 해킹할 수 있다. 해커들이 이렇게 비밀번호를 찾아서 해킹할 가능성을 줄이기 위해서 비밀번호가 변경 되었을 때도 이를 인증하는 메일을 사용자에게 보내서 확인하도록 해야 된다.

또한 사용자들에게 어떠한 개인정보가 저장되고 있는지 알려야 한다. 만약 신용카드 번호를 저장 하고 있다면 사용자들에게 신용카드를 재 발급 받도록 알려주고 이 카드 번호를 사용해 결제된 내용들에 대해 확인하도록 알려 줘야된다.

비밀번호 정책은 무엇이 되야 하나?

비밀번호 정책은 무엇이 되야 하나? 강력한 암호를 사용하도록 해야되나?

만약 서비스가 엄격한 보안 정책이 필요 하지 않다면 사용자들이 비밀번호를 설정하는데 제한을 둘 필요가 없다. 사용자들이 원하는 대로 비밀번호를 설정 할 수 있게 한다. 만약 특별한 보안 정책이 필요 하다면 비밀번호는 최소 12자 이상을 사용하고 최소한 두 글자, 두 자리, 두 가지 특수 문자 이상을 사용하도록 한다.

사용자들에 매 6개월 이상으로 비밀번호를 강제로 변경하도록 하지 않는다. 비밀번호를 자주 바꾸도록 하면 사용자들이 이를 귀찮아해서 간단한 비밀번호를 사용할 가능성이 높아 진다.

해커들이 데이터베이스에 접속 가능하면

해커들이 데이터베이스에 접속 가능하면, 사용자의 비밀번호 해시를 그들이 생성한 해시로 바꾸고 로그인 할 수 있지 않나?

가능하다, 만약 데이터베이스에 접속할 수 있다면 해커들은 아마 서버에 있는 모든 것들에 접근 할 수 있을 것이고 따라서 그들이 필요 하지 않는한 별도로 사용자의 계정에 로그인할 필요는 없다. 암호 해시의 목적은 시스템 전체를 해킹하는것을 방어하는 것이 아니라 비밀번호 해킹이 발생하는 것을 막는 것이다.

데이터베이스의 계정을 사용자 계정을 생성할때 사용할 것과 로그인시 사용할 것을 분리해서 사용하면 로그인시 SQL Injection 공격을 사용해 비밀번호를 변경하는 것을 막을 수 있다.

Why do I have to use a special algorithm

Why do I have to use a special algorithm like HMAC? Why can't I just append the password to the secret key?

Hash functions like MD5, SHA1, and SHA2 use the Merkle–Damgård construction, which makes them vulnerable to what are known as length extension attacks. This means that given a hash H(X), an attacker can find the value of H(pad(X) + Y), for any other string Y, without knowing X. pad(X) is the padding function used by the hash.

This means that given a hash H(key + message), an attacker can compute H(pad(key + message) + extension), without knowing the key. If the hash was being used as a message authentication code, using the key to prevent an attacker from being able to modify the message and replace it with a different valid hash, the system has failed, since the attacker now has a valid hash of message + extension.

It is not clear how an attacker could use this attack to crack a password hash quicker. However, because of the attack, it is considered bad practice to use a plain hash function for keyed hashing. A clever cryptographer may one day come up with a clever way to use these attacks to make cracking faster, so use HMAC.

소금값을 암호 앞, 뒤 어드쪽에 붙여야 되나?

둘 중 아무거나 사용해도 상관 없다. 비밀번호 앞에 사용하는게 좀 더 일반적이긴 하다.

Why does the hashing code on this page

Why does the hashing code on this page compare the hashes in "length-constant" time?

Comparing the hashes in "length-constant" time ensures that an attacker cannot extract the hash of a password in an on-line system using a timing attack, then crack it off-line.

The standard way to check if two sequences of bytes (strings) are the same is to compare the first byte, then the second, then the third, and so on. As soon as you find a byte that isn't the same for both strings, you know they are different and can return a negative response immediately. If you make it through both strings without finding any bytes that differ, you know the strings are the same and can return a positive result. This means that comparing two strings can take a different amount of time depending on how much of the strings match.

For example, a standard comparison of the strings "xyzabc" and "abcxyz" would immediately see that the first character is different and wouldn't bother to check the rest of the string. On the other hand, when the strings "aaaaaaaaaaB" and "aaaaaaaaaaZ" are compared, the comparison algorithm scans through the block of "a" before it determins the strings are unequal.

Suppose an attacker wants to break into an on-line system that rate limits authentication attempts to one attempt per second. Also suppose the attacker knows all of the parameters to the password hash (salt, hash type, etc), except for the hash and (obviously) the password. If the attacker can get a precisise measurement of how long it takes the on-line system to compare the hash of the real password with the hash of a password the attacker provides, he can use the timing attack to extract part of the hash and crack it using an offline attack, bypassing the system's rate limiting.

First, the attacker finds 256 strings whose hashes begin with every possible byte. He sends each string to the on-line system, recording the amount of time it takes the system to respond. The string that takes the longest will be the one whose hash's first byte matches the real hash's first byte. The attacker now knows the first byte, and can continue the attack in a similar manner on the second byte, then the third, and so on. Once the attacker knows enough of the hash, he can use his own hardware to crack it, without being rate limited by the system.

It might seem like it would be impossible to run a timing attack over a network. However, it has been done, and has been shown to be practical. That's why the code on this page compares strings in a way that takes the same amount of time no matter how much of the strings match.


왜 해싱을 지루하게 생각하나?

사용자가 비밀번호를 입력하고 사이트에 로그인할때 이들은 이것이 보안 처리 되어 있을것으로 믿는다. 만약 데이터베이스가 해킹되고 사용자들의 비밀번호가 보호되지 않고 있다면 악성 해커들은 이 정보를 다른 웹사이트와 시스템에 사용할 것이다.(대부분의 사람들이 동일한 비밀번호를 사용한다) 이 문제는 단순히 해당 사이트만의 문제가 아니고 사용자들에 대한 문제이다. 시스템 담당자는 사용자들의 정보를 안전하게 관리해야될 책임이 있다.

관련 문서

  1. 피에이치피(PHP)의 경우 mcrypt_create_ivopenssl_random_pseudo_bytes를 사용하면 된다.