Laravel CSRF Token - laravel

EDIT: I should have said this at the start, I'm using AngularJS in the FronEnd, and I'm making all the request via XHR.
I'm developing an Application using CSRF Token for every user request.
Should I regenerate the Token after each request?
Something like
Session::forget("_token") and Session::put("_token", RANDOM_SOMETHING)
Or is it enough to use the same one each user Session?
Is there any benefit?

With Laravel 5 using Blades templates, it's pretty easy.
If you only want the value of the csrf token, you can generate it by writing:
{{ csrf_token() }}
which generates the token value like this:
7YC0Sxth7AYe4RFSjzaPf2ygLCecJhPbyXhz6vvF
If you are using forms, you can add the following line of code inside the form:
{{ csrf_field() }}
which will generate html like this:
<input type="hidden" name="_token" value="7YC0Sxth7AYe4RFSjzaPf2ygLCecJhblahblah">

Laravel should be doing this for you, you don't need to manage the creation / deletion of _token
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
See the 'CSRF Protection' section in the docs here: http://laravel.com/docs/security

If you are using Laravel 5.6, do the following at the top of forms to create hidden input field for the CSRF token
#csrf

Depends. If the attacker is not MITM, in the sense that they cannot eavesdrop on traffic between your web app and the API server, a single CSRF token for the entire session should be enough.
Assuming you guard sensitive operations on the server-side too (i.e. allow access to resources only to the owner of the resource, e.g. "delete my account", etc.) the token would ensure that the browser making the request is the legitimate, authenticated user's browser. That's all you should worry about, I think.
On the other hand, if the attacker is capable of looking at non-secure traffic between the web app and your API, they may get hold of the CSRF token and your session_id and do evil stuff transparently. In such case granting, using and subsequently discarding a token for each request (POST, or any kind that does sensitive operation) only makes their job a bit more difficult, but you're still doomed.
My 2 cents...

