ВНИМАНИЕ: Невозможно проверить рельсы подлинности токена CSRF.

238

Я посылаю данные из представления на контроллер с AJAX, и я получил эту ошибку:

ВНИМАНИЕ: Невозможно проверить подлинность токена CSRF.

Я думаю, что я должен отправить этот токен с данными.

Кто-нибудь знает, как я могу это сделать?

Изменить: мое решение

Я сделал это, поместив следующий код в сообщение AJAX:

headers: {
  'X-Transaction': 'POST Example',
  'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
},
kbaccouche
источник
7
у вас есть <% = csrf_meta_tag%> в заголовке вашего макета?
Анатолий
да, вот так: <% = csrf_meta_tags%>
kbaccouche
6
у вас есть библиотеки jquery-rails, которые предоставляют клиентскую функциональность ajax?
Анатолий
2
И путь HAML - добавить "= csrf_meta_tags"
Эге Акпинар,
хороший вопрос, спасибо за вопрос
AMIC MING

Ответы:

374

Вы должны сделать это:

  1. Убедитесь, что у вас есть <%= csrf_meta_tag %>в вашем макете

  2. Добавьте beforeSendко всем запросам ajax, чтобы установить заголовок, как показано ниже:


$.ajax({ url: 'YOUR URL HERE',
  type: 'POST',
  beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
  data: 'someData=' + someData,
  success: function(response) {
    $('#someDiv').html(response);
  }
});

Для отправки токена во всех запросах вы можете использовать:

$.ajaxSetup({
  headers: {
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
  }
});
Чау Хонг Линь
источник
5
Спасибо! Работал для меня как шарм!
Миша Морошко
35
Библиотека jQuery UJS, предоставленная командой Rails, автоматически добавляет токен CSRF в запрос jQuery AJAX. README содержит инструкции по настройке. github.com/rails/jquery-ujs/blob/master/src/rails.js#L91
Джеймс Конрой-Финн
1
Обратите внимание, что вы можете установить заголовок для всех запросов одновременно с помощью функции $ .ajaxSetup.
Питер Йонгсма
1
Превосходно! Некоторое время искал этот ответ. Работает без проблем. Спасибо!
cassi.lup
4
Как примечание, если вы используете jQuery UJS, как предложено выше, вам нужно убедиться, что включение rails-ujs происходит после включения jquery, или произойдет сбой с той же ошибкой, что и операция.
Topher Fangio
31

Лучший способ сделать это на самом деле просто использовать <%= form_authenticity_token.to_s %>для распечатки токена непосредственно в коде rails. Вам не нужно использовать javascript для поиска в домене токена csrf, как упоминалось в других сообщениях. просто добавьте опцию заголовков, как показано ниже;

$.ajax({
  type: 'post',
  data: $(this).sortable('serialize'),
  headers: {
    'X-CSRF-Token': '<%= form_authenticity_token.to_s %>'
  },
  complete: function(request){},
  url: "<%= sort_widget_images_path(@widget) %>"
})
АДАМ
источник
7
Вместо того чтобы делать это для каждой команды ajax, вы можете добавить заголовки в $ .ajaxSetup () .
Скотт Макмиллин
1
Я лучше рекомендую использовать этот ответ ...
opsidao
14
Мне не очень нравится подход использования ERB в JavaScript.
лучевая собака
Это заставляет вас генерировать свой JavaScript с помощью ERB, что очень ограничивает. Даже если есть места, где ERB может подходить, есть и другие, где его нет, и добавление его просто для получения токена будет пустой тратой.
sockmonk
22

Если я правильно помню, вам нужно добавить следующий код в форму, чтобы избавиться от этой проблемы:

<%= token_tag(nil) %>

Не забудьте параметр.

auralbee
источник
8
На самом деле, это должно быть: <%= token_tag(nil) %>. Затем вы получаете автоматически сгенерированный токен.
szeryf
15

Действительно самый простой способ. Не беспокойтесь о смене заголовков.

Убедитесь, что у вас есть:

<%= csrf_meta_tag %> in your layouts/application.html.erb

Просто сделайте скрытое поле ввода примерно так:

<input name="authenticity_token" 
               type="hidden" 
               value="<%= form_authenticity_token %>"/>

Или, если вы хотите JQuery AJAX сообщение:

$.ajax({     
    type: 'POST',
    url: "<%= someregistration_path %>",
    data: { "firstname": "text_data_1", "last_name": "text_data2", "authenticity_token": "<%= form_authenticity_token %>" },                                                                                  
    error: function( xhr ){ 
      alert("ERROR ON SUBMIT");
    },
    success: function( data ){ 
      //data response can contain what we want here...
      console.log("SUCCESS, data="+data);
    }
});
Вальтер Шрепперс
источник
Добавление скрытого поля ввода в мою форму ввода решило проблему для меня.
Теему Лейсти
это делается автоматически, если вы используете помощники форм Rails.
Джейсон Ф.Б.
13

