From 53a37558075cff1ebd4acacac16f536239bba072 Mon Sep 17 00:00:00 2001 From: Ermal Date: Fri, 22 Jul 2011 17:34:14 +0000 Subject: Clear \r from these files to be readble --- usr/local/www/csrf/csrf-magic.js | 354 +++++++++--------- usr/local/www/csrf/csrf-magic.php | 744 +++++++++++++++++++------------------- 2 files changed, 549 insertions(+), 549 deletions(-) (limited to 'usr/local') diff --git a/usr/local/www/csrf/csrf-magic.js b/usr/local/www/csrf/csrf-magic.js index 820f6e8..6992402 100644 --- a/usr/local/www/csrf/csrf-magic.js +++ b/usr/local/www/csrf/csrf-magic.js @@ -1,177 +1,177 @@ -/** - * @file - * - * Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory - * plays nice with other JavaScript libraries, needs testing though. - */ - -// Here are the basic overloaded method definitions -// The wrapper must be set BEFORE onreadystatechange is written to, since -// a bug in ActiveXObject prevents us from properly testing for it. -CsrfMagic = function(real) { - // try to make it ourselves, if you didn't pass it - if (!real) try { real = new XMLHttpRequest; } catch (e) {;} - if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;} - if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;} - if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;} - this.csrf = real; - // properties - var csrfMagic = this; - real.onreadystatechange = function() { - csrfMagic._updateProps(); - return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null; - }; - csrfMagic._updateProps(); -} - -CsrfMagic.prototype = { - - open: function(method, url, async, username, password) { - if (method == 'POST') this.csrf_isPost = true; - // deal with Opera bug, thanks jQuery - if (username) return this.csrf_open(method, url, async, username, password); - else return this.csrf_open(method, url, async); - }, - csrf_open: function(method, url, async, username, password) { - if (username) return this.csrf.open(method, url, async, username, password); - else return this.csrf.open(method, url, async); - }, - - send: function(data) { - if (!this.csrf_isPost) return this.csrf_send(data); - prepend = csrfMagicName + '=' + csrfMagicToken + '&'; - if (this.csrf_purportedLength === undefined) { - this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length); - delete this.csrf_purportedLength; - } - delete this.csrf_isPost; - return this.csrf_send(prepend + data); - }, - csrf_send: function(data) { - return this.csrf.send(data); - }, - - setRequestHeader: function(header, value) { - // We have to auto-set this at the end, since we don't know how long the - // nonce is when added to the data. - if (this.csrf_isPost && header == "Content-length") { - this.csrf_purportedLength = value; - return; - } - return this.csrf_setRequestHeader(header, value); - }, - csrf_setRequestHeader: function(header, value) { - return this.csrf.setRequestHeader(header, value); - }, - - abort: function() { - return this.csrf.abort(); - }, - getAllResponseHeaders: function() { - return this.csrf.getAllResponseHeaders(); - }, - getResponseHeader: function(header) { - return this.csrf.getResponseHeader(header); - } // , -} - -// proprietary -CsrfMagic.prototype._updateProps = function() { - this.readyState = this.csrf.readyState; - if (this.readyState == 4) { - this.responseText = this.csrf.responseText; - this.responseXML = this.csrf.responseXML; - this.status = this.csrf.status; - this.statusText = this.csrf.statusText; - } -} -CsrfMagic.process = function(base) { - var prepend = csrfMagicName + '=' + csrfMagicToken; - if (base) return prepend + '&' + base; - return prepend; -} -// callback function for when everything on the page has loaded -CsrfMagic.end = function() { - // This rewrites forms AGAIN, so in case buffering didn't work this - // certainly will. - forms = document.getElementsByTagName('form'); - for (var i = 0; i < forms.length; i++) { - form = forms[i]; - if (form.method.toUpperCase() !== 'POST') continue; - if (form.elements[csrfMagicName]) continue; - var input = document.createElement('input'); - input.setAttribute('name', csrfMagicName); - input.setAttribute('value', csrfMagicToken); - input.setAttribute('type', 'hidden'); - form.appendChild(input); - } -} - -// Sets things up for Mozilla/Opera/nice browsers -if (window.XMLHttpRequest && window.XMLHttpRequest.prototype) { - var x = XMLHttpRequest.prototype; - var c = CsrfMagic.prototype; - - // Save the original functions - x.csrf_open = x.open; - x.csrf_send = x.send; - x.csrf_setRequestHeader = x.setRequestHeader; - - // Notice that CsrfMagic is itself an instantiatable object, but only - // open, send and setRequestHeader are necessary as decorators. - x.open = c.open; - x.send = c.send; - x.setRequestHeader = c.setRequestHeader; -} else { - // The only way we can do this is by modifying a library you have been - // using. We support YUI, script.aculo.us, prototype, MooTools, - // jQuery, Ext and Dojo. - if (window.jQuery) { - // jQuery didn't implement a new XMLHttpRequest function, so we have - // to do this the hard way. - jQuery.csrf_ajax = jQuery.ajax; - jQuery.ajax = function( s ) { - if (s.type && s.type.toUpperCase() == 'POST') { - s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); - if ( s.data && s.processData && typeof s.data != "string" ) { - s.data = jQuery.param(s.data); - } - s.data = CsrfMagic.process(s.data); - } - return jQuery.csrf_ajax( s ); - } - } else if (window.Prototype) { - // This works for script.aculo.us too - Ajax.csrf_getTransport = Ajax.getTransport; - Ajax.getTransport = function() { - return new CsrfMagic(Ajax.csrf_getTransport()); - } - } else if (window.MooTools) { - Browser.csrf_Request = Browser.Request; - Browser.Request = function () { - return new CsrfMagic(Browser.csrf_Request()); - } - } else if (window.YAHOO) { - YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject; - YAHOO.util.Connect.createXhrObject = function (transaction) { - obj = YAHOO.util.Connect.csrf_createXhrObject(transaction); - obj.conn = new CsrfMagic(obj.conn); - return obj; - } - } else if (window.Ext) { - // Ext can use other js libraries as loaders, so it has to come last - // Ext's implementation is pretty identical to Yahoo's, but we duplicate - // it for comprehensiveness's sake. - Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject; - Ext.lib.Ajax.createXhrObject = function (transaction) { - obj = Ext.lib.Ajax.csrf_createXhrObject(transaction); - obj.conn = new CsrfMagic(obj.conn); - return obj; - } - } else if (window.dojo) { - dojo.csrf__xhrObj = dojo._xhrObj; - dojo._xhrObj = function () { - return new CsrfMagic(dojo.csrf__xhrObj()); - } - } -} +/** + * @file + * + * Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory + * plays nice with other JavaScript libraries, needs testing though. + */ + +// Here are the basic overloaded method definitions +// The wrapper must be set BEFORE onreadystatechange is written to, since +// a bug in ActiveXObject prevents us from properly testing for it. +CsrfMagic = function(real) { + // try to make it ourselves, if you didn't pass it + if (!real) try { real = new XMLHttpRequest; } catch (e) {;} + if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;} + if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;} + if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;} + this.csrf = real; + // properties + var csrfMagic = this; + real.onreadystatechange = function() { + csrfMagic._updateProps(); + return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null; + }; + csrfMagic._updateProps(); +} + +CsrfMagic.prototype = { + + open: function(method, url, async, username, password) { + if (method == 'POST') this.csrf_isPost = true; + // deal with Opera bug, thanks jQuery + if (username) return this.csrf_open(method, url, async, username, password); + else return this.csrf_open(method, url, async); + }, + csrf_open: function(method, url, async, username, password) { + if (username) return this.csrf.open(method, url, async, username, password); + else return this.csrf.open(method, url, async); + }, + + send: function(data) { + if (!this.csrf_isPost) return this.csrf_send(data); + prepend = csrfMagicName + '=' + csrfMagicToken + '&'; + if (this.csrf_purportedLength === undefined) { + this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length); + delete this.csrf_purportedLength; + } + delete this.csrf_isPost; + return this.csrf_send(prepend + data); + }, + csrf_send: function(data) { + return this.csrf.send(data); + }, + + setRequestHeader: function(header, value) { + // We have to auto-set this at the end, since we don't know how long the + // nonce is when added to the data. + if (this.csrf_isPost && header == "Content-length") { + this.csrf_purportedLength = value; + return; + } + return this.csrf_setRequestHeader(header, value); + }, + csrf_setRequestHeader: function(header, value) { + return this.csrf.setRequestHeader(header, value); + }, + + abort: function() { + return this.csrf.abort(); + }, + getAllResponseHeaders: function() { + return this.csrf.getAllResponseHeaders(); + }, + getResponseHeader: function(header) { + return this.csrf.getResponseHeader(header); + } // , +} + +// proprietary +CsrfMagic.prototype._updateProps = function() { + this.readyState = this.csrf.readyState; + if (this.readyState == 4) { + this.responseText = this.csrf.responseText; + this.responseXML = this.csrf.responseXML; + this.status = this.csrf.status; + this.statusText = this.csrf.statusText; + } +} +CsrfMagic.process = function(base) { + var prepend = csrfMagicName + '=' + csrfMagicToken; + if (base) return prepend + '&' + base; + return prepend; +} +// callback function for when everything on the page has loaded +CsrfMagic.end = function() { + // This rewrites forms AGAIN, so in case buffering didn't work this + // certainly will. + forms = document.getElementsByTagName('form'); + for (var i = 0; i < forms.length; i++) { + form = forms[i]; + if (form.method.toUpperCase() !== 'POST') continue; + if (form.elements[csrfMagicName]) continue; + var input = document.createElement('input'); + input.setAttribute('name', csrfMagicName); + input.setAttribute('value', csrfMagicToken); + input.setAttribute('type', 'hidden'); + form.appendChild(input); + } +} + +// Sets things up for Mozilla/Opera/nice browsers +if (window.XMLHttpRequest && window.XMLHttpRequest.prototype) { + var x = XMLHttpRequest.prototype; + var c = CsrfMagic.prototype; + + // Save the original functions + x.csrf_open = x.open; + x.csrf_send = x.send; + x.csrf_setRequestHeader = x.setRequestHeader; + + // Notice that CsrfMagic is itself an instantiatable object, but only + // open, send and setRequestHeader are necessary as decorators. + x.open = c.open; + x.send = c.send; + x.setRequestHeader = c.setRequestHeader; +} else { + // The only way we can do this is by modifying a library you have been + // using. We support YUI, script.aculo.us, prototype, MooTools, + // jQuery, Ext and Dojo. + if (window.jQuery) { + // jQuery didn't implement a new XMLHttpRequest function, so we have + // to do this the hard way. + jQuery.csrf_ajax = jQuery.ajax; + jQuery.ajax = function( s ) { + if (s.type && s.type.toUpperCase() == 'POST') { + s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); + if ( s.data && s.processData && typeof s.data != "string" ) { + s.data = jQuery.param(s.data); + } + s.data = CsrfMagic.process(s.data); + } + return jQuery.csrf_ajax( s ); + } + } else if (window.Prototype) { + // This works for script.aculo.us too + Ajax.csrf_getTransport = Ajax.getTransport; + Ajax.getTransport = function() { + return new CsrfMagic(Ajax.csrf_getTransport()); + } + } else if (window.MooTools) { + Browser.csrf_Request = Browser.Request; + Browser.Request = function () { + return new CsrfMagic(Browser.csrf_Request()); + } + } else if (window.YAHOO) { + YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject; + YAHOO.util.Connect.createXhrObject = function (transaction) { + obj = YAHOO.util.Connect.csrf_createXhrObject(transaction); + obj.conn = new CsrfMagic(obj.conn); + return obj; + } + } else if (window.Ext) { + // Ext can use other js libraries as loaders, so it has to come last + // Ext's implementation is pretty identical to Yahoo's, but we duplicate + // it for comprehensiveness's sake. + Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject; + Ext.lib.Ajax.createXhrObject = function (transaction) { + obj = Ext.lib.Ajax.csrf_createXhrObject(transaction); + obj.conn = new CsrfMagic(obj.conn); + return obj; + } + } else if (window.dojo) { + dojo.csrf__xhrObj = dojo._xhrObj; + dojo._xhrObj = function () { + return new CsrfMagic(dojo.csrf__xhrObj()); + } + } +} diff --git a/usr/local/www/csrf/csrf-magic.php b/usr/local/www/csrf/csrf-magic.php index ccb1617..befad0a 100644 --- a/usr/local/www/csrf/csrf-magic.php +++ b/usr/local/www/csrf/csrf-magic.php @@ -1,372 +1,372 @@ - - */ -$GLOBALS['csrf']['input-name'] = '__csrf_magic'; - -/** - * Set this to false if your site must work inside of frame/iframe elements, - * but do so at your own risk: this configuration protects you against CSS - * overlay attacks that defeat tokens. - */ -$GLOBALS['csrf']['frame-breaker'] = true; - -/** - * Whether or not CSRF Magic should be allowed to start a new session in order - * to determine the key. - */ -$GLOBALS['csrf']['auto-session'] = true; - -/** - * Whether or not csrf-magic should produce XHTML style tags. - */ -$GLOBALS['csrf']['xhtml'] = true; - -// FUNCTIONS: - -// Don't edit this! -$GLOBALS['csrf']['version'] = '1.0.1'; - -/** - * Rewrites
on the fly to add CSRF tokens to them. This can also - * inject our JavaScript library. - */ -function csrf_ob_handler($buffer, $flags) { - // Even though the user told us to rewrite, we should do a quick heuristic - // to check if the page is *actually* HTML. We don't begin rewriting until - // we hit the first "; - $buffer = preg_replace('#(]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer); - if ($GLOBALS['csrf']['frame-breaker']) { - $buffer = str_ireplace('', '', $buffer); - } - if ($js = $GLOBALS['csrf']['rewrite-js']) { - $buffer = str_ireplace( - '', - ''. - '', - $buffer - ); - $script = ''; - $buffer = str_ireplace('', $script . '', $buffer, $count); - if (!$count) { - $buffer .= $script; - } - } - return $buffer; -} - -/** - * Checks if this is a post request, and if it is, checks if the nonce is valid. - * @param bool $fatal Whether or not to fatally error out if there is a problem. - * @return True if check passes or is not necessary, false if failure. - */ -function csrf_check($fatal = true) { - if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; - csrf_start(); - $name = $GLOBALS['csrf']['input-name']; - $ok = false; - $tokens = ''; - do { - if (!isset($_POST[$name])) break; - // we don't regenerate a token and check it because some token creation - // schemes are volatile. - $tokens = $_POST[$name]; - if (!csrf_check_tokens($tokens)) break; - $ok = true; - } while (false); - if ($fatal && !$ok) { - $callback = $GLOBALS['csrf']['callback']; - if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden'; - $callback($tokens); - exit; - } - return $ok; -} - -/** - * Retrieves a valid token(s) for a particular context. Tokens are separated - * by semicolons. - */ -function csrf_get_tokens() { - $has_cookies = !empty($_COOKIE); - - // $ip implements a composite key, which is sent if the user hasn't sent - // any cookies. It may or may not be used, depending on whether or not - // the cookies "stick" - if (!$has_cookies && $secret) { - // :TODO: Harden this against proxy-spoofing attacks - $ip = ';ip:' . csrf_hash($_SERVER['IP_ADDRESS']); - } else { - $ip = ''; - } - csrf_start(); - - // These are "strong" algorithms that don't require per se a secret - if (session_id()) return 'sid:' . csrf_hash(session_id()) . $ip; - if ($GLOBALS['csrf']['cookie']) { - $val = csrf_generate_secret(); - setcookie($GLOBALS['csrf']['cookie'], $val); - return 'cookie:' . csrf_hash($val) . $ip; - } - if ($GLOBALS['csrf']['key']) return 'key:' . csrf_hash($GLOBALS['csrf']['key']) . $ip; - // These further algorithms require a server-side secret - if ($secret === '') return 'invalid'; - if ($GLOBALS['csrf']['user'] !== false) { - return 'user:' . csrf_hash($GLOBALS['csrf']['user']); - } - if ($GLOBALS['csrf']['allow-ip']) { - return ltrim($ip, ';'); - } - return 'invalid'; -} - -/** - * @param $tokens is safe for HTML consumption - */ -function csrf_callback($tokens) { - header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); - echo "CSRF check failedCSRF check failed. Either your session has expired, this page has been inactive too long, or you need to enable cookies.
Debug: ".$tokens." -"; -} - -/** - * Checks if a composite token is valid. Outward facing code should use this - * instead of csrf_check_token() - */ -function csrf_check_tokens($tokens) { - if (is_string($tokens)) $tokens = explode(';', $tokens); - foreach ($tokens as $token) { - if (csrf_check_token($token)) return true; - } - return false; -} - -/** - * Checks if a token is valid. - */ -function csrf_check_token($token) { - if (strpos($token, ':') === false) return false; - list($type, $value) = explode(':', $token, 2); - if (strpos($value, ',') === false) return false; - list($x, $time) = explode(',', $token, 2); - if ($GLOBALS['csrf']['expires']) { - if (time() > $time + $GLOBALS['csrf']['expires']) return false; - } - switch ($type) { - case 'sid': - return $value === csrf_hash(session_id(), $time); - case 'cookie': - $n = $GLOBALS['csrf']['cookie']; - if (!$n) return false; - if (!isset($_COOKIE[$n])) return false; - return $value === csrf_hash($_COOKIE[$n], $time); - case 'key': - if (!$GLOBALS['csrf']['key']) return false; - return $value === csrf_hash($GLOBALS['csrf']['key'], $time); - // We could disable these 'weaker' checks if 'key' was set, but - // that doesn't make me feel good then about the cookie-based - // implementation. - case 'user': - if ($GLOBALS['csrf']['secret'] === '') return false; - if ($GLOBALS['csrf']['user'] === false) return false; - return $value === csrf_hash($GLOBALS['csrf']['user'], $time); - case 'ip': - if (csrf_get_secret() === '') return false; - // do not allow IP-based checks if the username is set, or if - // the browser sent cookies - if ($GLOBALS['csrf']['user'] !== false) return false; - if (!empty($_COOKIE)) return false; - if (!$GLOBALS['csrf']['allow-ip']) return false; - return $value === csrf_hash($_SERVER['IP_ADDRESS'], $time); - } - return false; -} - -/** - * Sets a configuration value. - */ -function csrf_conf($key, $val) { - if (!isset($GLOBALS['csrf'][$key])) { - trigger_error('No such configuration ' . $key, E_USER_WARNING); - return; - } - $GLOBALS['csrf'][$key] = $val; -} - -/** - * Starts a session if we're allowed to. - */ -function csrf_start() { - if ($GLOBALS['csrf']['auto-session'] && !session_id()) { - session_start(); - } -} - -/** - * Retrieves the secret, and generates one if necessary. - */ -function csrf_get_secret() { - if ($GLOBALS['csrf']['secret']) return $GLOBALS['csrf']['secret']; - $dir = dirname(__FILE__); - $file = $dir . '/csrf-secret.php'; - $secret = ''; - if (file_exists($file)) { - include $file; - return $secret; - } - if (is_writable($dir)) { - $secret = csrf_generate_secret(); - $fh = fopen($file, 'w'); - fwrite($fh, ' + */ +$GLOBALS['csrf']['input-name'] = '__csrf_magic'; + +/** + * Set this to false if your site must work inside of frame/iframe elements, + * but do so at your own risk: this configuration protects you against CSS + * overlay attacks that defeat tokens. + */ +$GLOBALS['csrf']['frame-breaker'] = true; + +/** + * Whether or not CSRF Magic should be allowed to start a new session in order + * to determine the key. + */ +$GLOBALS['csrf']['auto-session'] = true; + +/** + * Whether or not csrf-magic should produce XHTML style tags. + */ +$GLOBALS['csrf']['xhtml'] = true; + +// FUNCTIONS: + +// Don't edit this! +$GLOBALS['csrf']['version'] = '1.0.1'; + +/** + * Rewrites on the fly to add CSRF tokens to them. This can also + * inject our JavaScript library. + */ +function csrf_ob_handler($buffer, $flags) { + // Even though the user told us to rewrite, we should do a quick heuristic + // to check if the page is *actually* HTML. We don't begin rewriting until + // we hit the first "; + $buffer = preg_replace('#(]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer); + if ($GLOBALS['csrf']['frame-breaker']) { + $buffer = str_ireplace('', '', $buffer); + } + if ($js = $GLOBALS['csrf']['rewrite-js']) { + $buffer = str_ireplace( + '', + ''. + '', + $buffer + ); + $script = ''; + $buffer = str_ireplace('', $script . '', $buffer, $count); + if (!$count) { + $buffer .= $script; + } + } + return $buffer; +} + +/** + * Checks if this is a post request, and if it is, checks if the nonce is valid. + * @param bool $fatal Whether or not to fatally error out if there is a problem. + * @return True if check passes or is not necessary, false if failure. + */ +function csrf_check($fatal = true) { + if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; + csrf_start(); + $name = $GLOBALS['csrf']['input-name']; + $ok = false; + $tokens = ''; + do { + if (!isset($_POST[$name])) break; + // we don't regenerate a token and check it because some token creation + // schemes are volatile. + $tokens = $_POST[$name]; + if (!csrf_check_tokens($tokens)) break; + $ok = true; + } while (false); + if ($fatal && !$ok) { + $callback = $GLOBALS['csrf']['callback']; + if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden'; + $callback($tokens); + exit; + } + return $ok; +} + +/** + * Retrieves a valid token(s) for a particular context. Tokens are separated + * by semicolons. + */ +function csrf_get_tokens() { + $has_cookies = !empty($_COOKIE); + + // $ip implements a composite key, which is sent if the user hasn't sent + // any cookies. It may or may not be used, depending on whether or not + // the cookies "stick" + if (!$has_cookies && $secret) { + // :TODO: Harden this against proxy-spoofing attacks + $ip = ';ip:' . csrf_hash($_SERVER['IP_ADDRESS']); + } else { + $ip = ''; + } + csrf_start(); + + // These are "strong" algorithms that don't require per se a secret + if (session_id()) return 'sid:' . csrf_hash(session_id()) . $ip; + if ($GLOBALS['csrf']['cookie']) { + $val = csrf_generate_secret(); + setcookie($GLOBALS['csrf']['cookie'], $val); + return 'cookie:' . csrf_hash($val) . $ip; + } + if ($GLOBALS['csrf']['key']) return 'key:' . csrf_hash($GLOBALS['csrf']['key']) . $ip; + // These further algorithms require a server-side secret + if ($secret === '') return 'invalid'; + if ($GLOBALS['csrf']['user'] !== false) { + return 'user:' . csrf_hash($GLOBALS['csrf']['user']); + } + if ($GLOBALS['csrf']['allow-ip']) { + return ltrim($ip, ';'); + } + return 'invalid'; +} + +/** + * @param $tokens is safe for HTML consumption + */ +function csrf_callback($tokens) { + header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); + echo "CSRF check failedCSRF check failed. Either your session has expired, this page has been inactive too long, or you need to enable cookies.
Debug: ".$tokens." +"; +} + +/** + * Checks if a composite token is valid. Outward facing code should use this + * instead of csrf_check_token() + */ +function csrf_check_tokens($tokens) { + if (is_string($tokens)) $tokens = explode(';', $tokens); + foreach ($tokens as $token) { + if (csrf_check_token($token)) return true; + } + return false; +} + +/** + * Checks if a token is valid. + */ +function csrf_check_token($token) { + if (strpos($token, ':') === false) return false; + list($type, $value) = explode(':', $token, 2); + if (strpos($value, ',') === false) return false; + list($x, $time) = explode(',', $token, 2); + if ($GLOBALS['csrf']['expires']) { + if (time() > $time + $GLOBALS['csrf']['expires']) return false; + } + switch ($type) { + case 'sid': + return $value === csrf_hash(session_id(), $time); + case 'cookie': + $n = $GLOBALS['csrf']['cookie']; + if (!$n) return false; + if (!isset($_COOKIE[$n])) return false; + return $value === csrf_hash($_COOKIE[$n], $time); + case 'key': + if (!$GLOBALS['csrf']['key']) return false; + return $value === csrf_hash($GLOBALS['csrf']['key'], $time); + // We could disable these 'weaker' checks if 'key' was set, but + // that doesn't make me feel good then about the cookie-based + // implementation. + case 'user': + if ($GLOBALS['csrf']['secret'] === '') return false; + if ($GLOBALS['csrf']['user'] === false) return false; + return $value === csrf_hash($GLOBALS['csrf']['user'], $time); + case 'ip': + if (csrf_get_secret() === '') return false; + // do not allow IP-based checks if the username is set, or if + // the browser sent cookies + if ($GLOBALS['csrf']['user'] !== false) return false; + if (!empty($_COOKIE)) return false; + if (!$GLOBALS['csrf']['allow-ip']) return false; + return $value === csrf_hash($_SERVER['IP_ADDRESS'], $time); + } + return false; +} + +/** + * Sets a configuration value. + */ +function csrf_conf($key, $val) { + if (!isset($GLOBALS['csrf'][$key])) { + trigger_error('No such configuration ' . $key, E_USER_WARNING); + return; + } + $GLOBALS['csrf'][$key] = $val; +} + +/** + * Starts a session if we're allowed to. + */ +function csrf_start() { + if ($GLOBALS['csrf']['auto-session'] && !session_id()) { + session_start(); + } +} + +/** + * Retrieves the secret, and generates one if necessary. + */ +function csrf_get_secret() { + if ($GLOBALS['csrf']['secret']) return $GLOBALS['csrf']['secret']; + $dir = dirname(__FILE__); + $file = $dir . '/csrf-secret.php'; + $secret = ''; + if (file_exists($file)) { + include $file; + return $secret; + } + if (is_writable($dir)) { + $secret = csrf_generate_secret(); + $fh = fopen($file, 'w'); + fwrite($fh, '