Search code examples
laravelcsrflaravel-5.1

Requests to /autodiscover/autodiscover.xml causing TokenMismatchException


I have a Laravel app that is getting a reasonable amount of traffic.

To more gracefully handle exceptions of the TokenMismatchException type (which tend to get thrown when the session lifetime expires and then a user submits a form), I have altered the render() method of the exception handler, as follows:

/**
 * Render an exception into an HTTP response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  \Exception  $e
 * @return \Illuminate\Http\Response
 */
public function render($request, Exception $e)
{
    if ($e instanceof \Illuminate\Session\TokenMismatchException){

        return redirect($request->fullUrl())->with('error',"Sorry your session has expired please resubmit your request.");
    }

    return parent::render($request, $e);
}

This is working fine for normal requests, and instead of throwing the exception, it sets a session flash message and redirects back to the requested page. However, I'm noticing lots of these exceptions still being thrown for the following request:

/autodiscover/autodiscover.xml

I'm aware the above is something to do with Exchange, so it's probably not malicious.

What I'm confused about is a) why is this unrouted url triggering Laravel's CSRF protection and b) why is my updated handler not catching the exception?

I've tried adding a route for this url, and manually throwing a 404, but this hasn't helped.

What can I do to prevent these exceptions being thrown?

Edit - stack trace as requested:

2015-10-26 11:44:38] production.ERROR: exception 'Illuminate\Session\TokenMismatchException' in /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php:53
Stack trace:
#0 [internal function]: Illuminate\Foundation\Http\Middleware\VerifyCsrfToken->handle(Object(Illuminate\Http\Request), Object(Closure))
#1 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(124): call_user_func_array(Array, Array)
#2 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php(54): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#3 [internal function]: Illuminate\View\Middleware\ShareErrorsFromSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#4 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(124): call_user_func_array(Array, Array)
#5 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php(62): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#6 [internal function]: Illuminate\Session\Middleware\StartSession->handle(Object(Illuminate\Http\Request), Object(Closure))
#7 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(124): call_user_func_array(Array, Array)
#8 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php(37): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#9 [internal function]: Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse->handle(Object(Illuminate\Http\Request), Object(Closure))
#10 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(124): call_user_func_array(Array, Array)
#11 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php(59): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#12 [internal function]: Illuminate\Cookie\Middleware\EncryptCookies->handle(Object(Illuminate\Http\Request), Object(Closure))
#13 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(124): call_user_func_array(Array, Array)
#14 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php(42): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#15 [internal function]: Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode->handle(Object(Illuminate\Http\Request), Object(Closure))
#16 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(124): call_user_func_array(Array, Array)
#17 [internal function]: Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(Illuminate\Http\Request))
#18 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(103): call_user_func(Object(Closure), Object(Illuminate\Http\Request))
#19 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(122): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#20 /var/www/vhosts/sitedomain.com/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(87): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter(Object(Illuminate\Http\Request))
#21 /var/www/vhosts/sitedomain.com/public_html/index.php(53): Illuminate\Foundation\Http\Kernel->handle(Object(Illuminate\Http\Request))
#22 {main}  

Solution

  • So, the solution turns out to be relatively simple. It seems like it's Laravel's default behaviour to handle all post (and presumably put and delete...) requests, whether they are defined in a route or not.

    So we can add an exception to this in the VerifyCsrfToken middleware $except array:

    protected $except = [
        'autodiscover/autodiscover.xml'
    ]
    

    After adding the above, my TokenMismatchException is no longer thrown.