[CakePHP] CSRF token mismatch가 발생했을 때 처리하는 방법


Development note/PHP  2019. 10. 5. 09:00

안녕하세요. 명월입니다.


이 글은 CakePHP에서 CSRF token mismatch가 발생했을 때 처리하는 방법입니다.


PHP에서 Cake 프레임워크를 사용할때 Post Request를 보내면 발생하는 에러입니다.

이 에러는 routes.php를 보면 csrf 미들웨어를 등록되어 있는 것을 보실 수 있습니다.

사실 저 미들웨어 등록하는 부분을 주석처리하면 에러가 사라집니다. 정확히는 csrf 미들웨어를 사용하지 않는 형태로 되는 것입니다.

그럼 이 csrf 미들웨어가 무엇인지를 알아야 사용여부를 판단할 텐데, 이게 크로스-사이트 요청 위조 공격을 보호하는 미들웨어입니다.

다른 도메인에서 우리쪽 도메인으로 메소드는 POST로 form값을 넣고 점프하는 것을 막으려고 하는 것인데.. 이런 방식이 크게 도움이 되나 봅니다. 저는 개인적으로 잘 모르겠네요.. 결국 쿠기 가져가면 크로스-사이드 요청이 될 텐데 말입니다.


위 csrf 미들웨어를 등록 해제를 하지 않았다고 생각했을 때 사이트에 접속하면 csrfToken이라는 쿠키를 볼 수있습니다.

저 값을 POST의 form 데이터에 _csrfToken의 이름으로 포함시키면 저 에러가 발생하지 않습니다.


먼저 csrf 미들웨어를 사용한다고 생각한다면 저 쿠키값을 사용할 수 있게 view로 넘기기 전인 AppView에서 값을 설정합니다.

...
protected function _evaluate($viewFile, $dataForView) {
  foreach ($dataForView as $key => $val) {
    $this->_smarty->assign($key, $val);
  }
  $this->_smarty->assignByRef('this', $this);
  if(@$dataForView["error"] !== null){
    return parent::_evaluate($viewFile, $dataForView);
  }
  // request의 쿠키 값을 취득한다.
  $_csrfToken = $this->getRequest()->getCookie("csrfToken");
  // 그러나 페이지를 처음 방문시에는 쿠키가 설정이 되지 않으니 Set-Cookie의 해더값으로 데이터를 가져온다.
  if($_csrfToken === null){
    $_csrfToken = $this->getResponse()->getCookie("csrfToken")["value"];
  }
  // template에서는 _csrfToken로 사용한다.
  $this->_smarty->assign("_csrfToken", $_csrfToken);
  return $this->_smarty->fetch($viewFile);
}
...

그리고 폼 값안에 저 토큰 값을 넣으면 없어집니다.

<form method="post">
  <input type="text" name="data">
  <!-- 토큰을 넣는다. -->
  <input type="hidden" name="_csrfToken" value="{$_csrfToken}"/>
  <input type="submit">
</form>

CSRF token mismatch 에러가 사라졌습니다. 이 예제는 제가 form으로 submit했을 때 대응 방법이고 ajax에서 post타입일 경우도 아마 이 CSRF token mismatch가 나올 것입니다.

사실 이 때도 데이터에 _csrfToken값만 넣으면 해결됩니다.

<input type="text" id="data"><br />
<input type="text" id="data1">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
  $(function() {
    $.ajax({
      type: "POST",
      url: "/Ajax/index",
      dataType: "json",
      success: function(msg) {
        $("#data").val(msg.data);
        $("#data1").val(msg.data1);
      }
    });
  });
</script>

역시나 발생합니다. 이번에는 한개의 페이지가 아닌 레이아웃 페이지(default.ctp)로 가서 토큰 키를 넣겠습니다.

<!DOCTYPE html>
<html>
<head>
  <title>Example</title>
</head>
<body>
  <!-- 토큰키를 넣습니다. Dom의 id의 csrfToken입니다.-->
  <input type="hidden" id="csrfToken" value="{$_csrfToken}" >
  {$this->Flash->render()}
  <div class="container clearfix">
    {$this->fetch('content')}
  </div>
  <footer>
  </footer>
</body>
</html>

그리고 다시 ajax가 있는 소스로 와서 저 토큰 키를 추가합시다.

<input type="text" id="data"><br />
<input type="text" id="data1">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
  $(function() {
    $.ajax({
      type: "POST",
      url: "/Ajax/index",
      // 토큰 키를 추가하도록 합시다.
      data: {
        "_csrfToken" : $("#csrfToken").val()
      },
      dataType: "json",
      success: function(msg) {
        $("#data").val(msg.data);
        $("#data1").val(msg.data1);
      }
    });
  });
</script>

Ajax도 제대로 데이터를 취득합니다.


여기까지 CakePHP에서 CSRF token mismatch가 발생했을 때 처리하는 방법 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.