2015-02-18 16 views
17

Tôi nhận được lỗi mã thông báo CSRF không hợp lệ khi cố gắng cập nhật (hoặc tạo) bản ghi. Tôi đang sử dụng Elixir v1.0.3, Erlang/OTP 17 [erts-6.3], và Phoenix v0.8.0 (tôi nghĩ rằng, tôi không chắc chắn làm thế nào để kiểm tra phiên bản của Phoenix). Tôi đang tạo một ứng dụng web chủ yếu theo hướng dẫn Phoenix và các tài nguyên ví dụ về Elixir Dose Jobsite. Tuy nhiên, khi tôi cố gắng đăng thông tin từ một biểu mẫu html, tôi nhận được lỗi mã thông báo CSRF không hợp lệ. Theo lời khuyên trong lỗi, tôi đã thêm 'x-csrf-token': csrf_token vào hành động.Phoenix - Lỗi mã thông báo CSRF (Cross Site Forgery Protection) không hợp lệ

edit.html.eex:

<h2>Edit Directory</h2> 
<form class="form-horizontal" action="<%= directory_path @conn, :update, @directory.id, 'x-csrf-token': @csrf_token %>" method="post"> 
    <div class="form-group"> 
    <label for="directory" class="col-sm-2 control-label">Directory</label> 
    <div class="col-sm-10"> 
     <input type="hidden" name="_method" value="PATCH"> 
     <input type="text" class="form-control" value="<%= @directory.directory %>" name="directory" placeholder="Directory" required="required"> 
    </div> 
    </div> 
... 

nhưng tôi nhận được lỗi sau:

[error] #PID<0.579.0> running Ainur.Endpoint terminated 
Server: localhost:4000 (http) 
Request: POST /config/directories/2?x-csrf-token= 
** (exit) an exception was raised: 
    ** (Plug.CSRFProtection.InvalidCSRFTokenError) Invalid CSRF (Cross Site Forgery Protection) token. Make sure that all your non-HEAD and non-GET requests include the csrf_token as part of form params or as a value in your request's headers with the key 'x-csrf-token' 
     (plug) lib/plug/csrf_protection.ex:54: Plug.CSRFProtection.call/2 
     (ainur) web/router.ex:4: Ainur.Router.browser/2 
     (ainur) lib/phoenix/router.ex:2: Ainur.Router.call/2 
     (plug) lib/plug/debugger.ex:104: Plug.Debugger.wrap/3 
     (phoenix) lib/phoenix/endpoint/error_handler.ex:43: Phoenix.Endpoint.ErrorHandler.wrap/3 
     (ainur) lib/ainur/endpoint.ex:1: Ainur.Endpoint.phoenix_endpoint_pipeline/2 
     (plug) lib/plug/debugger.ex:104: Plug.Debugger.wrap/3 
     (phoenix) lib/phoenix/endpoint/error_handler.ex:43: Phoenix.Endpoint.ErrorHandler.wrap/3 

Theo như tôi có thể nói (bị mới để Elixir, Phoenix, và HTML), " hành động "về cơ bản là một đường dẫn và bất kỳ tham số nào tôi đặt trong đó sẽ tìm đường quay lại ứng dụng. Và, thực sự, tôi thấy rằng x-csrf-token = "" được truyền lại cho router, vì vậy @csrf_token không được chính xác. Tôi không chắc chắn chính xác nơi mà các csrf_token đến từ, vì vậy tôi không biết làm thế nào để tham khảo nó (hoặc có lẽ tôi đang làm điều này hoàn toàn sai).

Bất kỳ ý tưởng nào cũng sẽ được đánh giá cao.

Trả lời

3

Để xem phiên bản được cài đặt, chạy

cat ./deps/phoenix/mix.exs | grep version 

nào cho bạn thấy mà phượng bạn có trong thư mục DEPS.

Ngoài ra, nếu/khi bạn nâng cấp lên phượng 0.9.0, mọi thứ đã thay đổi (do cập nhật đối với Plug.CSRFProtection), CSRF hoạt động khác nhau bằng cách sử dụng cookie thay vì phiên.

Từ Phoenix changelog for v0.9.0 (2015-02-12)

[Plug] Plug.CSRFProtection now uses a cookie instead of session and expects a "_csrf_token" parameter instead of "csrf_token"

Để truy cập giá trị của mã thông báo, lấy nếu từ cookie, mà ở phía máy chủ trông giống như

Map.get(@conn.req_cookies, "_csrf_token") 

Vì vậy, đối mã của bạn, sẽ giống như

<h2>Edit Directory</h2> 
<form class="form-horizontal" action="<%= directory_path @conn, :update, @directory.id, 'x-csrf-token': Map.get(@conn.req_cookies, "_csrf_token") %>" method="post"> 
    <div class="form-group"> 
    <label for="directory" class="col-sm-2 control-label">Directory</label> 
    <div class="col-sm-10"> 
     <input type="hidden" name="_method" value="PATCH"> 
     <input type="text" class="form-control" value="<%= @directory.directory %>" name="directory" placeholder="Directory" required="required"> 
    </div> 
    </div> 

Bây giờ, để hoàn thành, tôi cần CSRF được cập nhật cho mọi yêu cầu ests được xây dựng hoàn toàn phía khách hàng, vì vậy đây là cách tôi truy cập cookie trong javascript, bằng cách sử dụng cookie JQuery, để dễ dàng truy cập.Bạn sẽ có thể nhìn thấy giá trị trong trình duyệt của bạn bằng cách chạy sau