CSRF token prevents Cross-Site attack by comparing cookie token with
server token.
You can generate csrf token in laravel by csrf_token() helper function. If you want full csrf fields then you can use csrf_field() function and csrf internal logic is
function csrf_field()
{
return new HtmlString('<input type="hidden" name="_token" value="'.csrf_token().'">');
}
When new request will generate then laravel create random token every time and store in browser cookie and session after stored Its compare to each other like cookie == session token
Laravel Internal logic is following and you can find it in VerifyCsrfToken Middleware.
/**
* Determine if the session and input CSRF tokens match.
*
* #param \Illuminate\Http\Request $request
* #return bool
*/
protected function tokensMatch($request)
{
$token = $this->getTokenFromRequest($request);
return is_string($request->session()->token()) &&
is_string($token) &&
hash_equals($request->session()->token(), $token);
}
/**
* Get the CSRF token from the request.
*
* #param \Illuminate\Http\Request $request
* #return string
*/
protected function getTokenFromRequest($request)
{
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
$token = $this->encrypter->decrypt($header);
}
return $token;
}
/**
* Add the CSRF token to the response cookies.
*
* #param \Illuminate\Http\Request $request
* #param \Symfony\Component\HttpFoundation\Response $response
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function addCookieToResponse($request, $response)
{
$config = config('session');
$response->headers->setCookie(
new Cookie(
'XSRF-TOKEN', $request->session()->token(), $this->availableAt(60 * $config['lifetime']),
$config['path'], $config['domain'], $config['secure'], false, false, $config['same_site'] ?? null
)
);
return $response;
}

If you want to get the CSRF Token in the controller so you can just use it like this and redirect the post Route
$CSRFToken = csrf_token();
Easy Peasy
Hope it helps you

Related

Why do _token and XSRF-TOKEN differ in Laravel?

I don't understand why is the token for AJAX requests (XSRF-TOKEN) different from a _token that normal forms use. In addition, it's much longer. Why? And why have 2 tokens at all? Why not just use one which would be same for both ajax and normal requests?
1 Approach, 2 Technics
Laravel Uses 2 distinct Technics to prevent CSRF Attack.
The Approaches are The same:
to send a token (CSRF or XSRF) to The Client and Client Have to return
it back in following request
and there are 2 steps:
server sends token (get a form) (CSRF or XSRF)
client return token as X-token (post a form) (X-CSRF or X-XSRF)
when you see an X- token its an client-replied that client sends with Post to the server
The Reason we have 2 technics is not these uses different approaches,
its because web application Client-Side Architectures using 2 different Architectures :
old-fashion : server generates pure html and send it to client
Single Page Application : client SPA Framework (like Vue,React,Angular) send and receive data as Json or Xml and create proper Html in Dom
Now CSRF-Protection Technics Adapts with this Two Client-Side Architectures as Below:
+-------------+-----------------+-----------+------------+
| Client Arch | Protection Tech | Get Token | Post Token |
+-------------+-----------------+-----------+------------+
| old-fashion | sync-token | CSRF | X-CSRF |
| SPA | cookie-header | XSRF | X-XSRF |
+-------------+-----------------+-----------+------------+
Mechanism Description
1.Server Generates Token
Laravel make a CSRF Token (40 chars) and store it in session
/**
* Regenerate the CSRF token value.
*
* #return void
*/
public function regenerateToken()
{
$this->put('_token', Str::random(40));
}
After Generating and Storing token in Session, Token Will be Send To Client as CSRF and XSRF
client side will decide to use whatever it wants.
2.Server Sends Token To Client
for the old-fashioned (sync-token technic) client can receive The CSRF Token in two forms with call to csrf_token() helper method in blade:
in form body : <input type='hidden' name='_token' value='{{csrf_token()}}' />
in meta tag that Ajax request can use it in its header
here is how this helper method returns corresponding value:
/**
* Get the CSRF token value.
*
* #return string
*
* #throws \RuntimeException
*/
function csrf_token()
{
$session = app('session');
if (isset($session)) {
return $session->token();
}
throw new RuntimeException('Application session store not set.');
}
for cookie-header (SPA Frameworks) client framework (like Angular) can receive XSRF Token in the Cookie Because:
there is no Html Form generating in the server which server can seed
its hidden input in it. and The Way it can send its token to the
client is sending it with cookie. (This method named XSRF)
/**
* Add the CSRF token to the response cookies.
*
* #param \Illuminate\Http\Request $request
* #param \Symfony\Component\HttpFoundation\Response $response
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function addCookieToResponse($request, $response)
{
$config = config('session');
$response->headers->setCookie(
new Cookie(
'XSRF-TOKEN', $request->session()->token(), $this->availableAt(60 * $config['lifetime']),
$config['path'], $config['domain'], $config['secure'], false, false, $config['same_site'] ?? null
)
);
return $response;
}
Laravel put token in both places since its up to client which method to use, and expect client to response to one of this methods.
3.Client Sends X- Token To Server
In client-side:
old-fashion (X-CSRF):
post token in post data or:
make ajax call like this:
`$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});`
SPA Framework : These Framework put Token as X-XSRF-TOKEN in Post Headers
Server Checks X- Token Vs in-session Token
Now Its Time To Laravel Check For The Token
in VerifyCSRFMiddleware, Laravel Checks if The Request Should Be Check For CSRF Protection Token it Checks :
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*
* #throws \Illuminate\Session\TokenMismatchException
*/
public function handle($request, Closure $next)
{
if (
$this->isReading($request) ||
$this->runningUnitTests() ||
$this->inExceptArray($request) ||
$this->tokensMatch($request) //compares request_token vs session_token
) {
return tap($next($request), function ($response) use ($request) {
if ($this->shouldAddXsrfTokenCookie()) {
$this->addCookieToResponse($request, $response); //add cookie to response
}
});
}
throw new TokenMismatchException('CSRF token mismatch.');
}
Two of lines are in interest:
$this->tokensMatch($request)
and
$this->addCookieToResponse($request, $response);
so there are multiple data in each request that server can put:
html form input _token (40 chars) (CSRF)
html meta header csrf-token (40 chars) (CSRF)
cookie XSRF-TOKEN (224 chars) (XSRF)
and multiple data can client send to server as response to the tokens
post parameter _token (40 chars) (X-CSRF)
http header X-CSRF-TOKEN (40 chars) (X-CSRF)
http header X-XSRF-TOKEN (224 chars) (X-XSRF)
Why in CSRF token are 40 chars and in XSRF are 224 chars ?
We Will get to this a little bit latter
The Http Request Has To Match Token with one of the Above X-Token
/**
* Determine if the session and input CSRF tokens match.
*
* #param \Illuminate\Http\Request $request
* #return bool
*/
protected function tokensMatch($request)
{
$token = $this->getTokenFromRequest($request);// it get token from request
return is_string($request->session()->token()) &&
is_string($token) &&
hash_equals($request->session()->token(), $token); //checks if it is equal to session token or not
}
/**
* Get the CSRF token from the request.
*
* #param \Illuminate\Http\Request $request
* #return string
*/
protected function getTokenFromRequest($request)
{
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');//check sync-token
if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
$token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized()));
}
return $token;
}
first pattern to examine is sync-token, token from client can be in an <input name='_token' /> or it can be in Http Header if requested from an Ajax method call in the client.
the line
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
will check for that and if it can be retrieved, it will return and check via the session_token
but if (! $token is NULL it will check against the cookie-header pattern:
getting $header = $request->header('X-XSRF-TOKEN') from header and decrypt it, if its need decryption
$token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized()));
if it has been encrypted before it has been added to cookie
Cookie Encryption
This is The Reason That XSRF Token Could be 224chars :
Cookie Encryption
and you may disable cookie Encryption and make the XSRF Token 40 chars, Like The CSRF Token
so The Difference was for The Cookie Encryption.
Necessity of Cookie Encryption
But Why Cookie Needs to get Encrypted?? Why XSRF Cookie needs to get Encrypted??
In General, Laravel Store some data on cookies and cookie can be modified by client. because server dose not want modifications on client, it Encrypt Cookies .
this can be config to not to Encrypt CSRF Cookie Since it is not subjected to change by user and its only subjected to be stole by cookie hijacking which encryption is not going to preventing this event.
The only Difference its make is to having to token (unencrypted and encrypted)
for two CSRF Protection methods.
So if attackers can access a cookie-stored (X-XSRF) Token (Since Hijacking > Cookie is much easier to hijacking runtime html and css with XSS )
it cannot be Abuse With sync-token mechanism.
Since CSRF Attack with http-form parameter is easier since html can be in email
or etc While Runnig Js is Less common.
Conclusion
So if a client use old-fashion client architect . the cookie-header technic > ( XSRF stored in Cookie ) wont leave him with a data leak in cookie.
further information on this prevention patterns can be found here:
https://en.wikipedia.org/wiki/Cross-site_request_forgery#Prevention
If you take a look at vendor/laravel/framework/src/Illuminate/Session/Store.php there is a method named regenerateToken which will make the token for the application
/**
* Regenerate the CSRF token value.
*
* #return void
*/
public function regenerateToken()
{
$this->put('_token', Str::random(40));
}
Then the token that you get either from session or JS(which is also from the session), they all have the same 40 char length and the token is just a simple random 40 character which is stored in your session. it's not encrypted or hashed because only you, the user have the access to the session since if I want to CSRF attack from any outside source the source does not have access to the session so basically it does not required to hash or encrypt that 40 length token.
Side note: token is not encrypted by itself, all the sessions are encrypted in laravel by default.
Heres what the documentation says about the XSRF:
https://laravel.com/docs/8.x/csrf#csrf-x-xsrf-token
So basically its just same value of _token encrypted and saved in cookies
The short answer is that XSRF-TOKEN is encrypted CSRF-TOKEN, that's just what it is. Encryption will end up in a much longer string than the original.
The reason it exists to begin with is (as mentioned in the docs):
This cookie is primarily sent as a convenience since some JavaScript frameworks and libraries, like Angular and Axios, automatically place its value in the X-XSRF-TOKEN header on same-origin requests.
This means when using a framework or library that automatically sets the X-XSRF-TOKEN header in a request if the XSRF-TOKEN cookie is present then you won't have to worry about passing along the CSRF token on every request since it is done automatically.
Personally I'm not 100% sure that the statement in the manual is currently accurate or if it's actually useful. Personally I've ended up removing this cookie completely because of a badly configured firewall some of our clients use which strip long cookies, and so far I haven't missed it.

