Yii 1.1: Logout CSRF Protection

12 followers

Overview

CSRF

CSRF is the acronymn for Cross-site request forgery.
It is an attack which forces an end user to execute unwanted actions on a web application in which he/she is currently authenticated.

To help mitigate the threat, all requests that cause side-effects should be protected.
Control measures include using a validation token in forms.

Logout CSRF

This kind of CSRF exploits the fact that the logout URLs are usually unprotected.
The Logout CSRF doesn't pose major threats, but is annoying for the users.

Logout CSRF and Yii

Yii is a very secure framework. But as of version 1.1.7, the code generated by "yiic webapp" isn't protected against Logout CSRF.
When you enable Yii's CSRF validation, all forms will have the validation token, thus being more protected against CSRF, but the logout link is not a form and remains unprotected.
If you want to help protect your users against this annoyance, follow the tutorial below.

Tutorial: How to get rid of Logout CSRF in Yii

The strategy followed in this tutorial is to have an unique logout link for each user. We will achieve that using Yii's CSRF token and validating it before actually logging the user out.
Follow the three easy steps below.

1. Enable CSRF protection

To enable Yii's default CSRF protection, configure the CHttpRequest component like the snippet below:

return array(
    'components'=>array(
        'request'=>array(
            'enableCsrfValidation'=>true,
        ),
    ),
);

See the documentation of CHttpRequest::enableCsrfValidation for more details.

2. Add the token to the logout link

Go to your layout file and change the route for the logout link. You need to add the token parameter. Its value will come from CSRF cookie, using Yii::app()->getRequest()->getCsrfToken()
It should look like this:

array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout', 'token'=>Yii::app()->getRequest()->getCsrfToken()), 'visible'=>!Yii::app()->user->isGuest)

You should do the same when you create a new link to the logout action.

3. Add the token validation to the logout action

In your logout action (by default in SiteController), add the $token parameter to the method, like below:

public function actionLogout($token)

The parameter $token will receive the token from the GET parameter.
Then add the validation code in the beginning of the method:

if ($token !== Yii::app()->getRequest()->getCsrfToken())
    throw new CHttpException(400, Yii::t('app', 'Invalid request. Please do not repeat this request again.'));

The method will look like:

public function actionLogout($token) {
    if ($token !== Yii::app()->getRequest()->getCsrfToken())
        throw new CHttpException(400, Yii::t('app', 'Invalid request. Please do not repeat this request again.'));
 
    Yii::app()->user->logout();
    $this->redirect(Yii::app()->homeUrl);
}

And your users are now a bit more protected!

Note

This tutorial is not intended to cover all aspects of CSRF. Please read the references below to better understand this type of exploit.
I don't mean that following this tutorial you will be 100% protected against Logout CSRF or any other security threat. Nothing is 100% safe. Please go on and read the references below, they are a nice kickstart. Information is your best tool.

References

Yii guide: Security. A must-read.

CSRF on Wikipedia

CSRF on OWASP

CHttpRequest documentation

Total 1 comment

#4635 report it
resurtm at 2011/07/28 04:48am
Same but cleaner way

Configuration:

'components'=>array(
    'request'=>array(
        'enableCsrfValidation'=>true,
    ),
),

Controller:

class UserController extends Controller 
{
    public function filters()
    {
        return array(
            'postOnly + logout',
        );
    }
 
    public function actionLogin()
    {
        // do your login stuff here
    }
 
    public function actionLogout()
    {
        Yii::app()->user->logout();
        $this->redirect(array('site/index'));
    }
}

View:

<div class="logout-block">
    <?php echo CHtml::link('Logout', '#', array(
        'submit'=>array('user/logout'),
        'csrf'=>true,
    )); ?>
</div>

Leave a comment

Please to leave your comment.

Write new article