Written by Whemoon Jang.

Jang's Blog

로그인할 때 패스워드를 해싱해서 보내는 것은 보안에 아무런 도움이 안됩니다

December 29, 2021

이 글은 법적이나 도덕적으로 어째야 한다 저째야 한다 하는 당위에 관한 내용이 아닙니다. 기술적으로 패스워드가 누구의 책임 하에 들어가느냐(즉 누가 짠 코드 위에서 움직이느냐) 하는 이야기입니다.

어떤 사람들은 로그인할 때 자바스크립트로 브라우저에서 패스워드를 해싱해서 보내는 것이 안전하다고 주장하고 실제로 그렇게 하는 웹 사이트도 종종 있습니다. 그러나 이는 대부분 오래되어서 더이상 맞지 않거나 비현실적인 가정을 바탕으로 한 것으로, 대개의 경우 보안에 아무런 도움이 안됩니다. 다음은 몇가지 흔한 오해와 그 정정입니다.

해싱하지 않은 패스워드 로그인은 평문 전송이다?

https를 사용하지 않는다면 그렇습니다. 그러나 https를 사용한다면 평문전송이 아닙니다. (루트 인증서가 변조되지 않았다는 가정하에) https를 사용한다면 서버 주인이 아닌 다른 누군가가 중간에 패킷을 가로채서 패스워드를 알아내는 것은 (현재까지 알려진 바로는) 불가능합니다.

덧붙여, 누군가가 도청하는 상태에서 평문전송을 한다면? 도청자는 그냥 사용자와 서버 간에 오가는 정보를 확인하고 그 인증정보로 로그인을 할 수 있으니까 해싱을 하든 말든 웹사이트의 보안에는 아무런 차이가 없습니다.

해싱하지 않은 패스워드를 보내면 웹 사이트 관리자가 패스워드를 알 수 있다?

패스워드를 해싱해서 보내든 평문으로 보내든 로컬에서만 갖고있든 (혹은 그렇다고 주장하든) 사용자가 이를 컨트롤할 수 있는 방법이 없으므로 ‘사용자가 웹 사이트에 패스워드를 입력한 그 순간’부터 웹 서비스 시스템 자체는 패스워드를 알고 있다고 가정해야 합니다.

자주 망각되는 사실이지만 사용자가 어떤 정보를 웹 사이트에 입력할 때부터 그 정보는 전적으로 웹 사이트의 책임 하에 들어가게 됩니다. 웹 사이트의 코드는 사용자의 컴퓨터에서 실행되니까 일견 사용자가 컨트롤한다는 인상을 주지만 조금만 생각해보면 전혀 그렇지 않다는 것을 알 수 있습니다. 웹 사이트 (다른 말로 프론트엔드) 는 독립된 컴포넌트가 아닙니다. TLS 암호화, 쿠키 정책, CORS, 자바스크립트 코드 등 사이트의 모든 부분은 서버에서 컨트롤합니다. 인증 정보를 브라우저에서 해싱해서 보내든 그냥 실어 보내서 서버에서 해싱하든 구현만 달라질 뿐 누가 진짜로 패스워드를 갖고 있느냐는 달라지지 않습니다.

사용자가 입력한 정보가 서버로 가는지, 어떻게 이용되는지, 인간 관리자가 패스워드를 알 수 있는지 등등은 온전히 서버 측의 책임/권한이고 사용자가 통제할 수 있는 부분이 아닙니다. 이것들은 물론 중요한 이슈지만 법률과 시스템의 문제고, 브라우저에서 패스워드 해싱은 여기에 아무런 보완을 해주지 못합니다.

브라우저에서 패스워드를 해싱하면 사전 공격, 브루트 포스 등의 공격에 더 잘 버틴다?

해시 함수의 결과값은 probability space에 uniform하게 분포하니까 해싱을 하면 브루트 포스 공격에 더 잘 버틸까요? 당연히 아닙니다. 해시 함수는 deterministic function이기 때문에 해싱을 한다고 shannon entropy가 늘어나지 않습니다.

쉬운 말로 하자면, 사전 공격이나 브루트 포스는 어차피 패스워드는 거기서 거기라는 점을 이용한 공격인데, 공격자는 똑같이 공격하되 서버로 보낼 때 해싱만 하면 되기 때문에 해싱은 이런 데 별 도움이 안됩니다.

뒤에서 훔쳐보기, 브라우저에 저장된 패스워드 해킹하기, 키 로거, 메모리 덤프, 피싱 사이트 등등 패스워드를 직접 노린 공격들 대부분 마찬가지입니다. 여기에 브라우저에서 패스워드 해싱은 아무런 도움이 안됩니다.

브라우저에서 패스워드 해싱을 하면 안되는 이유

제 생각에 브라우저에서 패스워드를 해싱해서 보냈을 때 도움이 되는 유일한 시나리오는 이렇습니다.

  1. 패스워드를 포함한 개인정보가 서버 측 로그, 데이터베이스 등 영속성 레이어에 원문으로 노출되는 상태에서
  2. 악성 공격자가 이러한 개인정보가 포함된 영속성 레이어에 접근 가능한 상태에서
  3. 하나의 패스워드를 돌려 쓰는 사용자의 패스워드 원문을 이용해 다른 사이트에 로그인을 시도해(credential stuffing 이라고 부릅니다) 다른 정보를 탈취할 경우

위 사례의 경우 만약 패스워드를 브라우저에서 해싱해서 보냈다면 패스워드 원문 하나만큼은 지켜서 credential stuffing은 막을 수 있었겠지만, 보시다시피 다른 개인정보 유출이 우려되는 총체적 난국에서 패스워드 유출만 다행히 안되었다고 해서 뭐가 면책될 일은 없습니다.

위와 같이 보안에 별 도움은 안되는 반면 패스워드 해싱같은 중요한 프로세스를 사용자의 브라우저에서 실행하지 말아야 할 이유는 많습니다.

  • 해시함수는 완전무결하지 않습니다. 나중에 충돌 공격 기법이 개발될 수도 있고, 브라우저 구현에 버그가 있을 수도 있습니다. 클라이언트에서 해싱을 하면 나중에 이런 문제가 생겼을 때 업데이트하기가 훨씬 힘들어집니다.
  • 브라우저 자바스크립트 코드는 사용자에게 고스란히 노출이 되므로 솔트(salt)가 의미가 없어지고 해시함수의 종류가 노출됩니다.
  • 서버 단에서 패스워드 원문을 받지 못하면 유효성을 확인할 수가 없게 됩니다. 빈 문자열, 공백문자, 짧은 패스워드, 흔한 패스워드가 사용되더라도 이를 막는 보안규칙을 세울 수가 없게 됩니다.

그러니 보안이 특별히 중요한 경우라도 브라우저에서 이런 조치가 필요할지는 숙고하여 결정해야 하겠습니다.


copyright(c) 2022 Whemoon Jang. All right reserved.
disclaimer