Hey,
I'm trying to implement CSRF tokens on a site of mine, but all the existing solutions have one of two problems;
1) The site runs on multiple servers (4 to be precise) so we can't use anything in _SESSION
2) The site gets over 400k hits a day on average so using a DB to store tokens would be too much of a performance hit.

The solution I've come up with at the moment is to generate three tokens, a unique 'hash' which uses a secret key known only to the server, the URL, base64 encoded and the request time to expire requests.

PHP:

<?php

//Server secret key
$CSRFKey = "some-secret-key";

//Generate the tokens
$RequestTime = time();
$Output["CSRF"] = sha1($_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'].$_SERVER['REQUEST_URI'].$RequestTime.$CSRFKey);
$Output["CSRF_KEY"] = base64_encode($_SERVER['REQUEST_URI']);
$Output["CSRF_TIME"] = base64_encode($RequestTime);

//Post requests should have CSRF keys
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
	//Generate a second token from the submitted data
	$CSRF_URL = base64_decode($_POST['CSRF_KEY']);
	$CSRF_TIME = base64_decode($_POST['CSRF_TIME']);
	$CSRF = sha1($_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'].$CSRF_URL.$CSRF_TIME.$CSRFKey);
	
	//Compare the generated token to the submitted token
	if ( $CSRF != $_POST['CSRF'] ) {
		header('HTTP/1.0 500 Forbidden');
		die("Request forbidden!");
	}
	
	//Check the expiry time of the request
	//Assume the user will submit a form within 10 minutes but take longer than 10 seconds
	if ( $CSRF_TIME < $RequestTime+10 || $CSRF_TIME > $RequestTime+60*10 ) {
		header('HTTP/1.0 500 Forbidden');
		die("Request expired!");
	}
}

HTML:

<head>
<!--CSRF-->
<meta name="CSRF" id="CSRF" content="{{CSRF}}" />
<meta name="CSRF_KEY" id="CSRF_KEY" content="{{CSRF_KEY}}" />
<meta name="CSRF_TIME" id="CSRF_TIME" content="{{CSRF_TIME}}" />
</head>

Javascript (With jQuery):

var CSRF_KEY = $('#CSRF_KEY').attr('content');
var CSRF_TIME = $('#CSRF_TIME').attr('content');
var CSRF = $('#CSRF').attr('content');

$('form').each(function(i,elm){
	$(elm).append('<input type="hidden" name="CSRF" value="'+CSRF+'"  />');
	$(elm).append('<input type="hidden" name="CSRF_KEY" value="'+CSRF_KEY+'"  />');
	$(elm).append('<input type="hidden" name="CSRF_TIME" value="'+CSRF_TIME+'"  />');
});

(I haven't tested this code but dry running it seemed to work with no errors).

As you can see I already have a rough idea how to do it but I just wanted to check I wasn't missing anything blindingly obvious.
-Sam

Recommended Answers

All 3 Replies

Does CSRF refer to Cross-site request forgery? Or your way of scaling your app? I am answering in the letter scenario. If this is your code and you receive more than 400k hits a day rewrite it. There are few PHP frameworks that are frameworks and not applications that generate codes and do other hopus pocus things but there are (search). Season data in bottom line is just a file in your server. So using the same season in multiple servers can be done. But you are talking about thread (visit) scope cache, this can be more easily done if a visitor – user has a target in a specific server and all its cashing objects are pulled by this one. Hoped I help…

Hi,
The issue is with Cross-site request forgery. We can't change the way our app works or the way users are distributed (Visitors are distributed evenly to all servers, regardless of sessions/cookies/IPs). Rewriting the application is also out of the question, it's very advanced and uses some custom PHP modules and a whole lot of server customizations (I.E. it wouldn't just run on any PHP/Apache server).
-Sam

I understand that you might be a programmer who have to do things that disagree (I have been in that place) (of course the right way is rewriting all that and don’t play again with CSRF). The blindingly obvious that you mentioned is that if you cover a problem than someday, sure ,it will come out of its grave and you will not want to be around. Other than that if the question is that with this method you are doing right my answer is simple ‘I don’t know’. I have been in scaling a PHP application to multiple servers but CSRF was never an option. I am whishing you to fix the problem and keep your bosses happy, and move to a better job, in order not be around when this grave open.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.