- b2evolution CMS User Manual
- Operations Reference
- Security
- Crumbs (nonces)
Crumbs (nonces)
Principle
The goal of crumbs (sometimes called nonces) is to prevent a hacker from being able to blindly carry out actions on your blog by tricking you into clicking on a link.
For example, let’s suppose a hacker sends you an email containing a link saying "click here" and the link goes to yourblog.com/admin.php?action=delete&some_object_ID=123456
If you were to click on that link, if you were currently logged into your blog with a cookie and if the hacker had the ID right… which is a lot of IFs… including you being drunk enough to actually click on the link… then the hacker could actually get you to delete a specific object from your blog.
Variations of this also work for inserting and updating objects (but it’s even more difficult to carry out).
This is why b2evo 4 uses crumbs that guarantee that any action that changes data was actually initiated by you:
- when you use form, it contains a hidden "crumb" field that is valid only for a 2 hour time frame.
Similarly, when a "delete" link is displayed to you, a crumb is embedded in that link.
- when the form is submitted or the link is clicked, b2evo will check that a valid crumb has been received. (valid crumbs are stored in the user session and invalidated automatically after 2 hours).
For developpers
You are encouraged to use this in your plugins too.
To create a crumb in a form, use this:
$Form->add_crumb( 'nameofcrumb' );
To create a crumb in an action link (like a delete), use this:
…url.’&’.url_crumb( ‘nameofcrumb’ );
To verify that a valid crumb has been received before carrying out an action, do this:
// Check that this action request is not a CSRF hacked request:
$Session->assert_received_crumb( 'nameofcrumb' );
You should use your unique plugin code as ‘nameofcrumb’ but you can also append an additional name if you want to use different crumbs for different objects within your plugin. This will enhance security even more (although marginally).
Implementation
Crumbs are created internally in the user session by Session::create_crumb( $crumb_name ).
The $crumb_name can be any specific action you want to protect. All calls with the same $crumb_name will share the same crumb up to the time that crumb is one hour old. That means many links on a page can share a single crumb.
After one hour, a new crumb is stored and the old one is still retained (and will still validate as a valid crumb until it’s 2 hours old).
When a crumb is received and checked by Session::assert_received_crumb( $crumb_name ), we check that the crumb has been created before and that it’s not oldr than 2 hours.
Crumbs are NOT deleted from the session. They just stop validating when they are more than 2 hours old.
Issues with caching
Any paged cached for more than 2 hours will obviously include obsolete crumbs and any action links therein won’t work any more.
However, there is a bigger issue when using page cache in b2evo versions prior to 4.1 and when a static cache version of a page is shared between multiple users; this can happen for non-logged in users. Especially on pages like:
- Comment forms
- Contact forms
It is important to check that these pages are either not cached or that a crumb is generated dynamically (javascript) so that they can work. Any crumb that makes it into the pagecache is a mistake that must be avoided.
In b2evo version 4.1 the crumbs are generated dynamically. This happens when the comment or message form is loaded dynamically using AJAX when the page is displayed in the browser. The form is never cached as part of the page. Hence the form crumb will always match the current session.
Testing
In order to simulate 2 concurrent user sessions, use 2 different browsers.
Test 1:
- log in in each browser with a different admin accounts
- copy a delete link url from one browser
- paste it into the url of the other browser
- you will get an error message stating: incorrect crumb received.
Test 2:
- enable the page cache for a blog
- log out of both browsers
- open the same blog post with a comment form in both browsers
- check that the page has been cached in (for example) blogs/cache/c1
- try to post a comment from both browsers
- Depending on which version of b2evolution you use, this will either work or fail:
- Prior to version 4.1: one of the 2 will fail because a single crumb has been cached for all users!
- Since verison 4.1: crumbs will work properly because the forms are loaded dynamically with Javascript/AJAX. They are never cached and the form crumb will always match the current session.
For users with versions priori to 4.1:
- Note that the default pagecache only caches for 10 minutes, so if you test slowly you won’t see this.
- This means however that if anyone is looking at a page, no one else can comment on that page for 10 minutes.
- We recommend you upgrade to version 4.1 (or don’t use page caching priori to version 4.1)