Protecting Against Cross-Site Request Forgery with the Token Validation Library

Cross-Site Request Forgery is a method by which an action on a particular website is undertaken in secret, powered by malicious code hosted elsewhere. For example, let's say a form on your concrete5 site enables the changing of a user's password. If a malicious user somewhere knows where that form exists, how it's structured, and what parameters it takes, they can send a link to a user that, when the user opens the link, uses JavaScript to execute a post-back to their concrete5 site, with the proper POST parameters in place. If the user is logged into their concrete5 site in the background, they may have just unknowingly changed their password.

This is a cross-site request forgery attack. Protecting against these types of attacks requires the simple addition of some code within the form itself, as well as within the controller code that takes care of validating and performing the action.

Let's take a look at a vulnerable form, and a vulnerable piece of controller code.

(Obviously, this is a contrived example – there's no validation that a new password is being posted, there's no success message, etc... but it's for explanatory purposes.) These two snippets of code will work together to successfully change a user's password. It checks to see if the user is logged in, and if so, retrieves the UserInfo object for the logged-in user, and changes their password. This code is vulnerable to cross-site request forgery! If a malicious user crafts a web page that uses an iframe to POST to this page, with a valid new_password password, as long as the user is logged in to their concrete5 site their password will be changed without them knowing it!

Let's fix that. We're going to do this by adding a token to the password form. A validation token is simply a unique string that the website is able to generate, that cannot be practically forged by a malicious user.

Core::make('token') retrieves the shared Validation Token library, Concrete\Core\Validation\CSRF\Token. output() generates a hidden input field with the name ccm_token (although you don't need to care about the name) based on the unique token value you pass in. This will now be included in the post.

This is only half of what you have to do. The other half? Modify the controller method to check for the valid presence of this token.

That's it! The request value will automatically be checked for the presence of this token. It's encoded and can't easily be forged by some malicious party who doesn't have access to the website source code.

Dashboard Pages

Token-based validation is easy in concrete5 Dashboard pages. As long as your dashboard page extends the base Dashboard Controller Concrete\Core\Page\Controller\DashboardPageController (which is shoul always do) you'll have access to the token validator in your controller and in your view.

Controller

If the change_password method above were in a Dashboard page, you could simply check it like this: