우아한형제들 SOT팀의 권현준님에게 오프라인 보안 특강을 들을 기회가 있었다. 백엔드에서의 보안 뿐만 아니라 클라이언트에서의 보안도 많이 다뤄 유익한 시간이었다.
JWT의 Statelessness
가장 인상적이었던 부분은 Statelessness로 인한 JWT의 취약성이었다.
- 토큰은 Stateless하기 떄문에 서버가 제어할 수 있는 방법이 없다. 따라서 클라이언트에서 임의로 로그아웃시키더라도 공격자가 미리 토큰을 탈취했다면 expired time까지는 권한을 얻게 된다는 것.
- JWT 토큰을 JS를 이용하여 Base64 decoding해서 사용해야 하다보니, 토큰을 쿠키로 받을 경우 httponly 옵션을 추가할 수 없으며, 쿠키가 아닌 경우 브라우저 내장 스토리지를 사용해야 하기 때문에 XSS로 인한 토큰 탈취 가능성이 높아진다.
이러한 단점을 해결하기 위한 조치 방안은 서버에서 JWT Blacklist 구현, JWT Payload 내 stateful한 토큰을 추가를 통해 서버에서 토큰에 대한 제어를 수행할 수 있도록 하는 것이었다. 토큰에 대한 유효성 검증 로직이 서버에 추가됨에 따라 기존 JWT에 비하여 서버 부하가 조금 증가하나, stateful 토큰을 이용한 방식보다는 여전히 부하가 적다는 점에서 유효한 방법이라고 생각되었다.
HTTPS에서의 비밀번호 평문 전송?
질의응답 시간에 체크메이트 서비스 데모데이에서 받은 질문이었던 HTTPS 통신에서 클라이언트에서의 비밀번호 암호화 전송 필요성에 대해 질문했다. 인터넷을 통한 조사에서도 사람마다 의견이 달라 평소에 궁금했는데 보안 전문가의 의견을 직접 들을 수 있어 좋았다. 권현준님의 의견은 ‘SSL 버전만 잘 관리한다면 의미없다. 다만, 개인정보보호법에서 주민등록번호나 비밀번호를 보이면안된다는 조항, 정부에서 작성한 정보 보호 관련 가이드에 평문으로 하지말라는 권고사항이 있기에 이해는 간다.‘였다. 개인적으로도 이미 SSL을 통해 암호화가 되었기 때문에 안전하다고 생각했고, 자바스크립트에 의한 암호화는 공격자에 의해 어떻게든 복호화가 가능하다고 생각해 납득이 갔다.
자바스크립트인 이상 취약하다.
토큰과 같은 보안을 위한 데이터를 자바스크립트의 변수로 저장하는 것에 대해서도 질문했는데, 예상 밖의 답변을 받았다. 모듈, 스코프를 통해 아무리 격리를 하고 로직을 꼬아도 자바스크립트인 이상 공격자가 100% 탈취 가능하다는 것. 컴퓨터의 메모리에 직접 접근하지 않는 이상 알아내는 것이 불가능하다고 생각했던 평소의 믿음이 깨지는 순간이었다. 정확히 어떤 방식으로 알아내는 것인지 궁금해졌다.
시큐어 코딩
법적으로 기업은 연 2회 취약점 진단을 받아야하는데 시큐어 코딩 가이드를 통해 대부분의 취약점을 예방할 것을 권장했다. 화면에 지나가는 글자들을 열심히 옮겨 타이핑했다.
- HTTPS가 적용되어 있다.
- 응답, 오류페이지, 오류메시지 등을 통해 웹서버/WAS 버전정보를 사용자가 확이할 수 없다.
- 중요한 개인정보를 URI에 포함시켜 전달받지 않는다.
- 세션 또는 토큰에 대한 발급/갱신/만료 로직이 명확히 구현되어 있다.
- 세션 또는 토큰에 대한 서버에서의 임의 만료 로직이 구현되어 있다.
- 권한에 따른 메뉴 접근, 본인 인증 등 검증이 필요한 모든 로직은 프론트엔드가 아닌 백엔드에 구현되어 있다.
- 모든 권한에 대한 검증은 발급한 세션 또는 토큰을 이용하고 있다.
- 등등… 17개 정도의 체크리스트가 있었다.
가장 중요했던 항목은 세션 또는 토큰에 대한 서버에서의 임의 만료 로직 구현, 권한에 대한 메뉴 접근, 본인 인증 등 검증이 필요한 모든 로직은 백엔드에 구현, HTML Entity 인코딩이었다. 클라이언트에서는 Sanitization을 통해 해결할 수 있을 듯.
참고
- 우아한 테크 세미나 - 개발자가 꼭 알아야 할 애플리케이션 보안
- 우아한 형제들 기술블로그 - Actuator 안전하게 사용하기
- OWASP Top Ten OWASP에서 3~4년 주기로 발표하는 가장 위협적인 웹 취약점 10개 항목