Is checking the referrer enough to protect against a cross site request forgery attack? I know the referrer can be spoofed, but is there any way for the attacker to do that FOR the client? I know tokens are the norm, but would this work?
no is not enough, it is very easy for the attacker to do that FOR the client, as you ask, all the attacker has to do is get the user to click in a link created by him, from that point, is game over
The attacker will copy the form used in the original site, and spoof the rest because now the code is on his own site, then submit that to the victim site
As you mention on the question, tokens are the norm when it comes to prevent CSRF
Among other things, using the referrer won't work for users whose browsers (or corporate proxies) don't send referrers.
You can add tokens to your form so that when you receive the token, you can validate the token and see if the request is valid.
In PHP for example, you can send a CRC32 hash of the session ID:
<form method="post" action="action.php">
<input type="hidden" name="sesscheck" value="<?php echo dechex(crc32(session_id())) ?>" />
<!-- all other inputs -->
</form>
On your action page:
<?php
if(isset($_POST['sesscheck']) && $_POST['sesscheck'] == dechex(crc32(session_id()))){
// session is valid. do the appropriate.
// check for referral as well - but allow empty referral (some browsers don't send referrer header)
}else{
// session is invalid, throw error.
}
?>
That will at least fight off some kind of bots that are out there to automate forms.
Follow the norm: use tokens.
Checking the referrer actually does nothing, because the request is coming from that page anyway! The problem you are trying to prevent is the page being requested without the user doing anything; not the page being hit itself.
Tokens are the way to protect against this. You generate one, store it in the session, and write it to the HTML, then, upon posting, you check the one you receive, and see if it matches the one you expect. If it doesn't, you fail. Either way, you generate a new token afterwards.
It may also be relevant to consider that this will mess people up if the have multiple pages; so you may like to make a different token per page.
I know checking refer is not foolproof. At least let me know how to spoof refer header using any client side methods you know.