Laravel 5 CSRF глобальный токен скрытого поля для всех форм на странице
Недавно я перешел на Laravel 5, и теперь проверка CSRF находится на каждой отправке сообщений. Я думал об удалении его, но я хочу следовать лучшим практикам, поэтому я сохраню его таким образом.
С другой стороны, у меня проблемы с отправкой ajax-запросов. Моя страница имеет несколько форм, а некоторые представления даже не из форм, а просто для вызовов ajax. Моя идея состоит в том, чтобы на странице был введен один скрытый "токен" и прикрепить его к каждой отправке. Есть ли недостатки в том, что этот универсальный входной токен?
Также, как я могу вывести токен? Было бы хорошо просто создать скрытый ввод в нижнем колонтитуле страницы?
Ответы
Ответ 1
Я не вижу недостатков. Вы можете легко создать глобальное поле маркера в вашем файле макета:
<input type="hidden" name="_token" id="csrf-token" value="{{ Session::token() }}" />
Или, если вы используете построитель форм:
{!! Form::token() !!}
В jQuery вы можете использовать что-то вроде this для прикрепления маркера к каждому запросу.
Ответ 2
Существует helper, чтобы добавить формальные форматы внутри форм. Вы можете просто использовать
{!! csrf_field() !!}
внутри форм. Он добавит скрытый ввод и токен.
Ответ 3
Вот некоторые отрывки из того, как я получил свой CSRF, работающий для всех различных сценариев в моем приложении jQuery Mobile, который я недавно обновил, чтобы использовать Laravel 5:
Я добавил зашифрованный токен csrf в переменную, которая будет передана моим представлениям в моем основном базовом контроллере:
app\Http\Controllers\MyController.php
$this->data['encrypted_csrf_token'] = Crypt::encrypt(csrf_token());
Затем я добавил метатег в свой заголовок главного заголовка:
resources\views\partials\htmlHeader.blade.php
<meta name="_token" content="{!! $encrypted_csrf_token !!}"/>
Затем я также добавил этот фрагмент jquery, как предлагается на некоторых форумах:
$(function () {
$.ajaxSetup({
headers: {
'X-XSRF-TOKEN': $('meta[name="_token"]').attr('content')
}
});
});
Но ключ (по крайней мере, для моей настройки) заключался в добавлении проверки для cookie XSRF-TOKEN
в моем обычном промежуточном программном обеспечении VerifyCsrfToken:
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));
}
Прежде чем я добавил, что почти все мои POST-адреса AJAX (включая представления форм и списки просмотров) не срабатывали из-за TokenMismatchException
.
EDIT:
С другой стороны, я не уверен, насколько это важно, чтобы сравнить токен сеанса с тем, который был установлен в cookie (который был бы получен из токена сеанса в первую очередь?). Возможно, это просто обошло безопасность всего этого.
Я думаю, что моя основная проблема заключалась в фрагменте jquery выше, который должен был добавить заголовок X-XSRF-TOKEN для каждого запроса ajax. Это не работало для меня в моем приложении jQuery Mobile (в частности, в плагине lazyloader), пока я не добавлю некоторые опции для самого модуля. Я добавил новый селектор по умолчанию csrf
(который в этом случае будет meta[name="_token"]
) и новый параметр по умолчанию csrfHeaderKey
(который в этом случае будет X-XSRF-TOKEN
). В принципе, при инициализации плагина новое свойство _headers
инициализируется маркером CSRF, если он локализуется селектором csrf
(по умолчанию или определяется пользователем). Затем, в трех разных местах, где может быть запущен ajax POST (при сбросе переменных сеанса или при lazyloading listview), параметр headers в $.ajax устанавливается с тем, что находится в _headers
.
Во всяком случае, поскольку X-XSRF-TOKEN, полученный на стороне сервера, поступает из зашифрованного meta_token, я думаю, что защита CSRF теперь работает так, как должна.
Теперь мой app\Http\Middleware\VerifyCsrfToken.php
выглядит так (что в основном относится к реализации по умолчанию, предоставленной 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)));
}
Ответ 4
В нижней части страницы вы можете использовать что-то вроде этого:
$('form').append('{{csrf_field()}}');
Это добавит скрытый ввод ко всем вашим forms
:
<input type="hidden" name="_token" value="yIcHUzipr2Y2McGE3EUk5JwLOPjxrC3yEBetRtlV">
И для всех ваших запросов AJAX:
$.ajaxSetup({
beforeSend: function (xhr, settings) {
//////////// Only for your domain
if (settings.url.indexOf(document.domain) >= 0) {
xhr.setRequestHeader("X-CSRF-Token", "{{csrf_token()}}");
}
}
});
Ответ 5
Вам нужно пройти по заголовку X-XSRF-TOKEN
, который содержит зашифрованную версию csrf-token
.
Есть два способа сделать это, о котором я знаю. Вы можете зашифровать токен и передать его в представление:
$xsrfToken = app('Illuminate\Encryption\Encrypter')->encrypt(csrf_token());
return view('some.ajax.form.view')->with('xsrf_token', $xsrfToken);
Или вы можете захватить токен из файлов cookie с помощью JavaScript (Angular делает это проще). В vanilla JS вы можете сделать что-то вроде этого:
function getCookie(name) {
var pattern = RegExp(name + "=.[^;]*")
matched = document.cookie.match(pattern)
if (matched) {
var cookie = matched[0].split('=')
return decodeURIComponent(cookie[1])
}
return false
}
В jQuery вы можете сделать что-то подобное для запроса ajax:
$.ajax({
// your request
//
beforeSend: function(request) {
return request.setRequestHeader('X-XSRF-TOKEN', getCookie('XSRF-TOKEN'));
}
});
Ответ 6
Я думаю, что вы можете сделать что-то вроде этого (не проверено будет обновляться, если я получаю шанс)
$(document).on('submit', 'form', function(e)
$(this).append('<input name="_token" value="{{{ Session::token() }}}">);
});
вы действительно можете захотеть сохранить токен в переменной, которую вы повторно заполняете по мере ее истечения.
Преимущество добавления его в submit - если вы добавляете элементы через ajax, я думаю, что он по-прежнему будет работать без необходимости добавлять что-либо еще.
РЕДАКТИРОВАТЬ: Вот отличная статья об использовании Rails UJS с Laravel (которая включает в себя эту функцию автоматического маркера CRSF): https://medium.com/@barryvdh/unobtrusive-javascript-with-jquery-ujs-and-laravel-e05f444d3439