php - generar - Campo oculto de token global Laravel 5 CSRF para todos los formularios en una página
generar token laravel (6)
Recientemente migré a Laravel 5, y ahora el control de CSRF está en cada envío posterior. Pensé en eliminarlo, pero quiero seguir las mejores prácticas, así que lo seguiré haciendo.
Por otro lado, tengo problemas para enviar solicitudes ajax ... mi página tiene varios formularios y algunos envíos ni siquiera son de formularios, solo son simples llamadas ajax. Mi idea es tener una única entrada de "token" oculta en la página y adjuntarla a cada envío. ¿Hay algún inconveniente en tener esa entrada de token único universal?
Además, ¿cómo puedo mostrar el token? ¿Estaría bien simplemente crear una entrada oculta en el pie de página?
Aquí hay algunos extractos de cómo conseguí que mi CSRF funcionara para todos los diferentes escenarios en mi aplicación jQuery Mobile que recientemente actualicé para usar Laravel 5:
Agregué un token csrf cifrado en una variable que se pasará a mis vistas en mi controlador base principal: app/Http/Controllers/MyController.php
$this->data[''encrypted_csrf_token''] = Crypt::encrypt(csrf_token());
Luego, agregué la metaetiqueta en el encabezado de mi vista principal: resources/views/partials/htmlHeader.blade.php
<meta name="_token" content="{!! $encrypted_csrf_token !!}"/>
Luego, también agregué este fragmento de jquery como se sugiere en algunos foros:
$(function () {
$.ajaxSetup({
headers: {
''X-XSRF-TOKEN'': $(''meta[name="_token"]'').attr(''content'')
}
});
});
Pero, la clave (al menos para mi configuración) fue la adición de la comprobación de la cookie XSRF-TOKEN
en mi middleware personalizado 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));
}
Antes de agregar eso, casi todos mis POST AJAX (incluidos los envíos de formularios y las listas de visitas de carga lenta) fallaron debido a una TokenMismatchException
.
EDITAR: Pensándolo bien , no estoy seguro de cuánto sentido tiene comparar el token de sesión con el que está establecido en la cookie (que habría salido del token de sesión en primer lugar, ¿no?). Eso puede haber estado pasando por alto la seguridad de todo esto.
Creo que mi principal problema fue con el fragmento de código jquery anterior, que se suponía que debía agregar el encabezado X-XSRF-TOKEN a cada solicitud de ajax. Eso no funcionó para mí en mi aplicación jQuery Mobile (específicamente, en mi complemento de lazyloader ) hasta que agregué algunas opciones para el complemento. csrf
un nuevo selector predeterminado csrf
(que sería meta[name="_token"]
en este caso) y una nueva configuración predeterminada csrfHeaderKey
(que sería X-XSRF-TOKEN
en este caso). Básicamente, durante la inicialización del complemento, una nueva propiedad _headers
se inicializa con el token CSRF si es posible localizarlo con el selector csrf
(predeterminado o definido por el usuario). Luego, en los 3 lugares diferentes donde se puede disparar un Ajax POST (al restablecer las variables de la sesión o al cargar de forma lenta una vista de lista), la opción de encabezados de $ .ajax se establece con lo que sea en los _headers
.
De todos modos, dado que el X-XSRF-TOKEN recibido en el lado del servidor proviene del meta _token cifrado, creo que la protección CSRF ahora está funcionando como debería.
Mi app/Http/Middleware/VerifyCsrfToken.php
ahora tiene este aspecto (que es esencialmente de nuevo a la implementación predeterminada proporcionada por 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)));
}
Creo que puedes hacer algo como esto (no probado se actualizará si tengo la oportunidad)
$(document).on(''submit'', ''form'', function(e)
$(this).append(''<input name="_token" value="{{{ Session::token() }}}">);
});
es posible que desee almacenar el token en una variable que vuelva a actualizar a medida que caduque.
El beneficio de agregarlo en el envío es que si agrega elementos a través de ajax, creo que seguirá funcionando sin tener que agregar nada más.
EDITAR: Aquí hay un gran artículo sobre el uso de Rails UJS con Laravel (que incluye esta funcionalidad de token CRSF ): https://medium.com/@barryvdh/unobtrusive-javascript-with-jquery-ujs-and-laravel-e05f444d3439
Hay un helper para agregar el token de formulario dentro de los formularios. Solo puedes usar
{!! csrf_field() !!}
Dentro de las formas. Se agregará la entrada oculta y el token.
No veo ningún inconveniente. Puede crear fácilmente un campo de token global en su archivo de diseño:
<input type="hidden" name="_token" id="csrf-token" value="{{ Session::token() }}" />
O si usas el creador de formularios:
{!! Form::token() !!}
En jQuery puede usar algo como this para adjuntar el token a cada solicitud.
Puedes usar algo como esto en la parte inferior de la página:
$(''form'').append(''{{csrf_field()}}'');
Esto agregará una entrada oculta a todos sus forms
:
<input type="hidden" name="_token" value="yIcHUzipr2Y2McGE3EUk5JwLOPjxrC3yEBetRtlV">
Y para todas tus peticiones AJAX:
$.ajaxSetup({
beforeSend: function (xhr, settings) {
//////////// Only for your domain
if (settings.url.indexOf(document.domain) >= 0) {
xhr.setRequestHeader("X-CSRF-Token", "{{csrf_token()}}");
}
}
});
csrf-token
pasar el encabezado X-XSRF-TOKEN
que contiene una versión encriptada del csrf-token
.
Hay dos maneras en que esto se puede hacer de las que estoy al tanto. Puede cifrar el token y pasarlo a la vista:
$xsrfToken = app(''Illuminate/Encryption/Encrypter'')->encrypt(csrf_token());
return view(''some.ajax.form.view'')->with(''xsrf_token'', $xsrfToken);
O puede tomar el token de las cookies usando JavaScript (Angular lo hace fácil). En vainilla JS podrías hacer algo como esto:
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
}
En jQuery puede hacer algo como esto para la solicitud ajax:
$.ajax({
// your request
//
beforeSend: function(request) {
return request.setRequestHeader(''X-XSRF-TOKEN'', getCookie(''XSRF-TOKEN''));
}
});