How to refresh csrf token on each request without session expire issue in Laravel 5.4

I want to refresh the CSRF token on each post request in Laravel 5.4
Added code from below SO link, still not helping
How to generate new CSRF Token for each user request in Laravel?
protected function addCookieToResponse($request, $response)
{
$response = next($request); // process petition
$request->session()->regenerateToken(); // regenerate token
return $response; // send response
}
Try this instead
protected function addCookieToResponse($request, $response)
{
session()->regenerateToken();
return parent::addCookieToResponse($request, $response);
}
Although this is not advised as it will break usuability, csrf_token is outputted before the new token is refreshed causing mismatch exception, would only work if you're extracting the token from the cookie with Javascript and not using the blade #csrf directive or csrf_token() helper function

Laravel Passport API registering new users

I'm trying to build a login-system for my web-app, but I can't get Passport to work. The app is build as a REST API, so users should be able to register with an email and password, and after this they should be able to login with these credentials (so I think they will need to receive an access token from Passport when the login credentials are correct).
I thought I could just do a JSON post to a 'register' route to register a new user and then do a post to a 'login' route to get the access token back to the client, but there is no such thing as far as I can tell.
How do I register a new user?
If you're building SPA and using default Laravel register, login, forgot password, & reset password web functionality like Google Account for authentication & authorization purpose, you can override the registered method on App\Http\Controllers\Auth\RegisterController.php with redirect logic when intended url is exists.
This lines tells Laravel to look over intended url, prior navigation to the web register controller redirect path.
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
if ($request->session()->has('url.intended')) {
return redirect()->intended();
}
}
For example I'm using authorization code with PKCE grant on my Vue.js SFC
<template>
<v-app-bar app flat>
<button-login #login="authorize"></button-login>
<span>|</span>
<button-register #register="authorize"></button-register>
</v-app-bar>
</template>
<script>
import ButtonLogin from '#/components/Buttons/ButtonLogin'
import ButtonRegister from '#/components/Buttons/ButtonRegister'
import { base64URL, encrypt, hashMake, randomString } from '#/helpers'
import sha256 from 'crypto-js/sha256'
import httpBuildQuery from 'http-build-query'
import { SERVICE } from '#/config/services'
import { STORAGE_API_AUTHORIZATION_STATE, STORAGE_API_CODE_VERIFIER } from '#/config/storage'
export default {
name: 'AppBar',
components: {
ButtonLogin,
ButtonRegister
},
authorize() {
const authorizationState = randomString(40)
const codeVerifier = randomString(128)
const codeChallenge = base64URL(sha256(codeVerifier))
const query = httpBuildQuery({
client_id: SERVICE.CLIENT_ID,
redirect_uri: authorizationURL,
response_type: 'code',
scope: '*',
state: authorizationState,
code_challenge: codeChallenge,
code_challenge_method: 'S256'
})
localStorage.setItem(
STORAGE_API_AUTHORIZATION_STATE,
hashMake(authorizationState)
)
localStorage.setItem(
STORAGE_API_CODE_VERIFIER,
encrypt(codeVerifier, authorizationState)
)
location.href = `${SERVICE.API_URL}/oauth/authorize?${query}`
}
}
</script>
Whenever user click on login/register button on my SPA it'll redirect to my API OAuth authorization page.
The authenticate middleware will intercept the request and check for the logged in state of user, if user is not authenticated then it'll redirect user to the login page.
If user choose to register his/her account by clicking on the register button, we will redirect user to the web registration page (still on API not on SPA).
After the user is registered, the controller will call registered method and check for intended URL existence, if exists then we are able to redirect user to the intended url (the oauth/authorize endpoint), and the authorization process can be continued after registration process.
I'm facing the same problem here, and by now the best solution I've found is to create the register method manually by creating a UserController and a store method like this
public function store(Request $request) {
$data=$request->only('name', 'email','password');
$valid = validator(
$data, [
'name' => 'required|string|max:255',',
'email' => 'required|string|email|max:155|unique:users',
'password' => 'required|string|min:4',
]);
$return=null;
if ($valid->fails()) {
$return = response()->json($valid->errors()->all(), 400);
}else{
$data['password']=Hash::make($data['password']);
#return = User::create($data);
}
return $return;
}

