2015-02-09 68 views
17

Tôi đang làm việc trên ứng dụng Laravel 5 có tính năng bảo vệ CSRF được bật theo mặc định cho tất cả các yêu cầu POST. Tôi thích bảo mật này vì vậy tôi đang cố gắng làm việc với nó.

Trong khi thực hiện yêu cầu $.post() đơn giản, tôi đã nhận được lỗi 'Illuminate\Session\TokenMismatchException' do dữ liệu POST bị thiếu dữ liệu POST trong biểu mẫu yêu cầu nhập _token. Dưới đây là một ví dụ về một yêu cầu $ .post trong câu hỏi:

var userID = $("#userID").val(); 
$.post('/admin/users/delete-user', {id:userID}, function() { 
// User deleted 
}); 

Tôi đã CSRF token tôi được lưu trữ như một lĩnh vực meta trong phần đầu của tôi và có thể dễ dàng truy cập nó bằng cách sử:

var csrf_token = $('meta[name="csrf-token"]').attr('content'); 

Có thể để thêm dữ liệu này vào dữ liệu json trên tất cả các yêu cầu gửi đi $.post()? Tôi đã thử sử dụng tiêu đề nhưng Laravel dường như không nhận ra chúng -

var csrf_token = $('meta[name="csrf-token"]').attr('content'); 
alert(csrf_token); 
$.ajaxPrefilter(function(options, originalOptions, jqXHR){ 
    if (options['type'].toLowerCase() === "post") { 
     jqXHR.setRequestHeader('X-CSRFToken', csrf_token); 
    } 
}); 
+0

Tôi có một cách chính xác hiểu rằng bạn muốn làm cho đối tượng dữ liệu của bạn có một '_token' lĩnh vực? (ví dụ: một số thứ như 'tùy chọn ['dữ liệu'] ._ token = csrf_token'?) Một ví dụ đơn lẻ về yêu cầu làm việc sẽ hữu ích nếu bạn có. – apsillers

+0

Có, chính xác ... – NightMICU

+0

Để thực hiện yêu cầu, tôi sẽ phải thêm mã thông báo trực tiếp vào dữ liệu bài đăng - vì vậy '{id: userID, '_token': token}' – NightMICU

Trả lời

21

Phương pháp tiếp cận $.ajaxPrefilter là phương pháp tốt nhất. Tuy nhiên, bạn không cần thêm tiêu đề; bạn chỉ cần thêm thuộc tính vào chuỗi data.

Dữ liệu được cung cấp làm đối số thứ hai cho $.post và sau đó được định dạng dưới dạng chuỗi truy vấn (id=foo&bar=baz&...) trước khi trình lọc được truy cập vào tùy chọn data. Vì vậy, bạn cần phải thêm lĩnh vực riêng của mình vào chuỗi truy vấn:

var csrf_token = $('meta[name="csrf-token"]').attr('content'); 
$.ajaxPrefilter(function(options, originalOptions, jqXHR){ 
    if (options.type.toLowerCase() === "post") { 
     // initialize `data` to empty string if it does not exist 
     options.data = options.data || ""; 

     // add leading ampersand if `data` is non-empty 
     options.data += options.data?"&":""; 

     // add _token entry 
     options.data += "_token=" + encodeURIComponent(csrf_token); 
    } 
}); 

này sẽ biến id=userID vào id=userID&_token=csrf_token.

+0

Được rồi, tôi chỉ có thể thực hiện tác vụ này với 'tùy chọn ['dữ liệu'] = tùy chọn ['dữ liệu'] + '& _token =' + csrf_token;' - ký hiệu dấu chấm dường như không hoạt động. Có vẻ tốt hơn, bất kỳ ý tưởng nào có thể là vấn đề? – NightMICU

+0

@NightMICU Tôi thấy; vấn đề không phải là ký hiệu dấu chấm, mà đúng hơn là jQuery chuyển đổi đối tượng dữ liệu thành một chuỗi truy vấn trước khi trình lọc tiền nhận được nó. Tôi đã chỉnh sửa (nhưng tôi chưa thử nghiệm, vì vậy hãy cho tôi biết nếu nó vẫn bị hỏng). – apsillers

+0

Có vẻ tốt! Cảm ơn nhiều! – NightMICU

0

Tôi nghĩ rằng giải pháp trên có thể không hoạt động. Khi bạn làm:

