Cross-site request forgery (XSRF). It’s something that’s becoming a big issue in internet security, and yet is still relatively poorly understood by many developers. If you don’t already know what it is, you can find an excellent introduction in wikipedia:
http://en.wikipedia.org/wiki/XSRF
In a nutshell, anything on your website is a security risk if it lets someone modify something on your system (eg an entry in a database) without first making sure that the request for that modification actually came from your site. This isn’t restricted to a URL query string. Forms are probably the worst offender because many developers seem to think they’re somehow safe. They’re not.
A good way of preventing a XSRF attack is to give each form a unique identifier which keeps changing. Only your web application will know for sure that a request has come from a form on your site, because that application generated the identifier in the first place and can cross-check it against an entry in a database. Drupal does this, and so does WordPress (including WordPress MU).
Here I’m focusing on how WordPress MU does the XSRF protection. It’s called, wait for it…
Nonce?
ok, ok. Get the sniggering done now. And relax…
To the good people at WordPress (and seemingly everyone in America) nonce has only one – very unfunny – meaning: a word or expression which only ever occurs once. ie. a unique key which changes its value every time it’s used.
By default all WordPress forms use nonce values to help prevent XSRF. If you write your own plugins, to get this to work in WordPress is amazingly simple. Just put this in your home-grown form:
wp_nonce_field('my_nonce_phrase');
where ‘my_nonce_phrase’ can be any unique phrase which acts as a base for the system-generated nonce key. It’s usually a good idea to use something like plugin-name+form-name so you eliminate the risk of clashing one nonce value with one from a different form. But basically you can use any phrase you like.
If you need more info, the WordPress API listing is here:
http://codex.wordpress.org/Function_Reference/wp_nonce_field
If you want to put a nonce in a URL rather than a form, just use the very similar:
wp_nonce_url('theurl', 'my_nonce_phrase');
where this time ‘theurl’ is, predictably, the url that you want to noncify, and ‘my_nonce_phrase’ is once again your unique phrase which is going to act as the seed for the eventual nonce key.
Whitelist
There is however a slight catch. Some forms on the backend are forms where you want someone to update various options parameters. For example, if you’ve built a plugin and want someone to be able to set various plugin default options. In standard WordPress, you use options.php as the form’s action, and can use the following as a nonce field:
wp_nonce_field('update-options');
This way, when the update button is pressed the fields you’ve added to your form will automatically get updated as part of the options.php code, and the nonce system will be happy that the form came from the right website. Everyone’s smiling. Everyone’s happy.
This doesn’t work with WordPress MU, however.
Instead you need to add this to your form:
wp_nonce_field('my_nonce_phrase-options');
where adding -options to the end is crucial if you want options.php to work properly. I’m not sure why. It just is.
Finally, in your plugin you need to be able to alter the nonce whitelist ie. the list of option names which are accepted by options.php. You could just alter options.php, but that would be crazy. Instead use a filter to intercept the whitelist and add your own values to it:
function my_plugin_alter_whitelist_options($whitelist) {
if(is_array($whitelist)) {
$option_array = array('my_nonce_phrase'=>array('plugin_option_1', 'plugin_option_2', 'plugin_option_3', 'plugin_option_4'));
$whitelist = array_merge($whitelist, $option_array);
}
return $whitelist;
}
add_filter('whitelist_options', 'my_plugin_alter_whitelist_options');
Yes, it’s ugly and bad. But there doesn’t seem to be any way round this for WordPress MU and forms used for setting your own options in a plugin.
Summary
WordPress MU lets you build forms which are relatively safe against XSRF attacks. Doing this is easy, and takes just one line of code in your form. A word of warning: adding protection to forms which make use of WordPress’s options feature is more fiddly in MU than it needs to be, but still not too bad.