TokenMismatchException , despite excluding route - php

Im trying to create an img upload using ajax .
Im using the plugin https://github.com/Vinelab/mr-uploader ,and i'm getting CSRFTokenMismatchException .
Iv'e solved the issue before by adding the route to $execpet on verifyCsrf
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'/admin/upload'
];
}
Since solving the problem the first time i've reveted the whole project back to laravel 5 from laravel 5.1 (long story , i've had to do it )
now the problem appears agian .
when trying to send a post request to that route i get 'TokenMismatchException'.

Method 1: Disable CSRF Protection
Add the following code in VerifyCsrfToken.php
This route will ignored from checking csrf token..
public function handle($request, Closure $next)
{
//disable CSRF check on following routes
$skip = array(
'/admin/upload',
);
foreach ($skip as $key => $route) {
//skip csrf check on route
if($request->is($route)){
return parent::addCookieToResponse($request, $next($request));
}
}
return parent::handle($request, $next);
}
Method 2: Add CSRF TOKEN in ajax request
var token = "{{ csrf_token() }}";
$.ajax({
type : "POST",
url : "/admin/upload",
data : {_token:token},
});

Related

how to give CSRF in controller laravel

i have method to update, i test update via postman, in routing is in web.php not in api.php
if i using get in routing is it working, but i thing its not correct way, but when i using patch or put is showing "page expired" i know is it cause CSRF, but how i put csrf in controller ?
when i googling there is say
$request->session()->token().
bu how i use it
this is my code
route
Route::put('/customer/update/{id}', 'CustomerController#update');
controller
public function update(Request $request, $id){
$customer = Customer::find($id);
$customer->name = $request->name;
$customer->save();
}
my endpoint
http://127.0.0.1:8000/customer/update/42?name=maria50
i using in postman, patch/put not working only get
thank for help before
For testing you can use this to exclude CSRF protection for your path
https://laravel.com/docs/6.x/csrf#csrf-excluding-uris
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
];
}
But in production , you would use some form to post form data and there you can use #csrf
<form method="POST" action="/profile">
#csrf
...
</form>
Or you can set request header with value of CSRF
https://laravel.com/docs/6.x/csrf#csrf-x-csrf-token
'X-CSRF-TOKEN': 'value'

Exclude route from Slim CSRF middleware

I'm working on a Slim 3 based application with a Twig frontend and I'm also making a REST API.
I've implemented slimphp\Slim-Csrf for the entire app but I now want to exclude this CSRF check from every "API" routes.
I'm trying to implement the "Option 2" of this post :
Slim3 exclude route from CSRF Middleware
Here is the code :
File App\Middleware\CsrfMiddleware.php :
namespace App\Middleware;
class CsrfMiddleware extends \Slim\Csrf\Guard {
public function processRequest($request, $response, $next) {
// Check if this route is in the "Whitelist"
$route = $request->getAttribute('route');
if ($route->getName() == 'token') {
var_dump('! problem HERE, this middleware is executed after the CsrfMiddleware !');
// supposed to SKIP \Slim\Csrf\Guard
return $next($request, $response);
} else {
// supposed to execute \Slim\Csrf\Guard
return $this($request, $response, $next);
}
}
}
File app\app.php :
$app = new \Slim\App([
'settings' => [
'determineRouteBeforeAppMiddleware' => true
]
]);
require('container.php');
require('routes.php');
$app->add($container->csrf);
$app->add('csrf:processRequest');
File app\container.php :
$container['csrf'] = function ($container) {
return new App\Middleware\CsrfMiddleware;
};
File app\routes.php :
<?php
$app->get('/', \App\PagesControllers\LieuController::class.':home')->setName('home');
$app->post('/api/token', \App\ApiControllers\AuthController::class.'postToken')->setName('token');
When I do a POST request on http://localhost/slim3/public/api/token I've got :
Failed CSRF check!string(70) "! problem HERE, this middleware is executed after the CsrfMiddleware !"
Like if my CsrfMiddleware was executed after \Slim\Csrf\Guard...
Anyone has an idea ?
Thank you.
In Slim 3 the middleware is LIFO (last in first out).
Add the middleware in the opposite direction:
Before
$app->add($container->csrf);
$app->add('csrf:processRequest');
After
$app->add('csrf:processRequest');
$app->add($container->csrf);
Notice: The public directory should not be part of the url
Not correct: http://localhost/slim3/public/api/token
Correct: http://localhost/slim3/api/token
To skip the processing within the middleware, just return the $response object.
// supposed to SKIP \Slim\Csrf\Guard
return $response;
Here is how I achieved this with Slim 3.
1) Create a class that extends \Slim\Csrf\Guard as follows.
The CsrfGuardOverride class is key to enabling or disabling CSRF checking for a path. If the current path is whitelist'ed, then the __invoke() method just skips the core CSRF checking, and proceeds by executing the next middleware layer.
If the current path is not in the whitelist (i.e., CSRF should be checked), then the __invoke method defers to its parent \Slim\Csrf\Guard::__invoke() to handle CSRF in the normal manner.
<?php
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use \Slim\Csrf\Guard;
class CsrfGuardOverride extends Guard {
/**
* Invoke middleware
*
* #param ServerRequestInterface $request PSR7 request object
* #param ResponseInterface $response PSR7 response object
* #param callable $next Next middleware callable
*
* #return ResponseInterface PSR7 response object
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
// Set the name of the route we want whitelisted with a name
// prefix of 'whitelist'. check for that here, and add
// any path to the white list
$route = $request->getAttribute('route');
$routeName = $route->getName();
$whitelisted = strpos($routeName, 'whitelist');
// if url is whitelisted from being CSRF checked, then bypass checking by skipping directly to next middleware
if ($whitelisted !== FALSE) {
return $next($request, $response);
}
return parent::__invoke($request, $response, $next);
}
}
2) Register the CsrfGuardOverride class. Be sure to set settings.determineRouteBeforeAppMiddleware => true as this forces Slim to evaluate routes prior to executing any middleware.
// Method on App Class
protected function configureContainer(ContainerBuilder $builder)
{
parent::configureContainer($builder);
$definitions = [
'settings.displayErrorDetails' => true,
'settings.determineRouteBeforeAppMiddleware' => true,
// Cross-Site Request Forgery protection
\App\Middleware\CsrfGuardOverride::class => function (ContainerInterface $container) {
$guard = new \App\Middleware\CsrfGuardOverride;
$guard->setPersistentTokenMode(true); // allow same CSRF token for multiple ajax calls per session
return $guard;
},
'csrf' => DI\get(\App\Middleware\CsrfGuardOverride::class),
// add others here...
];
$builder->addDefinitions($definitions);
}
3) Add the path that you want to by bypass CSRF checking and give it a name with the prefix 'whitelist':
$app->post('/events/purchase', ['\App\Controllers\PurchaseController', 'purchaseCallback'])->setName('whitelist.events.purchase');

Refreshing authentication tokens for a Vue.js SPA using Laravel for the backend

I am building a single-page-app with Vue (2.5) using Laravel (5.5) as the backend. Everything works well, except for directly logging in again after having logged out. In this case, the call to /api/user (to retrieve the user's account information and to verify the user's identity once more) fails with a 401 unauthorized (even though the log-in succeeded). As a response, the user is bounced back directly to the login screen (I wrote this measure myself as a reaction to 401 responses).
What does work is to log out, refresh the page with ctrl/cmd+R, and then log in again. The fact that a page refresh fixes my problem, gives me reason to believe that I am not handling refresh of the X-CSRF-TOKEN correctly, or may be forgetting about certain cookies that Laravel uses (as described here ).
This is a snippet of the code of the login form that is executed after a user clicks the login button.
login(){
// Copy the form data
const data = {...this.user};
// If remember is false, don't send the parameter to the server
if(data.remember === false){
delete data.remember;
}
this.authenticating = true;
this.authenticate(data)
.then( this.refreshTokens )
.catch( error => {
this.authenticating = false;
if(error.response && [422, 423].includes(error.response.status) ){
this.validationErrors = error.response.data.errors;
this.showErrorMessage(error.response.data.message);
}else{
this.showErrorMessage(error.message);
}
});
},
refreshTokens(){
return new Promise((resolve, reject) => {
axios.get('/refreshtokens')
.then( response => {
window.Laravel.csrfToken = response.data.csrfToken;
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = response.data.csrfToken;
this.authenticating = false;
this.$router.replace(this.$route.query.redirect || '/');
return resolve(response);
})
.catch( error => {
this.showErrorMessage(error.message);
reject(error);
});
});
},
the authenticate() method is a vuex action, which calls the login endpoint at the laravel side.
The /refreshTokens endpoint simply calls this Laravel controller function that returns the CSRF token of the currently logged-in user:
public function getCsrfToken(){
return ['csrfToken' => csrf_token()];
}
After the tokens have been refetched, the user is redirected to the main page (or another page if supplied)
with this.$router.replace(this.$route.query.redirect || '/'); and there the api/user function is called to check the data of the currently logged in user.
Are there any other measures I should take to make this work, that I am overlooking?
Thanks for any help!
EDIT: 07 Nov 2017
After all the helpful suggestions, I would like to add some information. I am using Passport to authenticate on the Laravel side, and the CreateFreshApiToken middleware is in place.
I have been looking at the cookies set by my app, and in particular the laravel_token which is said to hold the encrypted JWT that Passport will use to authenticate API requests from your JavaScript application. When logging out, the laravel_token cookie is deleted. When logging in again directly afterwards (using axios to send an AJAX post request) no new laravel_token is being set, so that's why it doesn't authenticate the user. I am aware that Laravel doesn't set the cookie on the login POST request, but the GET request to /refreshTokens (which is not guarded) directly afterwards should set the cookie. However, this doesn't appear to be happening.
I have tried increasing the delay between the request to /refreshTokens and the request to /api/user, to maybe give the server some time to get things in order, but to no avail.
For completeness sake, here is my Auth\LoginController that is handling the login request server-side:
class LoginController extends Controller
{
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = '/';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
// $this->middleware('guest')->except('logout');
}
/**
* Get the needed authorization credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(\Illuminate\Http\Request $request)
{
//return $request->only($this->username(), 'password');
return ['email' => $request->{$this->username()}, 'password' => $request->password, 'active' => 1];
}
/**
* The user has been authenticated.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function authenticated(\Illuminate\Http\Request $request, $user)
{
$user->last_login = \Carbon\Carbon::now();
$user->timestamps = false;
$user->save();
$user->timestamps = true;
return (new UserResource($user))->additional(
['permissions' => $user->getUIPermissions()]
);
}
/**
* Log the user out of the application.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function logout(\Illuminate\Http\Request $request)
{
$this->guard()->logout();
$request->session()->invalidate();
}
}
Considering that you are using an api for authentication, I would suggest using Passport or JWT Authentication to handle authentication tokens.
Finally fixed it!
By returning the UserResource directly in the LoginControllers authenticated method, it is not a valid Laravel Response (but I guess raw JSON data?) so probably things like cookies are not attached. I had to attach a call to response() on the resource and now everything seems to work fine (though I need to do more extensive testing).
So:
protected function authenticated(\Illuminate\Http\Request $request, $user)
{
...
return (new UserResource($user))->additional(
['permissions' => $user->getUIPermissions()]
);
}
becomes
protected function authenticated(\Illuminate\Http\Request $request, $user)
{
...
return (new UserResource($user))->additional(
['permissions' => $user->getUIPermissions()]
)->response(); // Add response to Resource
}
Hurray for the Laravel docs on attributing a section to this:
https://laravel.com/docs/5.5/eloquent-resources#resource-responses
Additionally, the laravel_token is not set by the POST request to login, and the call to refreshCsrfToken() also didn't do the trick, probably because it was protected by the guest middleware.
What worked for me in the end is to perform a dummy call to '/' right after the login function returned (or the promise was fulfilled).
In the end, my login function in the component was as follows:
login(){
// Copy the user object
const data = {...this.user};
// If remember is false, don't send the parameter to the server
if(data.remember === false){
delete data.remember;
}
this.authenticating = true;
this.authenticate(data)
.then( csrf_token => {
window.Laravel.csrfToken = csrf_token;
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = csrf_token;
// Perform a dummy GET request to the site root to obtain the larevel_token cookie
// which is used for authentication. Strangely enough this cookie is not set with the
// POST request to the login function.
axios.get('/')
.then( () => {
this.authenticating = false;
this.$router.replace(this.$route.query.redirect || '/');
})
.catch(e => this.showErrorMessage(e.message));
})
.catch( error => {
this.authenticating = false;
if(error.response && [422, 423].includes(error.response.status) ){
this.validationErrors = error.response.data.errors;
this.showErrorMessage(error.response.data.message);
}else{
this.showErrorMessage(error.message);
}
});
and the authenticate() action in my vuex store is as follows:
authenticate({ dispatch }, data){
return new Promise( (resolve, reject) => {
axios.post(LOGIN, data)
.then( response => {
const {csrf_token, ...user} = response.data;
// Set Vuex state
dispatch('setUser', user );
// Store the user data in local storage
Vue.ls.set('user', user );
return resolve(csrf_token);
})
.catch( error => reject(error) );
});
},
Because I didn't want to make an extra call to refreshTokens in addition to the dummy call to /, I attached the csrf_token to the response of the /login route of the backend:
protected function authenticated(\Illuminate\Http\Request $request, $user)
{
$user->last_login = \Carbon\Carbon::now();
$user->timestamps = false;
$user->save();
$user->timestamps = true;
return (new UserResource($user))->additional([
'permissions' => $user->getUIPermissions(),
'csrf_token' => csrf_token()
])->response();
}
You should use Passports CreateFreshApiToken middleware in your web middleware passport consuming-your-api
web => [...,
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
this attaches attach the right csrftoken() to all your Request headers as request_cookies

Laravel 5: force to validate [GET] requests using csrf

by default Laravel 5 validate & match "tokens" for all [POST] requests, how to tell L5 to validate "GET, PUT & Delete" requests too?
-> prevent any request without valid token
thanks,
You can create your own middleware that will take care of it and replace the default Laravel VerifyCsrfToken class. In Laravel 5.3:
Create your new middleware php artisan make:middleware VerifyCsrfTokenAll
Replace the middleware class in app/Http/Kernel.php - search for protected $middlewareGroups and replace VerifyCsrfToken::class by your new middleware. So it can look like this:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfTokenAll::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
...
In app/Http/Middleware/VerifyCsrfTokenAll.php make it extend original verifier and just override the isReading() method as this one is responsible for bypassing the GET requests. Something like this depending on your use case:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfTokenAll extends BaseVerifier
{
/**
* Determine if the HTTP request uses a ‘read’ verb.
*
* #param \Illuminate\Http\Request $request
* #return bool
*/
protected function isReading($request)
{
return false;
// return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']);
}
}
If you only wanted to validate in on certain routes, it is better to do it as a route middleware as in my case - I created a VerifyCsrfTokenGet middleware and assigned it in app/Http/Kernel to $routeMiddleware group like this:
protected $routeMiddleware = [
'csrf_get' => \App\Http\Middleware\VerifyCsrfTokenGet::class,
...
In app/Http/MIddleware/VerifyCsrfTokenGet.php I did the verification:
public function handle($request, Closure $next)
{
// check matching token from GET
$sessionToken = $request->session()->token();
$token = $request->input('_token');
if (! is_string($sessionToken) || ! is_string($token) || !hash_equals($sessionToken, $token) ) {
throw new \Exception('CSRF token mismatch exception');
}
return $next($request);
}
and finally assigned this to any route as a csrf_middleware whereever I want to validate it, eg. in constructor of some of the controllers:
class InvoicesController extends Controller
{
function __construct()
{
// define middleware
$this->middleware('csrf_get', ['only' => ['pay', 'createmail']]);
}
"csrf token" is just an ordinary session value with a key name "_token" ,you can just get and reset this value directly.
like this:
$token = $this->request->get('_token');
if(is_null($token) || $token!=csrf_token())
throw new AppException('illegal_pay_operation');
else
Session::regenerateToken();
Laravel validate the token for POST, PUT and DELETE. You don't need to validate the token for a GET request if you follow a RESTful system.
From the documentation:
You do not need to manually verify the CSRF token on POST, PUT, or DELETE requests. The VerifyCsrfToken HTTP middleware will verify token in the request input matches the token stored in the session.
http://laravel.com/docs/5.1/routing#csrf-protection

Laravel cURL POST Throwing TokenMismatchException

I have a problem with POST cURL request to my application.
Currently, I'm building RESTFUL registration function using laravel 5.
The routes for this is example is
localhost:8000/user/create
I pass value using cURL function on terminal
curl -d 'fname=randy&lname=tan&id_location=1&email=randy#randytan.me&password=randytan&remember_token=Y&created_at=2015-03-03' localhost:8000/auth/register/
And this is my routes.php
Route::post('user/create', 'UserController#create');
And this is my function to store the registration user
public function create()
{
//function to create user.
$userAccounts = new User;
$userAccounts->fname = Request::get('fname');
$userAccounts->lname = Request::get('lname');
$userAccounts->id_location = Request::get('id_location');
$userAccounts->email = Request::get('email');
$userAccounts->password = Hash::make(Request::get('password'));
$userAccounts->created_at = Request::get('created_at');
$userAccounts->save();
return Response::json(array(
'error' => false,
'user' => $userAccounts->fname . " " . $userAccounts->lname
), 200);
}
Executing the cURL syntax above, I'm getting this error TokenMismatchException
Do you have any ideas?
Because I'm implementing middleware only in my few urls, and this cURL registration url is not tight into any authentication mechanism.
Thanks before.
In Laravel 5 (latest version) you can specify routes you want to exclude in /app/Http/Middleware/VerifyCsrfToken.php
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'rest-api/*', # all routes to rest-api will be excluded.
];
}
Hope this helps.
Laravel 5 enforces CSFR token authentication in middleware by default.
you can disable CSFR on selected route Here is the link
or you can try some of these solutions. Hope so it will help.
changing your csfr token method /app/Http/Middleware/VerifyCsrfToken.php
public function handle ($request, Closure $next)
{
if ( !$request->is("api/*"))
{
return parent::handle($request, $next);
}
return $next($request);
}
In my case, i needed to add the route on api.php instead of web.php

Categories