Cross-site request forgeries adalah jenis eksploitasi berbahaya di mana perintah yang tidak sah dilakukan atas nama pengguna yang terautentikasi. Untungnya, Laravel telah memudahkan perlindungan aplikasi Anda dari serangan cross-site request forgery (CSRF).
Jika Anda belum akrab dengan istilah cross-site request forgery (CSRF), mari kita bahas contoh bagaimana kerentanan ini dapat dieksploitasi. Bayangkan apabila aplikasi Anda memiliki route /pengguna/email
yang menerima request POST
untuk mengubah alamat email pengguna yang terotentikasi. Kemungkinan besar, route ini mengharapkan kolom input email
yang berisi alamat email yang ingin didaftarkan oleh pengguna.
Tanpa perlindungan CSRF, situs web berbahaya dapat membuat formulir HTML yang mengarah ke route /user/email
aplikasi Anda dan mengirimkan alamat email "jahat" milik peretas:
<form action="https://your-application.com/user/email" method="POST">
<input type="email" value="[email protected]">
</form>
<script>
document.forms[0].submit();
</script>
Jika situs web berbahaya dapat mengirimkan formulir secara otomatis ketika halaman dimuat, pengguna jahat hanya perlu memperdaya pengguna (asli) aplikasi Anda yang tidak menaruh curiga untuk mengunjungi situs web mereka, kemudian alamat email mereka pada aplikasi Anda akan tertimpa oleh alamat email "jahat" milik peretas.
Untuk mencegah kerentanan ini, kita perlu memeriksa nilai rahasia sesi pada setiap request POST
, PUT
, PATCH
, atau DELETE
yang masuk. Nilai rahasia yang tidak dapat diakses oleh aplikasi jahat.
Laravel secara otomatis menghasilkan "token" CSRF untuk setiap sesi pengguna yang aktif yang dikelola oleh aplikasi secara otomatis. Token ini digunakan untuk memverifikasi bahwa pengguna yang telah terautentikasi adalah orang yang benar-benar membuat request ke aplikasi. Karena token ini disimpan dalam sesi pengguna dan berubah setiap kali sesi diregenerasi (dihasilkan ulang), aplikasi jahat tidak akan dapat mengaksesnya.
Token CSRF untuk sesi saat ini dapat diakses melalui request milik sesi atau melalui fungsi helper csrf_token
:
use Illuminate\Http\Request;
Route::get('/token', function (Request $request) {
$token = $request->session()->token();
$token = csrf_token();
// ...
});
Setiap kali Anda mendefinisikan formulir HTML "POST", "PUT", "PATCH", atau "DELETE" dalam aplikasi Anda, Anda harus menyertakan kolom _token
CSRF yang tersembunyi pada formulir tersebut sehingga middleware untuk perlindungan CSRF dapat memvalidasi request untuk formulir tersebut. Untuk kemudahan, Anda dapat menggunakan directive @csrf
milik Blade untuk menghasilkan kolom input token yang tersembunyi:
<form method="POST" action="/profile">
@csrf
<!-- Setara dengan... -->
<input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>
Middleware App\Http\Middleware\VerifyCsrfToken
yang telah termasuk di dalam grup middleware web
secara default, akan secara otomatis memverifikasi bahwa token di dalam input pada request telah sesuai dengan token yang tersimpan di dalam sesi. Ketika kedua token ini cocok, maka kita mengetahui bahwa pengguna yang terotentikasi adalah yang pengguna yang melakukan request.
Jika Anda membangun sebuah SPA yang menggunakan Laravel sebagai backend API, Anda sebaiknya mengunjungi dokumentasi Laravel Sanctum untuk informasi tentang autentikasi dengan API dan perlindungan terhadap kerentanan CSRF pada aplikasi Anda.
Terkadang Anda mungkin ingin mengecualikan sekumpulan URI dari perlindungan CSRF. Misalnya, jika Anda menggunakan Stripe untuk memproses pembayaran dan menggunakan sistem webhook mereka, Anda perlu mengecualikan rute handler untuk webhook milik Stripe dari perlindungan CSRF karena Stripe tidak pernah tahu token CSRF apa yang perlu dikirim ke rute Anda.
Biasanya, Anda harus menempatkan rute-rute semacam ini di luar grup middleware web
yang diterapkan oleh App\Providers\RouteServiceProvider
ke semua rute yang terdapat di dalam file routes/web.php
. Atau, Anda juga dapat mengecualikan rute-rute tersebut dengan menambahkan URI tersebut ke properti $except
pada middleware VerifyCsrfToken
:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* URI yang seharusnya dikecualikan dari verifikasi CSRF.
*
* @var array
*/
protected $except = [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
];
}
Catatan
Untuk kenyamanan, middleware CSRF secara otomatis dinonaktifkan pada semua rute ketika melakukan pengujian.
Selain memeriksa token CSRF sebagai parameter POST, middleware App\Http\Middleware\VerifyCsrfToken
juga akan memeriksa header X-CSRF-TOKEN
pada request. Misalnya, Anda dapat menyimpan token di dalam tag meta
HTML:
<meta name="csrf-token" content="{{ csrf_token() }}">
Kemudian, Anda dapat menginstruksikan library seperti jQuery untuk menambahkan token secara otomatis ke semua header untuk request. Hal ini bertujuan untuk memberikan perlindungan CSRF yang sederhana dan nyaman untuk aplikasi berbasis AJAX yang menggunakan teknologi JavaScript lama
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
Laravel menyimpan token CSRF saat ini yang telah terenkripsi di dalam cookie XSRF-TOKEN
yang disertakan pada setiap respons yang dihasilkan oleh framework. Anda dapat menggunakan nilai cookie ini untuk menetapkan nilai X-XSRF-TOKEN
pada header request.
Pada dasarnya cookie ini dikirim untuk kenyamanan pengembang karena beberapa framework dan library JavaScript, seperti Angular dan Axios, secara otomatis menempatkan nilai tersebut di header X-XSRF-TOKEN
untuk asal request yang sama (same-origin request).
Catatan
Secara default, fileresources/js/bootstrap.js
menyertakan pustaka HTTP Axios yang secara otomatis akan mengirimkan headerX-XSRF-TOKEN
untuk Anda.