Laravel passport change header authentication

I am using Laravel passport and it requires to send in every request the header Authentication to be sent.
Is it possible to change the name of the header to X-Access-Token?
I saw passport uses the package
League\OAuth2\Server\AuthorizationValidators;
method:
/**
* {#inheritdoc}
*/
public function validateAuthorization(ServerRequestInterface $request)
{
dd($request);
if ($request->hasHeader('authorization') === false) {
throw OAuthServerException::accessDenied('Missing "Authorization" header');
}
$header = $request->getHeader('authorization');
$jwt = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header[0]));
I tried to change here but seems the validation of the headers happen before this method.
There are many fundamental pieces of code that rely on the existence of the authorization header.
You could roll your own if you felt so inclined.
Note also that authorization is a web standard request header. X-Access-Token is a response header pattern.
*Edit**
Given our conversation below, you can use Middleware and Middleware priority to dictate which runs first, observe requests that have an X-Access-Token and use addHeader to convert the value of that header to authorization:
php artisan make:middleware AuthorizationToolMiddleware
Then in the handle function:
public function handle($request, Closure $next)
{
$request->headers->set('Authorization', $request->headers->get('X-Access-Token'));
return $next($request);
}
This middleware should execute before other middleware in order to ensure the headers are set by the time that passport handles the request.
For Laravel 5.8 you'd have to force your custom middleware to always be on top of the call chain
So in your app\kernel.php add this -
protected $middlewarePriority = [
\App\Http\Middleware\AuthorizationToolMiddleware::class,
];

