Search code examples
yii2csrf

CSRF validation in Yii2 without cookies


In my Yii2 application there is a form that users submit. The site has CSRF validation turned but if the user has cookies disabled, an error is thrown. What is the best way to handle a situation like this? I don't really want to disable CSRF validation to be sure that the form is only submitted from my site. There is no login process involved. It is simply a one-time use situation for the user. Thanks.


Solution

  • You can force your application to store the CSRF token in session instead of cookie by setting enableCsrfCookie property of yii\web\Request component to false. You can do that in your web.php config:

    $config = [
        // ...
        'components' => [
            'request' => [
                'enableCsrfCookie' => false,
            ],
            // ...
        ],
        // ...
    ];
    

    But this will cause app to start the session for every request. Also sessions by default uses cookies for storing session id so in the end it might not be much improvement if you want to avoid using any cookies.

    You can also extend the yii\web\Request and override its methods generateCsrfToken() and loadCsrfToken() to use different storage for token. But rembeber that by default there is no way to recognize which requests came from same session. You have to save some sort of sessionId in client and then send it back to server in order to be able to pair the request that submits form with CSRF token and the request that generated the CSRF token.

    Another posibility is to turn off the standard CSRF protection and implement your own protection for form. For example you can have some secret key stored in app config. Then when generating form, you will use yii\base\Security::hashData() method to add the timestamp to your form:

    $timestamp = Yii::$app->security->hashData(time(), $yourSecurityKey);
    Html::hiddenInput('my-csrf', $timestamp);
    

    Then you can validate it in your controller action:

    $timestamp = Yii::$app->security->validateData(
       Yii::$app->request->post('my-csrf'),
       $yourSecurityKey
    );
    
    if ($timestamp === false || $timestamp < (time() - $timeLimitInSeconds)) {
         //Do something when CSRF validation is not valid (for example throw bad request exception)
    }
    
    // continue form processing
    

    Warning: the protection in example is not as safe as the standard CSRF protection because someone can request the form from your application to get the generated token than use the token from it for limited time in the form submitted from their site.