$.cookie("_csrf_token") 

Mà có thể trở lại một cái gì đó giống như

"K9UDa23e1sacdadfmvu zzOD9VBHTSr1c/lcvWY=" 

Lưu ý ở trên, không gian, mà trong phượng được là url mã hóa để +, vẫn khiến CSRF thất bại. Bây giờ là một lỗi trong Plug, hoặc đơn giản là một cái gì đó để được xử lý, tôi không chắc chắn, vì vậy bây giờ tôi chỉ đơn giản là xử lý + một cách rõ ràng

$.cookie("_csrf_token").replace(/\s/g, '+'); 

Với quyền truy cập vào CSRF token, bây giờ chúng ta chỉ cần thêm x-csrf-token vào tiêu đề yêu cầu của bạn (thank you ilake). Đây là mã để làm cho nó hoạt động với một công việc gọi ajax (điền vào url và dữ liệu và phản ứng tương ứng).

$.ajax({ 
    url: 'YOUR URL HERE', 
    type: 'POST', 
    beforeSend: function(xhr) { 
    xhr.setRequestHeader('x-csrf-token', $.cookie("_csrf_token").replace(/\s/g, '+')) 
    }, 
    data: 'someData=' + someData, 
    success: function(response) { 
    $('#someDiv').html(response); 
    } 
}); 

Lưu ý rằng bạn cũng có thể gửi lại tham số _csrf_token, nhưng tôi thích phần trên và cảm giác này sạch hơn với tôi.

Lưu ý cuối cùng, tôi không có đủ điểm danh tiếng để đăng đúng liên kết tới cookie jquery, nhưng nó phải dễ dàng với google.

+0

Cảm ơn bạn đã trả lời chi tiết. Nó chỉ ra tôi đang chạy Phoenix 0.8.0, do đó, các mã dưới đây hoạt động, nhưng bạn đã lưu cho tôi một nhức đầu lớn khi tôi nâng cấp lên 0.9.0! –

+0

Cảm ơn một lần nữa @ a4word, tôi nâng cấp lên Phoenix 0.9.0 và thay đổi mẫu để lấy mã thông báo từ cookie. Tuy nhiên, kỳ lạ đủ, nó có vẻ làm việc, nhưng tôi vẫn nhận được một lỗi mã thông báo CSRF (Cross Site Forgery Protection) không hợp lệ. Mã thông báo là 'ne0GATpoc/EW6jbIbC7tmfkAWl4qb1opTPWmmfYFTRY =' (không có dấu cách) và mẫu tạo url «POST/config/directories/9? X-csrf-token = ne0GATpoc% 2FEW6jbIbC7tmfkAWl4qb1opTPWmmfYFTRY% 3D' Tôi không biết tại sao Plug.CSRFProtection lại không thích nó Bạn có biết hay tôi nên mở một câu hỏi khác? –

+0

Tôi đã đổi tên mã thông báo trong mẫu từ: ''x-csrf-token": Map.get (@ conn.req_cookies, "_csrf_token") 'thành:'' _csrf_token ': Map.get (@ conn.req_cookies , "_csrf_token") 'và bây giờ nó hoạt động –

1

Tôi đã tìm thấy câu trả lời trên http://phoenix.thefirehoseproject.com. Bạn phải tạo một hàm để có được CSRF token:

web/view.ex

def csrf_token(conn) do 
    Plug.Conn.get_session(conn, :csrf_token) 
end 

Sau đó lấy nó trong mẫu:

web/template/thư mục/edit.html.eex

<form class="form-horizontal" action="<%= directory_path @conn, :update, @directory.id %>" method="post"> 
    <input type="hidden" name="csrf_token" value="<%= csrf_token(@conn) %>"> 

Và đó là nó!

19

Trên phiên bản 0.13 của Phoenix bạn có thể làm

<input type="hidden" name="_csrf_token" value="<%= get_csrf_token() %>"> 

vì trong hồ sơ web/web.ex có một khẩu của chức năng này.

+0

Cảm ơn! Tôi chỉ đang trong quá trình nâng cấp lên 0,13. –

+0

Cảm ơn-- đó là thay đổi duy nhất cho web/web.ex được tạo từ v0.12 -> 0.13 – Jay

6

Là một giải pháp khác có sẵn từ v0.10.0, bạn có thể cho phép Phoenix chèn đầu vào CSRF cho bạn.

Example from upgrade guide:

<%= form_tag("/hello", method: :post) %> 
... your form stuff. input with csrf value is created for you. 
</form> 

Đó sẽ ra thẻ hình thức và một vài thẻ đầu vào, bao gồm cả _csrf_token một. Kết quả sẽ giống như thế này:

<form accept-charset="UTF-8" action="/hello" method="post"> 
    <input name="_csrf_token" value="[automatically-inserted token]" type="hidden"> 
    <input name="_utf8" value="✓" type="hidden"> 
</form> 

form_tag docs: "cho các yêu cầu 'post', bộ thẻ form sẽ tự động bao gồm một thẻ đầu vào với tên _csrf_token"

0

Trong trường hợp của tôi, đó là dòng plug :scrub_params gây ra sự cố. Sau khi bình luận dòng, nó hoạt động. Nhưng cần phải chắc chắn để sửa chữa nó như là ứng dụng sẽ không an toàn mà không có scrub_params.

Các vấn đề liên quan