I was looking for a way to ensure CSRF-Protection
in my Quickform2
.
I found this link but it's for QuickForm1
.
Any ideas how I can adapt this to QF2
?
After some fiddling around I came up with this solution.
Maybe it helps someone else as well:
<?php
/**
* @uses HTML_QuickForm
* @desc Add automatic CSRF mitigation to all forms by incorporating a token that must be matched in the session and forcing the use of POST method
* Based on: http://www.zapoyok.info/2010/07/17/csrf-et-quickform-de-pear/
*/
require_once "QuickForm2.php";
class HTML_QuickForm2s extends HTML_QuickForm2
{
/**
* @property string $_sessionTokenKey The name of the session variable containing the token
*/
private $_sessionTokenKey;
/**
* @method __construct
* @desc Override the method to always use post and pass it on to the parent constructor. Create a session key for the token based on the form name.
* @param $id
* @param string $method
* @param mixed $attributes
* @param boolean $trackSubmit
*/
public function __construct($id, $method = 'post', $attributes = null, $trackSubmit = true)
{
$this->_sessionTokenKey = "QuickForm2s_" . md5($id);
parent::__construct($id, $method, $attributes, $trackSubmit);
//A token hasn't been created so do so
if (!isset($_SESSION[$this->_sessionTokenKey])) {
$_SESSION[$this->_sessionTokenKey] = md5(uniqid(rand(), true) . session_id()); //requires the session id to be known in order to add extra difficulty to compromising
}
//Hide the token at the end of the form
$this->addElement("hidden", "qfS_csrf");
$qfsCsrf= $this->getElementsByName('qfS_csrf');
$qfsCsrf[0]->setValue($_SESSION[$this->_sessionTokenKey]);
}
/**
* @method validate
* @desc Check if the passed token matches the session before allowing validation
* @return boolean
*/
public function validate()
{
$submitValues = $this->getValue();
//The token was not passed or does not match
if (!isset($submitValues['qfS_csrf']) || $submitValues['qfS_csrf'] != $_SESSION[$this->_sessionTokenKey]) {
$this->setError("Anti-CSRF token does not match");
}
return parent::validate();
}
}