Dưới đây là một số trích đoạn của làm thế nào tôi có CSRF tôi làm việc cho tất cả các kịch bản khác nhau trong ứng dụng jQuery Mobile của tôi mà gần đây tôi đã nâng cấp để sử dụng Laravel 5:
Tôi đã thêm mã thông báo csrf được mã hóa vào một biến sẽ được chuyển đến chế độ xem của tôi trong bộ điều khiển cơ sở chính của tôi: app\Http\Controllers\MyController.php
$this->data['encrypted_csrf_token'] = Crypt::encrypt(csrf_token());
Sau đó, tôi thêm thẻ meta trong tiêu đề giao diện chính của tôi: resources\views\partials\htmlHeader.blade.php
<meta name="_token" content="{!! $encrypted_csrf_token !!}"/>
Sau đó, tôi cũng nói thêm đoạn jquery này như đề xuất trong một số diễn đàn:
$(function() {
$.ajaxSetup({
headers: {
'X-XSRF-TOKEN': $('meta[name="_token"]').attr('content')
}
});
});
Tuy nhiên, chìa khóa (cho thiết lập của tôi ít nhất) là việc bổ sung kiểm tra cho các XSRF-TOKEN
cookie trong phần mềm trung gian VerifyCsrfToken tùy chỉnh của tôi: app\Http\Middleware\VerifyCsrfToken.php:
/**
* Determine if the session and input CSRF tokens match.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function tokensMatch($request)
{
$token = $request->session()->token();
$header = $request->header('X-XSRF-TOKEN');
$cookie = $request->cookie('XSRF-TOKEN');
return StringUtils::equals($token, $request->input('_token')) ||
($header && StringUtils::equals($token, $this->encrypter->decrypt($header))) ||
($cookie && StringUtils::equals($token, $cookie));
}
Trước khi tôi nói thêm rằng, chỉ là về tất cả các bài AJAX của tôi (bao gồm cả hình thức đệ trình và listviews lazyloading) đã thất bại do một TokenMismatchException
.
EDIT: Suy nghĩ thứ hai, tôi không biết cần so sánh mã thông báo phiên với mã được đặt trong cookie (mã này có thể đến từ mã phiên ở vị trí đầu tiên phải không?). Điều đó có thể đã bỏ qua sự an toàn của tất cả.
Tôi nghĩ vấn đề chính của tôi là đoạn mã jquery ở trên được cho là thêm tiêu đề X-XSRF-TOKEN vào mọi yêu cầu ajax. Điều đó không hiệu quả đối với tôi trong ứng dụng jQuery Mobile của tôi (đặc biệt, trong số lazyloader plugin) của tôi cho đến khi tôi thêm một số tùy chọn cho chính plugin đó. Tôi đã thêm một công cụ chọn mặc định mới csrf
(có thể là meta[name="_token"]
trong trường hợp này) và cài đặt mặc định mới csrfHeaderKey
(trong trường hợp này là X-XSRF-TOKEN
). Về cơ bản, trong quá trình khởi tạo plugin, thuộc tính mới _headers
được khởi tạo bằng mã thông báo CSRF nếu một mã định vị được chọn bởi bộ chọn csrf
(mặc định hoặc do người dùng xác định). Sau đó, ở 3 nơi khác nhau mà POST ajax có thể được tắt (khi đặt lại biến phiên hoặc khi tải xuống chế độ xem danh sách), tùy chọn tiêu đề của $ .ajax được đặt bằng bất kỳ nội dung nào trong số _headers
.
Dù sao, vì X-XSRF-TOKEN nhận được ở phía máy chủ đến từ siêu dữ liệu được mã hóa _token, tôi nghĩ rằng bảo vệ CSRF hiện đang hoạt động như mong muốn.
app\Http\Middleware\VerifyCsrfToken.php
của tôi bây giờ trông như thế này (mà chủ yếu là về việc thực hiện mặc định được cung cấp bởi Laravel 5 - LOL):
/**
* Determine if the session and input CSRF tokens match.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function tokensMatch($request)
{
$token = $request->session()->token();
$_token = $request->input('_token');
$header = $request->header('X-XSRF-TOKEN');
return StringUtils::equals($token, $_token) ||
($header && StringUtils::equals($token, $this->encrypter->decrypt($header)));
}
Tốt, trong L5 nó sẽ là {!! Biểu mẫu :: token() !!} – raphadko
@RaphaelCabrera Có bạn đã đúng. Chết tiệt, tôi sẽ không bao giờ quen với '{!!' ... May mắn thay bạn có thể thay đổi chúng :) – lukasgeiter
Không thể gửi lại biểu mẫu với cùng một mã thông báo sau khi lỗi xác thực – mvladk