NodeJS 에서 CSRF 웹 세션 공격을 막기 위한 대책 및 방법 - Whitmem
NodeJS 에서 CSRF 웹 세션 공격을 막기 위한 대책 및 방법
NodeJS 개발
2025-05-19 23:31 게시 37f6ccacfa454274f27b

0
0
35
이 페이지는 외부 공간에 무단 복제할 수 없으며 오직 있는 그대로 게시되며 부정확한 내용을 포함할 수 있습니다. 법률이 허용하는 한 가이드 라인에 맞춰 게시 내용을 인용하거나 출처로 표기할 수 있습니다.
This page is not to be distributed to external services; it is provided as is and may contain inaccuracies.
항상 퇴근하면 집에서 개발하는 것이 이 블로그의 댓글 시스템인데, 보안과 안정성에 깊게 신경을 써서 개발하고 있다. 아무래도 심도 있게 개발을 하다보니 노트북과 같은 화면이 작은 환경에서는 개발할 수가 없어, 대형 모니터로 편안한 환경에서만 작업을 고수하고 있다...
아무튼 서론을 뒤로하고, CSRF 공격은 브라우저의 신뢰 상태를 이용한 악의적인 공격 요청이다.
보통 우리가 웹 사이트를 만들면 다음과 같은 라우팅 정보를 생성할 수 있다.
POST /register/ - 회원가입 POST /unregister/ - 탈퇴 POST /logout - 로그아웃
보통은 위와 같은 request 용 응답 경로를 만들고, Ajax 나 메인 공간에서 Form으로 발송하는 식으로 구현할 수 있다.
<form action="/logout" method="POST"> <input type="submit"> </form>
보통 사용자가 로그인하는 경우 로그인 정보는 세션 및 쿠키에 저장되어, 사용자의 별 다른 입력 없이도 세션이 유지된다면 클릭만으로 서비스를 사용할 수 있다.
하지만 위와 같은 방법에는 심각한 보안 문제가 존재한다.
POST /logout - 로그아웃
<form action="/logout" method="POST"> <input type="submit"> </form>
분명 /logout 은 POST로 요청을 받은 경우에만 수행된다. 하지만 <form> 태그는 반드시 본인 사이트가 아니라, 다른 사이트를 출처로 하여금 내 사이트에 요청이 발생할 수 있다. 즉 다른 사이트에서 action 에 상대 경로가 아닌 절대 경로 형식으로 URL 을 넣으면 외부 사이트에서 넘어와 작업을 수행할 수 있게 된다.
따라서 위와 같은 상황에서는 보통 Referer 을 검증하여 어떤 사이트를 출처로 접근 했는지 필터링하는 작업이 필요하다.
즉 미들웨어 어딘가에 req 를 읽여들여 headers 의 referer을 읽어 원하는 사이트로부터 출처가 발생하지 않은 경우 접근을 차단하는 것이 일차적인 방법이다.
기본적으로 웹 브라우저는 POST 나 GET 등을 발송할 때 어떤 사이트에서 출처로 발생했는지 자체적으로 Referer을 포함하여 발송한다. 이 값은 정상적인 브라우저의 경우 쿠키에서 별도로 변조할 수는 없다. 즉 일반적인 사용자의 경우에는 Referer을 기반으로 차단할 수 있다. 그 외 Referer을 조작하는 경우는 애초 클라이언트, 즉 접속자가 고의로 바꾼 경우이기 때문에 사용자도 해당 위험을 감수하거나 알고 있는 것이다.
두 번째 방법은 CSRF 방지용 토큰을 만들어 인증을 하는 방법이다.
위 문제는 결국 단일 일회성 요청을 리퀘스트 보냄으로써 발생하는 것이기 때문에, 리퀘스트할 때 파라메터로 추가적인 정보를 요구하는 경우 외부 사이트에서 해당 파라메터를 예측할 수 없어 악용을 방지할 수 있다.
즉 A 라는 정상적인 사이트가 있을 때, A 사이트에서 CSRF 용 토큰을 발급해 사용자에게 전송하고, A 사이트의 form 문에서 logout request를 하고자 할 때 발급받은 CSRF 토큰을 같이 전송해 서버에서 유효한지 확인하는 방법이다.
이 때 주의해야 할 것이 단순히 토큰만 랜덤으로 발송해서 토큰이 존재하는지 검증해서는 안된다. 악의적인 서버에서 리퀘스트 형태로 토큰을 강제로 생성한 뒤 form 태그에 같이 포함할 수 있기 때문이다.
따라서 브라우저 세션에 토큰을 저장하고, 해당 세션 + 파라메터를 입력받아 두 값이 동일한지 확인하는 방법을 사용해야 한다.
보통은 CSRF 을 방지하는 스크립트 또는 라이브러리를 제공하지만, 여기서는 직접 구현을 해 본다.
우선 토큰을 임의로 만들고 관리하는 부분이 필요하다.
브라우저가 실제 토큰 생성을 요청하는 경우, 실질적으로 토큰을 랜덤 발급한다. 그리고 해당 토큰에 대해 사용자 세션에도 넣어준다.
그러면 사용자 브라우저의 세션 공간에도 토큰이 있고, 서버에도 토큰이 유효하게 저장된다. 이제 이 토큰을 브라우저 클라이언트에도 별도로 전송한다. 이는 이제 Form 문 등 안에 hidden으로 삽입하는 것이다.
그리고 특정 리퀘스트 등을 처리하는 서버단에서 1. 사용자의 세션에 토큰이 존재하는지 확인하고, 2. 사용자가 별도로 보내온 파라메터의 토큰 값을 읽고, 3. 사용자가 별도 보내온 파라메터 값과 세션에 저장된 토큰이 둘다 동일하게 유효한지 확인한다.
그러면 다른 서버에서 이 페이지를 악의적인 리퀘스트를 날리고자 할 때, 서버가 발급한 토큰을 모르기 때문에 파라메터에 토큰을 채워서 보낼 수가 없다. 즉 요청이 거부 된다.
악의적인 서버에서 임의로 토큰을 만든다고 해도, 해당 토큰은 서버 내에서만 유효한 토큰이지다른 사용자에게 전달해서 리퀘스트에 담는다고 하더라도, 아이피, 세션 정보가 달라 토큰이 유효하지 않음을 검증할 수 있다.
댓글 0개
댓글을 작성하는 경우 댓글 처리 방침에 동의하는 것으로 간주됩니다. 댓글을 작성하면 일회용 인증키가 발급되며, 해당 키를 분실하는 경우 댓글을 제거할 수 없습니다. 댓글을 작성하면 사용자 IP가 영구적으로 기록 및 부분 공개됩니다.
확인
Whitmemit 개인 일지 블로그는 개인이 운영하는 정보 공유 공간으로 사용자의 민감한 개인 정보를 직접 요구하거나 요청하지 않습니다. 기본적인 사이트 방문시 처리되는 처리 정보에 대해서는 '사이트 처리 방침'을 참고하십시오. 추가적인 기능의 제공을 위하여 쿠키 정보를 사용하고 있습니다. Whitmemit 에서 처리하는 정보는 식별 용도로 사용되며 기타 글꼴 및 폰트 라이브러리에서 쿠키 정보를 사용할 수 있습니다.
이 자료는 모두 필수 자료로 간주되며, 사이트 이용을 하거나, 탐색하는 경우 동의로 간주합니다.