Asked  7 Months ago    Answers:  5   Viewed   37 times

Can the TokenMismatchException be catched using try catch block? Instead of displaying the debug page that shows the "TokenMismatchException in VerifyCsrfToken.php line 46...", I want it to display the actual page and just display an error message.

I have no problems with the CSRF, I just want it to still display the page instead of the debug page.

To replicate (using firefox): Steps:

  1. Open page (http://example.com/login)
  2. Clear Cookies (Domain, Path, Session). I am using web developer toolbar plugin here.
  3. Submit form.

Actual Results: "Whoops, looks like something went wrong" page displays. Expected Results: Still display the login page then pass an error of "Token mismatch" or something.

Notice that when I cleared the cookies, I didn't refresh the page in order for the token to generate a new key and force it to error out.

UPDATE (ADDED FORM):

        <form class="form-horizontal" action="<?php echo route($formActionStoreUrl); ?>" method="post">
        <input type="hidden" name="_token" value="<?php echo csrf_token(); ?>" />
        <div class="form-group">
            <label for="txtCode" class="col-sm-1 control-label">Code</label>
            <div class="col-sm-11">
                <input type="text" name="txtCode" id="txtCode" class="form-control" placeholder="Code" />
            </div>
        </div>
        <div class="form-group">
            <label for="txtDesc" class="col-sm-1 control-label">Description</label>
            <div class="col-sm-11">
                <input type="text" name="txtDesc" id="txtDesc" class="form-control" placeholder="Description" />
            </div>
        </div>
        <div class="form-group">
            <label for="cbxInactive" class="col-sm-1 control-label">Inactive</label>
            <div class="col-sm-11">
                <div class="checkbox">
                    <label>
                        <input type="checkbox" name="cbxInactive" id="cbxInactive" value="inactive" />&nbsp;
                        <span class="check"></span>
                    </label>
                </div>
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-12">
                <button type="submit" class="btn btn-primary pull-right"><i class="fa fa-save fa-lg"></i> Save</button>
            </div>
        </div>
    </form>

Nothing really fancy here. Just an ordinary form. Like what I've said, the form is WORKING perfectly fine. It is just when I stated the above steps, it errors out due to the TOKEN being expired. My question is that, should the form behave that way? I mean, when ever I clear cookies and session I need to reload the page too? Is that how CSRF works here?

 Answers

48

You can handle TokenMismatchException Exception in AppExceptionsHandler.php

<?php namespace AppExceptions;
use Exception;
use IlluminateFoundationExceptionsHandler as ExceptionHandler;
use IlluminateSessionTokenMismatchException;


class Handler extends ExceptionHandler {


    /**
     * A list of the exception types that should not be reported.
     *
     * @var array
     */
    protected $dontReport = [
        'SymfonyComponentHttpKernelExceptionHttpException'
    ];
    /**
     * Report or log an exception.
     *
     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
     *
     * @param  Exception  $e
     * @return void
     */
    public function report(Exception $e)
    {
        return parent::report($e);
    }
    /**
     * Render an exception into an HTTP response.
     *
     * @param  IlluminateHttpRequest  $request
     * @param  Exception  $e
     * @return IlluminateHttpResponse
     */
    public function render($request, Exception $e)
    {
        if ($e instanceof TokenMismatchException){
            // Redirect to a form. Here is an example of how I handle mine
            return redirect($request->fullUrl())->with('csrf_error',"Oops! Seems you couldn't submit form for a long time. Please try again.");
        }

        return parent::render($request, $e);
    }
}
Wednesday, March 31, 2021
 
qitch
answered 7 Months ago
40

If you're on Laravel 5.2 and using MySQL, there was a bit of a "bug" introduced with the timestamps. You can read all about the issue on github here. It has to do with the timestamp defaults, and MySQL automatically assigning DEFAULT CURRENT_TIMESTAMP or ON UPDATE CURRENT_TIMESTAMP attributes under certain conditions.

Basically, you have three options.

  1. Update MySQL variable:

If you set the explicit_defaults_for_timestamp variable to TRUE, no timestamp column will be assigned the DEFAULT CURRENT_TIMESTAMP or ON UPDATE CURRENT_TIMESTAMP attributes automatically. You can read more about the variable here.

  1. Use nullable timestamps:

Change $table->timestamps() to $table->nullableTimestamps(). By default, the $table->timestamps() command creates timestamp fields that are not nullable. By using $table->nullableTimestamps(), your timestamp fields will be nullable, and MySQL will not automatically assign the first one the DEFAULT CURRENT_TIMESTAMP or ON UPDATE CURRENT_TIMESTAMP attributes.

  1. Define the timestamps yourself:

Instead of using $table->timestamps, use $table->timestamp('updated_at'); $table->timestamp('created_at'); yourself. Make sure your 'updated_at' field is the first timestamp in the table, so that it will be the one that is automatically assign the DEFAULT CURRENT_TIMESTAMP or ON UPDATE CURRENT_TIMESTAMP attributes.

Wednesday, March 31, 2021
 
MassiveAttack
answered 7 Months ago
41

When I realized this was only happening in IE and Chrome, but not Firefox, it led me to the fix. The app was using AddThis share buttons and the javascript was adding an iframe to the pages. This issue is resolved by adding a P3P header to the VerifyCsrfToken Middleware. Hope this saves somebody the hours I lost.

public function handle($request, Closure $next)
    {
        $response = $next($request);

        if (last(explode('\',get_class($response))) != 'RedirectResponse') {
            $response->header('P3P', 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
        }

        return $response;
    }
Wednesday, March 31, 2021
 
Puneet
answered 7 Months ago
99

Generally API's are used for cross site requests. So your CSRF protection is pointless.

If you're not gonna use it cross-site, chances are that an API is not the optimal solution for what you're trying to do. Anyway, you could make an API endpoint which returns a token.

public function getToken(){
    return Response::json(['token'=>csrf_token()]);
}

If you want to disable CSRF-protection on some methods, you could use except or only.

$this->beforeFilter('csrf', array('on' => 'post', 
                                 'except'=>array('methodName', 'anotherMethod')
                                  ));

Please refer to the official Laravel documentation.

Saturday, May 29, 2021
 
samrap
answered 5 Months ago
78

Login function needs user of type Authenticatable and you just given email which is string thats why you get this error, Either use Auth::loginUsingId($id);

 $user = User::where('email','=',$email)->first();
 Auth::loginUsingId($user->id, TRUE);

Or just

Auth::login($user);
Friday, August 13, 2021
 
lechup
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :