CSRF và CSRF Protection trong Laravel
Bài đăng này đã không được cập nhật trong 9 năm
Trong blog này, chúng ta sẽ có cái nhìn rõ ràng hơn về tấn công CSRF, CSRF Protection trong Laravel và so sánh sự khác nhau giữa CSRF filter trong Laravel và VerifyCsrfToken middleware trong Laravel 5.
I. Tổng quan về CSRF
- CSRF là gì?
CRSF (Cross Site Request Forgery) còn được gọi là "Session riding", "XSRF" là kĩ thuật tấn công bằng cách sử dụng quyền chứng thực của người sử dụng đối với một website khác (tấn công giả mạo). Các ứng dụng web hoạt động theo cơ chế nhận các câu lệnh HTTP từ người sử dụng, sau đó thực thi các câu lệnh này.
Hacker sử dụng phương pháp CSRF để lừa trình duyệt của người dùng gửi đi các câu lệnh HTTP đến các ứng dụng website. Trong trường hợp phiên làm việc của người dùng chưa hết hiệu lực thì các câu lệnh trên sẽ được thực hiện với quyền chứng thực của người dùng.
Tôi sẽ lấy 1 ví dụ về csrf cho các bạn dễ hình dung: Gỉa sử hệ thống của bạn có 1 action xóa bài viết theo url sau youwebsite.com/admin/post/{id}/delete. Như vậy nếu có một người nào biết được URL này thì họ sẽ hack được, và họ sẽ lợi dụng chính admin của hệ thống. Họ có thể gửi cho bạn một email với nội dung là một hay nhiều thẻ img với src chính là url đó và mỗi hình có 1 id khác nhau, như vậy nếu admin đọc cái mail đó và đang login vào hệ thống thì admin đã vô tình xóa đi các post với id như trong các src của thẻ img trên.
- Cách phòng chống
Để ngăn chặn CSRF, có hai cách thường được sử dụng:
- Sử dụng token: Phương pháp thứ nhất là chèn thêm token vào đường link thực hiện thao tác. Giá trị của token phải mạnh, khó đoán, thường là hash của session ID của user kết hợp với password, IP của user, thời điểm đăng nhập,...Giá trị của biến token được sinh ra ngẫu nhiên hoặc tùy theo thuật toán của developer. Mục đích của token là làm cho hacker không thể xác định được chính xác đường link thực hiện thao tác. Như trong ví dụ trên URL sau khi thêm token: youwebsite.com/admin/post/{id}/delete?_token=uZVTLBCWcw33RIhvnbxTKxTxM2rKJ7YJrwyUXhXn
- Chèn thêm xác nhận trung gian trước những thao tác nhạy cảm: Có thể yêu cầu user nhập lại password để xác thực thao tác hoặc yêu cầu nhập captcha. Các dich vụ thanh toán (ngân hàng, chứng khoán) thường yêu cầu user sử dụng thêm OTP(One-time password) hoặc chứng thư số để xác nhận lại các giao dịch.
II. CSRF Protection trong Laravel
Laravel có cơ chế bảo vệ khỏi csrf được kích hoạt mặc định. Vì vậy ngay cả khi bạn không biết gì csrf, tại sao cần bảo vệ các ứng dụng khỏi nó bạn vẫn có thể sử dụng nó khá đơn giản . Laravel tự động tạo một SCRF "token" trong mỗi phiên làm việc của bạn và được quản lý bởi ứng dụng. Chúng ta sẽ tìm hiểu cách sử dụng trong các phiên bản 4 và 5.
- Laravel 4.2
Trong phiên bản 4.x chúng ta có filters. Việc của bạn cần làm chỉ là thêm vào app/filters.php
Route::filter('csrf', function()
{
if (Session::token() !== Input::get('_token'))
{
throw new Illuminate\Session\TokenMismatchException;
}
});
Bạn chỉ cần sử dụng nó ở bất kì route nào bạn muốn:
Route::post('register', array('before' => 'csrf', function()
{
return 'You gave a valid CSRF token!';
}));
Và chắc chắn rằng bạn không quên chèn thêm token trong form của bạn:
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
-
Laravel 5.0
Phiên bản 5.0 cung cấp Middleware thay thế cho filter. Và đã có 1 bộ các class Middleware được load mặc định, bao gồm cả CSRF. trong file app/Http/Kernel.php bạn có thể tìm thấy
class Kernel extends HttpKernel {
protected $middleware = [
'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
'Illuminate\Cookie\Middleware\EncryptCookies',
'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
'Illuminate\Session\Middleware\StartSession',
'Illuminate\View\Middleware\ShareErrorsFromSession',
'Illuminate\Foundation\Http\Middleware\VerifyCsrfToken',
];
Class VerifyCsrfToken đã được include trong $middleware, tức là tất cả các POST request đã được bảo vệ khỏi CSRF. Nhưng chính điều đó lại trở thành vấn đề. Rõ rang là không phải tất các POST request đều đến từ một form nào đó, có thể nó được gọi từ 1 API hoặc Ajax... Vì vậy mòi người bắt đầu tìm kiếm cách loại bỏ CSRF khỏi middleware mặc định. Và câu trả lời đơn giản nhất là không include nó trong $middleware array, và thêm nó vào bất cứ nơi nào cần thiết như trong phiên bản Laravel 4.
- Laravel 5.1 - Vấn đề được giải quết
Vấn đề trên đã được nhìn nhận và được thay đổi trong phiên bản 5.1. Và sau đây là một số điểm mới:
- Bạn có thể sử dụng helper csrf_field() để tạo toàn bộ input thay vì chỉ mỗi token. Vì vậy, thay vì:
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
Bạn chỉ phải viết:
{!! csrf_field() !!}
- Vẫn còn VerifyCsrfToken trong $middleware array
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
];
Nhưng bạn có thể loại bỏ nó khỏi những URL nhất định mà bạn muốn, để là được điều đó mở app/Http/Middleware/VerifyCsrfToken.php thêm thuộc tính $except:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
'ajax/*',
];
}
Lưu ý rằng (*) là một biểu tượng để quy định 1 rule nào đấy và rule này mô tả các URL chứ ko phải route
All rights reserved