Sending POST request while using jwt with laravel 5.2

I am using jwt:auth with laravel 5.2 to authenticate in a secure manner against CSRF attacks.
You can find how to do here >> http://blog.nedex.io/create-an-api-server-for-mobile-apps-using-laravel-5-1/
I have edited the except array in VerifyCsrfToken.php middleware and added api/login and api/signup, so i can skip jwt tokens for these two actions because while i am trying to login or signup, so i still have no keys, yet.
Route::group(['prefix' => 'api'], function() {
Route::post('login', 'Api\AuthController#login');
Route::post('signup', 'Api\UsersController#signup');
Route::group(['middleware' => ['jwt.auth', 'jwt.refresh']], function() {
Route::post('logout', 'Api\AuthController#logout');
Route::get('test', function(){
return response()->json(['foo'=>'bar']);
});
Route::get('hospitals', 'Api\EmergenciesController#getHospitals');
Route::get('emergencyDetails/{id}', 'Api\EmergenciesController#getEmergencyDetails');
Route::get('profile/{id}', 'Api\UsersController#get_profile');
Route::post('submit_profile/{id}', 'Api\UsersController#submit_profile_data');
Route::post('update_property/{id}/{property}/{value}', 'Api\UsersController#update_property');
Route::post('pregnant/{id}', 'Api\UsersController#update_is_pregnant');
});
});
it works perfectly, when i send GET requests with ?token=
But it gives me a TokenMismatchException when i try to send POST request although i am sending the token.
check the screenshots here >> https://www.dropbox.com/sh/a901epayh1liapd/AABCwxxBN4pSG735SxQlC2jha?dl=0
Why POST requests fail?? please help.!
It happens because Laravel CSRF Protection is enabled default for all routes.You can disable it for you POST request or send CSRF token throw 'X-XSRF-TOKEN' header. But it seems that you don't have it, because you use jwt token(it isn't same). I suggest you exclude you API paths from csrf middleware :
Excluding URIs From CSRF Protection
Sometimes you may wish to exclude a set of URIs from CSRF protection. For example, if you are using Stripe to process payments and are utilizing their webhook system, you will need to exclude your webhook handler route from Laravel's CSRF protection.
You may exclude URIs by defining their routes outside of the web middleware group that is included in the default routes.php file, or by adding the URIs to the $except property of the VerifyCsrfToken middleware:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'stripe/*',
];
}
Just add new paths like
protected $except = [ 'api/login', 'api/signup', 'api'/logout' , 'api/test', 'api/hospitals']; //etc
For post request you need to pass _token hidden field.
<input type="hidden" name="_token" value="{{ csrf_token() }}" />

Resources