Переход от старого приложения к rails 3.1, включая метатег csrf, до сих пор не решает проблему. В блоге rubyonrails.org они дают несколько советов по обновлению, а именно эту строку jquery, которая должна быть в разделе head вашего макета:

$(document).ajaxSend(function(e, xhr, options) {
 var token = $("meta[name='csrf-token']").attr("content");
  xhr.setRequestHeader("X-CSRF-Token", token);
});

взято из этого блога: http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails .

В моем случае сеанс сбрасывался при каждом запросе ajax. Добавление приведенного выше кода решило эту проблему.

Дэнни
источник
10
  1. Убедитесь, что у вас есть <%= csrf_meta_tag %>в вашем макете
  2. Добавьте a, beforeSendчтобы включить токен csrf в запрос ajax для установки заголовка. Это требуется только для postзапросов.

Код для чтения csrf-токена доступен в rails/jquery-ujs, так что imho проще всего его использовать следующим образом:

$.ajax({
  url: url,
  method: 'post',
  beforeSend: $.rails.CSRFProtection,
  data: {
    // ...
  }
})
nathanvda
источник
Просто без повторной реализации того, что уже включено в Rails. Это должен быть выбранный ответ.
17
В Rails 5.1 тоже работает:headers: { 'X-CSRF-Token': Rails.csrfToken() }
olhor
@nathanvda, Может быть, вы можете ответить на этот похожий вопрос: stackoverflow.com/questions/50159847/…
6

Ответы с наибольшим количеством голосов здесь верны, но не будут работать, если вы выполняете междоменные запросы, потому что сеанс будет недоступен, если вы явно не скажете jQuery пропустить cookie сеанса. Вот как это сделать:

$.ajax({ 
  url: url,
  type: 'POST',
  beforeSend: function(xhr) {
    xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  },
  xhrFields: {
    withCredentials: true
  }
});
dreadwail
источник
Могу ли я спросить вас, можете ли вы ответить на этот очень похожий вопрос? stackoverflow.com/questions/50159847/…
5

Вы можете написать это глобально как ниже.

Нормальный JS:

$(function(){

    $('#loader').hide()
    $(document).ajaxStart(function() {
        $('#loader').show();
    })
    $(document).ajaxError(function() {
        alert("Something went wrong...")
        $('#loader').hide();
    })
    $(document).ajaxStop(function() {
        $('#loader').hide();
    });
    $.ajaxSetup({
        beforeSend: function(xhr) {xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))}
    });
});

Сценарий кофе:

  $('#loader').hide()
  $(document).ajaxStart ->
    $('#loader').show()

  $(document).ajaxError ->
    alert("Something went wrong...")
    $('#loader').hide()

  $(document).ajaxStop ->
    $('#loader').hide()

  $.ajaxSetup {
    beforeSend: (xhr) ->
      xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))
  }
Chezhian
источник
5

упс ..

Я пропустил следующую строку в моем application.js

//= require jquery_ujs

Я заменил его, и он работает ..

======= ОБНОВЛЕНО =========

Через 5 лет я вернулся с той же ошибкой, теперь у меня есть новый Rails 5.1.6 , и я снова нашел этот пост. Прямо как круг жизни.

Теперь проблема заключалась в том, что Rails 5.1 по умолчанию удалил поддержку jquery и jquery_ujs и добавил

//= require rails-ujs in application.js

Это делает следующие вещи:

  1. вызвать диалог подтверждения для различных действий;
  2. делать не-GET запросы от гиперссылок;
  3. заставить формы или гиперссылки передавать данные асинхронно с Ajax;
  4. кнопки отправки автоматически отключаются при отправке формы, чтобы избежать двойного щелчка. (от: https://github.com/rails/rails-ujs/tree/master )

Но почему он не включает токен csrf для запроса ajax? Если кто-то знает об этом подробно, просто прокомментируйте меня. Я ценю это.

В любом случае я добавил следующее в свой файл js, чтобы он работал (спасибо за другие ответы, которые помогут мне добраться до этого кода):

$( document ).ready(function() {
  $.ajaxSetup({
    headers: {
      'X-CSRF-Token': Rails.csrfToken()
    }
  });
  ----
  ----
});
Аби
источник
Это было то, что мне нужно было сделать. Причина этого заключается в том, что я создаю приложение React, чтобы медленно заменить существующее приложение Rails. Поскольку в существующем приложении происходит много шума javascript, я создал другой макет, который обращается к другому файлу javascript, но я не смог его включить jquery_ujs. Это был трюк.
Уильям Джадд
1
Да, иногда Если мы пропустим это, когда переделываем, что-то реорганизуем .. Трудно найти, что происходит не так. Поскольку мы ничего не делаем вручную, чтобы это работало, Rails автоматически включает это. Мы думаем, что это уже там. Спасибо за такие социальные Qn / Ans Sites
Abhi
Может быть, вы можете ответить на этот похожий вопрос: stackoverflow.com/questions/50159847/…
4

Если вы не используете JQuery и использовать что - то вроде выборки API для запросов вы можете использовать следующую команду, чтобы получить csrf-token:

document.querySelector('meta[name="csrf-token"]').getAttribute('content')

fetch('/users', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')},
    credentials: 'same-origin',
    body: JSON.stringify( { id: 1, name: 'some user' } )
    })
    .then(function(data) {
      console.log('request succeeded with JSON response', data)
    }).catch(function(error) {
      console.log('request failed', error)
    })