var x; 
x + "" 
// "undefined" + empty string coerces value to string 

Bạn sẽ nhận được dữ liệu như "undefined_token = xxx"

Khi bạn sử dụng các giải pháp trên cho laravel của xóa ví dụ bạn phải kiểm tra như thế này:

if (typeof options.data === "undefined") 
    options.data = ""; 
else 
    options.data += "&"; 
options.data = "_token=" + csrf_token; 
12

Từ tài liệu Laravel:

Bạn có thể lưu trữ mã thông báo trong thẻ "meta":

Khi bạn đã tạo thẻ meta, bạn có thể hướng dẫn thư viện như jQuery để thêm mã thông báo vào tất cả các tiêu đề yêu cầu. Điều này cung cấp đơn giản, thuận tiện bảo vệ CSRF cho các ứng dụng AJAX dựa của bạn:

$ .ajaxSetup ({ tiêu đề: { 'X-CSRF-TOKEN': $ ('meta [name = "CSRF-thẻ"]') .attr ('content') }});

Ví dụ: bạn có thể thực hiện yêu cầu như dưới đây.

Thêm thẻ meta này để xem bạn:

<meta name="csrf-token" content="{{ csrf_token() }}"> 

Và đây là một kịch bản ví dụ mà bạn có thể giao tiếp với Laravel (gửi yêu cầu khi bạn nhấp vào một phần tử với id = "some-id" và bạn có thể xem phản hồi trong phần tử có id = "result"):

<script type="text/javascript"> 
    $(document).ready(function(){ 

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

     $("#some-id").on("click", function() { 
      var request; 


      request = $.ajax({ 
       url: "/your/url", 
       method: "POST", 
       data: 
       { 
        a: 'something', 
        b: 'something else', 
       }, 
       datatype: "json" 
      }); 

      request.done(function(msg) { 
       $("#result").html(msg); 
      }); 

      request.fail(function(jqXHR, textStatus) { 
       $("#result").html("Request failed: " + textStatus); 
      }); 
     }); 

    }); 
</script> 
4

Nói chung tôi đồng ý với khái niệm Kornel đề xuất ngoại trừ một điều.

Có, lời khuyên của tài liệu Laravel để sử dụng $.ajaxSetup, nhưng không được khuyến nghị vì phương pháp này ảnh hưởng đến tất cả các yêu cầu ajax tiếp theo. Chính xác hơn là đặt cài đặt ajax cho mỗi yêu cầu. Mặc dù bạn có thể tái thiết lập thứ:

Tất cả Ajax sau đó kêu gọi sử dụng bất kỳ chức năng sẽ sử dụng các thiết lập mới, trừ khi ghi đè bởi các cuộc gọi cá nhân, cho đến khi gọi tiếp theo của $ .ajaxSetup()

Nếu bạn sử dụng $.ajax(), sẽ thuận tiện hơn khi sử dụng thuộc tính data hoặc headers. Laravel cho phép CSRF-token cả hai như là một tham số yêu cầu hoặc một tiêu đề.

Đầu tiên, bạn thêm thẻ meta sau vào xem

<meta name="csrf-token" content="{{ csrf_token() }}"> 

Và sau đó đưa ra một yêu cầu ajax một trong hai cách:

$.ajax({ 
    url: "/your/url", 
    method: "POST", 
    data: 
    { 
     a: 'something', 
     b: 'something else', 
     _token: $('meta[name="csrf-token"]').attr('content') 
    }, 
    datatype: "json" 
}); 

HOẶC

$.ajax({ 
    url: "/your/url", 
    method: "POST", 
    data: 
    { 
     a: 'something', 
     b: 'something else', 
    }, 
    headers: 
    { 
     'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') 
    } 
    datatype: "json" 
}); 
0

Các Django documentation on CSRF đưa ra một đoạn mã đẹp với ajaxSetup để tự động thêm tiêu đề phù hợp để tất cả các loại yêu cầu nơi quan trọng:

function csrfSafeMethod(method) { 
    // these HTTP methods do not require CSRF protection 
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 
} 
$.ajaxSetup({ 
    beforeSend: function(xhr, settings) { 
     if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 
      xhr.setRequestHeader("X-CSRFToken", csrftoken); 
     } 
    } 
}); 
Các vấn đề liên quan