Risan Bagja

Laravel AJAX Request dengan CSRF Token

Jika Anda menggunakan Laravel dan mengkombinasikan teknik AJAX untuk mengirim request ke server, pasti Anda pernah memikirkan: bagaimana caranya memproteksi endpoint AJAX tersebut dari serangan CSRF?

AJAX Form

Sebagai contoh, kita memiliki sebuah laman untuk mengirimkan pesan ke server dengan teknik AJAX:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Ajax Form</title>
    <meta name="csrf-token" content="{{ csrf_token() }}">
  </head>
  <body>
    <input type="text" id="message">
    <button type="button" id="send">Send</button>

    <script src="//code.jquery.com/jquery-1.10.2.min.js"></script>
    <script src="{{ asset('ajax-setup.js') }}"></script>
    <script src="{{ asset('ajax-handler.js') }}"></script>
  </body>
</html>

Setup AJAX Request

Pada kode di atas, kita tidak menyisipkan csrf_token di dalam hidden input seperti biasanya; melainkan pada meta tag dengan nama csrf-token. meta tag inilah yang akan digunakan pada file ajax-setup.js:

$(function() {
  $.ajaxSetup({
    headers: {
      'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
      }
  });
});

Pada ajax-setup.js inilah kita mengkonfigurasi metode $.ajax() agar selalu menyertakan HTTP header tambahan bernama X-CSRF-Token. Nilai dari header X-CSRF-Token ini diambil dari tag <meta "csrf-token"> yang telah kita sertakan sebelumnya.

Sementara file ajax-handler.js akan berisi kode Javascript yang Anda gunakan untuk mengirimkan request ke server:

$(function() {
  // On button click, send AJAX request
  $("#send").click(sendMessage);

  // Send AJAX request
  function sendMessage() {
    $.ajax({
      url     : "http://example.com/ajax-post",  // Request endpoint
      type    : "POST",                          // HTTP verb
      data    : { message: $("message").val() }, // Data to be sent
      success : function(data) {
        // Do something with returned response data
      }
    });
  }
});

CSRF Filter

Dan terakhir kita harus memodifikasi filter CSRF yang secara default telah disediakan oleh Laravel:

Route::filter('csrf', function() {
  if (Request::ajax()) {
    // If it is an AJAX request, use X-CSRF-Token meta
    $token = Request::header('X-CSRF-Token');
  } else {
    // Or else, use a basic hidden input which hold CSRF token
    $token = Input::get('_token');
  }

  // If CSRF token is mismtach, throw an exception
  if (Session::token() != $token) {
    throw new Illuminate\Session\TokenMismatchException;
  }
});

Perhatikan, jika request yang diterima server adalah AJAX, maka CSRF token akan diambil dari HTTP header dengan key yang bernama: X-CSRF-Token:

if (Request::ajax()) {
  $token = Request::header('X-CSRF-Token');
}

Epilog

Tentu ini hanyalah satu dari beragam cara untuk melindungi AJAX request dari serangan CSRF. Anda tentu bisa tetap menggunakan cara tradisional: menyisipkan CSRF token pada hidden input atau langsung menyertakannya pada parameter data dari metode $.ajax(). Namun saya pribadi merasa cara inilah yang paling elegan.