CODEOK.NET

Page history of 패스워드 보안의 기술



Title: 패스워드 보안의 기술 | edited by Youngrok Pak at 9 years, 2 months ago.

회원가입을 받는 인터넷 서비스를 개발할 때 사용자의 패스워드를 어떻게 보관할 것인가? 제일 쉬운 방법은 물론 페이스북 로그인을 붙이고 패스워드 보안 같은 건 페이스북님이 알아서 해주세요 하는 것이다. 인증의 아웃소싱은 보안 관점에서 서비스 제공자, 사용자에게 모두 유익한 방법이다. 구글, 트위터, 링크드인 등을 다 연동시켜서 선택할 수 있게 하면 의존성 리스크도 조금은 덜어낼 수 있다. 이 글의 주제와 걸맞지 않지만, 정말로 회원 인증은 소셜 로그인이 더 낫다.

그럼에도 불구하고 직접 회원인증을 해야하는 상황이 없지 않은데, 그럴 때 중요한 문제로 떠오르는 것이 보안이고, 그 보안 문제 중에서 한국의 인터넷 서비스들이 오랫동안 잘못해왔으며 지금도 많은 서비스들이 잘못하고 있는 것이 패스워드의 보안이다. 아직도 외부 업체 컨설팅을 다니다보면 패스워드를 잘못 저장하는 경우를 심심찮게 보고, 패스워드 입력 인터페이스만으로도 패스워드를 잘못 저장하고 있는 것으로 추정되는 사이트들이 보인다.

그래도 몇년 전 md5 파동 이후 패스워드 보안에 대한 지식이 개발자 사이에 많이 퍼져서 지금은 올바른 패스워드 저장 방식을 사용하는 개발자들이 많다. Django 같은 프레임웍에서는 기본으로 강력한 패스워드 저장 방식을 채택하고 있기 때문에 본의 아니게 안전하게 저장하고 있는 개발자도 많다. 그렇지만, 왜 그런 방식이 안전한지, 기존에 사용되던 방식은 왜 취약한지, 현재 사용하는 방식의 한계는 뭔지 등등에 대해 깊이 알고 있는 개발자는 드물다. 그래서, 이번 글을 기획했다. 이 글에서는 다음과 같은 내용을 다룬다.

  • 패스워드를 해싱해야 하는 이유
  • 왜 양방향 암호화는 안되는가
  • md5를 비롯한 단방향 해싱은 어떻게 뚫리는가
  • salt는 무엇이며 어떤 도움을 주는가
  • 권장할 만한 패스워드 저장 방식

다루는 내용에서 보다시피 이 글은 정답을 알려주기 위한 실용적인 글이 아니라, 왜 그 정답이 도출되는지 궁금해하는 개발자의 호기심을 충족시켜주기 위한 글이다. 실용적인 목적으로 들어온 독자는 중간을 뛰어넘고 본문 마지막 절을 읽으면 된다.

패스워드를 암호화해야 하는 이유

패스워드를 저장하는 올바른 방법은 이제 알았다. 그런데 왜 저렇게 복잡하게 저장해야 할까? 패스워드를 평문으로 저장하면 왜 안되는가? 알기 쉽게 설명한 패스워드 암호화에 잘 설명되어 있는데, 질문에 대한 답만 간단히 요약한다면 서버의 데이터베이스가 털렸을 때도 회원의 패스워드를 알 수 없게 만들기 위한 것이다. 패스워드가 잘 해싱되어 있으면 해커가 그 해시 값을 보더라도 사용자의 계정으로 로그인할 수 없기 때문에 해커에게 의미 없는 정보가 된다.

데이터베이스를 잘 지키면 되지 않나?

그럼 여기서 의문이 하나 생긴다. 서버를 안 뚫리게 하면 되지 않나? 패스워드를 안전하게 저장하려는 노력을 기울이는 대신, 서버 보안을 신경 쓰는 게 더 좋지 않을까? 원천봉쇄가 더 좋은 것은 당연하다. 하지만, 문제는 그 원천봉쇄가 쉽지 않다는 것이다. 서버는 보안 위험 요소가 너무 많아서 완벽하게 지키는 것이 어렵다. 게다가, 실제로 서버가 해킹 당하지 않아도 데이터베이스는 털릴 수 있다. 개발자가 데이터베이스를 백업한 덤프 파일을 옮기는 과정에서 실수로 SSL이 아닌 통신을 사용한다거나, 메일 따위로 주고 받는다거나, 로컬로 내려받아놓고 자리를 비운 사이 PC에 다른 사람이 접근한다거나 등등. 물론, 이런 초보적인 수준의 실수는 충분히 통제할 수 있다고 생각할 수 있을지 모른다. 그런데, 만일 개발자가 악의적인 의도를 가지고 패스워드를 본다면? 자기가 개발하는 사이트에 옛날 여자친구가 가입했다는 사실을 알고 그 여자친구의 패스워드를 알아낸 다음 그걸로 페이스북에 로그인을 시도한다면? 개발자들의 도덕성이 충분하다고 한들, 그런 가능성이 열려 있는 웹사이트에 가입하고 싶겠는가. 그래서, 원천봉쇄보다는, 데이터가 털리더라도 패스워드가 유출되지 않게 저장할 필요가 있는 것이다.

양방향 암호화

md5, sha1 등의 해싱 알고리즘이나 AES 같은 양방향 암호화를 둘다 묶어서 암호화라고 부르기도 하고, 해싱은 암호화가 아니고 양방향 암호화만 암호화라고 하기도 하는데, 암호화의 목적이 반드시 복호화인 것은 아니므로 굳이 해싱을 암호화가 아니라고 할 필요는 없다. 영어권에서도 해싱과 관련해서 encryption, cryptocraphic hash function 등의 표현을 쓴다. 아무튼, 패스워드를 암호화해야 한다면 AES처럼 복호화가 가능한 방식으로 할 수도 있을 텐데, 왜 굳이 복화하가 불가능한 단방향 암호화를 하는 것일까?

