Rails 6에서 CSRF 대응책

ayokinya·2020년 11월 29일
0

Rails 6에서 제공하는 form으로 POST 요청을 하면 문제가 없는데,
javascript로 POST를 실행하면 can't verify CSRF token authenticity 문구가 뜨면서 데이터 생성이 안 됐다. 이 문제를 해결하기 위해 검색을 하다가 CSRF가 무엇인지 알게 됐다.

CSRF란?

CSRF(Cross-Site Request Forgery)는 사용자가 자신의 의지와는 관계없이 공격자가 의도한 행동을 하여 특정 웹페이지를 보안에 취약하게 한다거나, 수정, 삭제, 등록 등의 작업을 하게 만드는 공격이다.

Rails 6에서 CSRF 대응책

CSRF 대응책에는 여러 가지가 있지만 Rails 6에서는 CSRF Token을 사용하여 CSRF를 방지한다.

CSRF Token은 random number로 우선 사용자 세션 쿠키에 저장된다. 그리고 사용자가 서버에 Request를 할 때 해당 Token을 포함시켜 전송하게 해 세션에 저장된 값과 Request parameter에 담긴 토큰 값을 비교한다. 두 값이 같을 때에만 사용자가 의도한 요청이라고 판단해 CSRF를 방어한다.

Rails 6에서는 application.html.erb에 CSRF Token이 심어져 있다.

<%= csrf_meta_tags %>

이 태그는 HTML로 아래처럼 변환된다.

<meta name="csrf-param" content="authenticity_token">
<meta name="csrf-token" content="Emq8ABYNVXEJAvJ1Z0BCnmD+dI69AHiMQ/gdTyuSMU4eNxPMWMxXNO4cTTTua/zZ1xDQ9uhy/6NyBXEwBeWjQQ==">

그래서 Rails의 기본 form으로 데이터를 전송하면 CSRF Token이 hidden field로 자동으로 추가되어 있다.
하지만 javascript로 데이터를 전송해야 할 때에는 이 메타 태그를 읽어 토큰을 획든한 후, 이 토큰과 함께 요청을 보내야 CSRF 공격이 아니라는 것을 서버에게 증명할 수 있다.
CSRF와 Rails의 CSRF 대응책을 전혀 모르고 CSRF Token 없이 서버에 POST 요청을 해서 제대로 데이터가 생성이 안 된 것이었다.

그래서 meta CSRF Token을 읽어 Request Header에 담아 요청을 보낼 수 있게 하는 코드를 찾아 해결했다!

$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
  var token;
  options.xhrFields = {
    withCredentials: true
  };
  token = $('meta[name="csrf-token"]').attr('content');
  if (token) {
    return jqXHR.setRequestHeader('X-CSRF-Token', token);
  }
});

이 코드 말고도 CSRF Token을 읽어 Request에 잘 담아주기만 하면 Rails 6에서 can't verify CSRF token authenticity가 뜨는 일은 없지 않을까?



[참고 자료]
https://flearning-blog.tistory.com/42
https://gist.github.com/gcollazo/1240683

profile
42 서울 교육생

0개의 댓글