svnm
источник
Могу ли я спросить вас, можете ли вы ответить на этот очень похожий вопрос? stackoverflow.com/questions/50159847/…
4

Используйте jquery.csrf ( https://github.com/swordray/jquery.csrf ).

  • Rails 5.1 или более поздняя версия

    $ yarn add jquery.csrf
    //= require jquery.csrf
  • Rails 5.0 или раньше

    source 'https://rails-assets.org' do
      gem 'rails-assets-jquery.csrf'
    end
    //= require jquery.csrf
  • Исходный код

    (function($) {
      $(document).ajaxSend(function(e, xhr, options) {
        var token = $('meta[name="csrf-token"]').attr('content');
        if (token) xhr.setRequestHeader('X-CSRF-Token', token);
      });
    })(jQuery);

swordray
источник
В 5.1 с использованием веб-пакета //= require jquery.csrfне сработает, верно? Вместо этого я использовал пакет JS файл с import 'jquery.csrf'в нем. Обратите внимание, что вам не нужно включать это с тегом пакета в ваших представлениях.
Дэмиен Джастин Шутевски
3

Если вы используете javascript с jQuery для генерации токена в вашей форме, это работает:

<input name="authenticity_token" 
       type="hidden" 
       value="<%= $('meta[name=csrf-token]').attr('content') %>" />

Очевидно, вы должны иметь <%= csrf_meta_tag %>в своем макете Ruby.

Тим Сколлик
источник
2

Для тех из вас, кому нужен ответ не от jQuery, вы можете просто добавить следующее:

xmlhttp.setRequestHeader ('X-CSRF-Token', $ ('meta [name = "csrf-token"]'). attr ('content'));

Очень простой пример может быть отправлен здесь:

xmlhttp.open ( "POST", "example.html", правда);
xmlhttp.setRequestHeader ('X-CSRF-Token', $ ('meta [name = "csrf-token"]'). attr ('content'));
xmlhttp.send ();
Мек
источник
6
Разве это не использует jQuery для селекторов?
Yule
2

Я боролся с этой проблемой в течение нескольких дней. Любой вызов GET работал правильно, но все PUT-сообщения вызывали ошибку «Не удается проверить подлинность токена CSRF». Мой веб-сайт работал нормально, пока я не добавил сертификат SSL в nginx.

Я наконец наткнулся на эту недостающую строку в моих настройках nginx:

location @puma { 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    proxy_set_header Host $http_host; 
    proxy_redirect off;
    proxy_set_header X-Forwarded-Proto https;   # Needed to avoid 'WARNING: Can't verify CSRF token authenticity'
    proxy_pass http://puma; 
}

После добавления пропущенной строки «proxy_set_header X-Forwarded-Proto https;» все мои ошибки токена CSRF завершаются.

Надеюсь, это поможет кому-то, кто также бьет их головой о стену. ха-ха

Hoffmann
источник
0

Я использую Rails 4.2.4 и не могу понять, почему я получаю:

Can't verify CSRF token authenticity

У меня есть в макете:

<%= csrf_meta_tags %>

В контроллере:

protect_from_forgery with: :exception

Invoking tcpdump -A -s 999 -i lo port 3000показывал устанавливаемый заголовок (несмотря на то, что не нужно было устанавливать заголовки с помощью ajaxSetup- это уже было сделано):

X-CSRF-Token: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
DNT: 1
Content-Length: 125
authenticity_token=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

В конце концов, это не удалось, потому что у меня были отключены куки. CSRF не работает без включенных файлов cookie, поэтому это еще одна возможная причина, если вы видите эту ошибку.

Нил Стокбридж
источник
это очень помогло!
Joehwang