그 이유는 패스워드를 평문으로 저장하지 않는 이유와 같다. 양방향 암호화는 알고리즘과 키값만 노출되면 바로 복호화가 가능하다. 서버가 해킹 당한 상황이면 소스코드도 다 노출된 상황이므로 알고리즘과 키값 모두 노출된 상황이나 마찬가지다. 그리고 역시 위에서 언급한 것과 마찬가지로 내부 개발자는 이미 알고리즘과 키, 데이터베이스에 모두 접근이 가능하므로 원하면 언제든 사용자의 패스워드를 볼 수 있다. 그래서, 패스워드 보안에서 양방향 암호화는 별 의미가 없다. 평문으로 저장한 것과 별반 다르지 않은 보안 수준이라고 할 수 있다.

원래 양방향 암호화는 데이터 보관의 보안보다는 통신 상황에서의 보안에 더 유용한 암호화다. 데이터 보관의 보안도 의미가 있으려면 키값을 보안을 유지하고 싶어하는 사람만 갖고 있어야 하므로 서버의 키값으로 양방향 암호화를 하는 것은 데이터 보관의 보안에서는 별 의미가 없다. 최근 이슈가 되었던 카톡의 보안 문제도 간단치 않은 게, 단순히 서버의 키값으로 암호화를 하면 검찰이 데이터를 요구할 때 복호화해줄 수 있으므로 의미가 없다. 그래서 클라이언트끼리 키값을 주고 받고 그 키값으로 암호화해서 통신해야 하기 때문에 생각처럼 단순한 문제는 아니다.

해시의 크랙

Brute force attack

해시는 단방향 암호화이긴 하나, 기본적으로 모든 해시는 brute force로 뚫을 수 있다. 다만, 그 시간이 엄청나게 오래 걸리게 만들어서 뚫기 어렵게 만들 수 있을 뿐이다. md5가 보안 목적으로 적합하지 않은 것은 일단 기본적인 brute force로도 꽤 빠른 시간에 뚫린다는 것이다. New 25 GPU Monster Devours Passwords In Seconds에 따르면 md5의 경우 25개의 GPU로 초당 1800억개의 대입이 가능하다. 단순히 영문 대소문자, 숫자만 조합한 8자리 고정 패스워드라면 62^8개의 조합이 가능한데,  이 정도면 62^8 / 1800억 = 1213초, 불과 20분이면 다 크랙되는 것이다. 그래도 10자 이상이면 두어 달 걸리니까 조금 더 안전하고, 특수문자도 조합하면 더 안전해지지만, 어쨋든 단순 무식 공격으로도 꽤나 위험한 것이다.

Rainbow table

그럼 만약에 해시 알고리즘을 엄청나게 복잡하게 만들어서 오래 걸리게 하면 brute force 공격으로부터 안전해지지 않을까? 이를테면 SHA-512의 경우는 앞서의 공격으로도 초당 364000개 밖에 대입하지 못한다고 한다. 물론 이것도 엄청나지만 앞서의 8자리 영문자 숫자 조합의 경우 7만년 쯤 걸린다. 이 정도면 안전하지 않을까?

그렇지 않다. Brute force attack을 가만히 생각해보면 더 좋은 방법을 생각해낼 수 있다. 미리 가능한 패스워드 조합을 다 계산한 테이블을 가지고 비교만 수행하는 것이다. 이것이 dictionary attack인데, 이 dictionary를 해시값 검색에 최적화시킨 것을 rainbow table이라고 한다. md5의 경우는 인터넷에 이미 수백억 개의 해시값에 대한 rainbow table이 있다. 이미 계산된 값을 이용하므로 알고리즘의 복잡도도 큰 상관이 없다. 이 안에 있는 패스워드는 그냥 1초 이내에 뚫리는 것이다. 공개된 md5 rainbow table로 찾으면 대부분의 웹사이트에서 90% 정도의 사용자 패스워드가 크랙된다고 하니 이쯤되면 md5는 이미 뚫려 있다. 

물론 rainbow table을 만드는데도 시간이 많이 들기 때문에 알고리즘의 복잡도가 전혀 상관이 없는 것은 아니다. 

Collision attack

하지만 사실 md5가 broken 판정을 받은 것은 brute force 때문이 아니다. md5는 1996년에 이미 collision 취약점에 대한 이론적인 가능성이 제시되었고, 이후에 그게 현실로 나타났다. collision은 서로 다른 두 원본 메세지가 같은 해시값을 갖는 경우를 말한다. 해시 함수의 특성상 collision은 존재할 수 밖에 없는데, 이런 collision을 찾기 힘든 특성을 collision resistance라고 하며 이것은 암호화 목적의 해시에 필수 요소다. 그런데, md5는 그 collision resistance가 낮아서 collision을 쉽게 찾을 수 있는 것이다. 위의 링크에서 그 collision의 예를 볼 수 있다.

Preimage attack

해시 알고리즘을 패스워드 암호화에 쓸 수 있는지 판단하는 기준은 preimage attack인데, 이것은 해시값에서부터 원본 데이터를 찾아낼 수 있는 가능성이다. 이건 md5를 포함한 대부분의 해시 함수가 안전하기 때문에 크게 걱정할 필요는 없으나, 암호학을 모르는 사람이 직접 만든 해시 함수를 사용하지 말아야 하는 이유가 되긴 한다. 

안전한 패스워드 저장 방법

해시에 대한 다양한 크랙 수단이 있지만, 그 중 가장 효율적인 수단은 물론 rainbow table이다.

Wiki at WikiNamu