2015-02-18 8 views
17

Я получаю ошибку токена CSRF при попытке обновления (или создания) записи. Я использую Elixir v1.0.3, Erlang/OTP 17 [erts-6.3] и Phoenix v0.8.0 (я думаю, я не уверен, как проверить версию Phoenix). Я создаю веб-приложение, в основном следуя инструкциям Phoenix и ресурсам примера Elixir Dose Jobsite. Однако, когда я пытаюсь опубликовать информацию из html-формы, я получаю ошибку токена CSRF. Следуя советам, приведенным в ошибке, я добавил «x-csrf-token»: csrf_token к действию.Phoenix - Неверная ошибка маркера CSRF (перекрестная защита)

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> 
... 

, но я получаю следующее сообщение об ошибке:

[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 

Насколько я могу сказать (быть новым для эликсира, Феникс, и HTML), " действие "- это по существу путь, и любые параметры, которые я размещаю в нем, вернутся к приложению. И, действительно, я обнаружил, что x-csrf-token = "" передается обратно маршрутизатору, поэтому @csrf_token не может быть правильным. Я точно не знаю, откуда происходит csrf_token, поэтому я не знаю, как ссылаться на него (или, возможно, я делаю это совершенно неправильно).

Любые идеи были бы весьма признательны.

ответ

3

Чтобы увидеть версию установлена, запустите

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

Это показывает, какие феникс у вас есть в каталоге DEPS.

Кроме того, если при обновлении до феникса 0.9.0 все изменилось (из-за обновлений для Plug.CSRFProtection), CSRF работает по-разному, используя файлы cookie вместо сеансов.

От 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"

Для доступа к значению в лексемы, возьмите, если из печенья, который на стороне сервера выглядит как

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

Так что для вашего кода, будет выглядеть как

<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> 

Теперь, для полноты, мне понадобился обновленный CSRF для запроса ests построил чисто клиентскую сторону, так вот как я получил доступ к куки-файлу в javascript, используя куки JQuery, для легкого доступа.Вы должны быть в состоянии увидеть значение в вашем браузере, выполнив следующие

$.cookie("_csrf_token") 

Что может вернуть что-то вроде

"K9UDa23e1sacdadfmvu zzOD9VBHTSr1c/lcvWY=" 

Примечание в приведенном выше, пространство, которое в феникс был быть в кодировке URL в +, который по-прежнему вызывает CSRF. Теперь о том, что ошибка в затворе, или просто что-то обращаться, я не уверен, так что сейчас я просто обработки + явно

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

С доступом к маркеру CSRF, теперь мы просто нужно добавить x-csrf-токен в ваш заголовок запроса (thank you ilake). Вот код, чтобы заставить его работать с работой ajax call (заполнить URL-адрес и данные и ответ соответственно).

$.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); 
    } 
}); 

Обратите внимание, что вы можете отправить обратно _csrf_token в качестве параметра, а также, но я предпочитаю выше, и он чувствует себя чище меня.

Заключительное примечание. У меня недостаточно очков репутации для правильной публикации ссылки на jquery cookie, но это должно быть легко для Google.

+0

Благодарим вас за подробный ответ. Оказывается, я запускаю Phoenix 0.8.0, поэтому код ниже работает, но вы спасли мне большую головную боль, когда я обновляюсь до 0.9.0! –

+0

Еще раз спасибо @ a4word, я обновился до Phoenix 0.9.0 и изменил шаблон, чтобы получить токен из файла cookie. Однако, как ни странно, он работает, но я все равно получаю недопустимую ошибку CSRF (Cross Site Forgery Protection). Токен является 'ne0GATpoc/EW6jbIbC7tmfkAWl4qb1opTPWmmfYFTRY =' (без пробелов), и шаблон создает URL-адрес 'POST/config/directories/9? X-csrf-token = ne0GATpoc% 2FEW6jbIbC7tmfkAWl4qb1opTPWmmfYFTRY% 3D' Я понятия не имею, почему Plug.CSRFProtection не нравится. Знаете ли вы, или я должен открыть другой вопрос? –

+0

Я изменил имя маркера в шаблоне: ''x-csrf-token": Map.get (@ conn.req_cookies, "_csrf_token") 'to:' '_csrf_token': Map.get (@ conn.req_cookies) , "_csrf_token") 'и теперь это работает. –

1

Я нашел ответ на http://phoenix.thefirehoseproject.com. Вы должны создать функцию, чтобы получить маркер CSRF:

Интернет/view.ex

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

Затем извлечь его в шаблоне:

веб/шаблон/Каталог/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) %>"> 

И все!

19

В версии 0.13 от Phoenix вы можете сделать

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

потому, что в файле web/web.ex есть импорт этой функции.

+0

Спасибо! Я был только в процессе обновления до 0,13. –

+0

Спасибо - это единственное изменение в сгенерированном web/web.ex от v0.12 -> 0.13 – Jay

6

В качестве другого решения, доступного с v0.10.0, вы можете позволить Phoenix вводить вход CSRF для вас.

Example from upgrade guide:

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

Это будет выводить форму тегов и несколько входных тегов, в том числе _csrf_token один. Результаты будут выглядеть следующим образом:

<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: «для„пост“запросов, форма тег автоматически будет включать в себя входной тег с именем _csrf_token»

0

В моем случае это была строка plug :scrub_params, вызывающая проблему. После комментирования строки это сработало. Но нужно убедиться, что это исправить, поскольку приложение будет небезопасно без scrub_params.

Смежные вопросы