From 502589511e8510b75e9bf742af64cf59012ec80f Mon Sep 17 00:00:00 2001 From: Peter Rotich <peter@osticket.com> Date: Fri, 20 Jul 2012 12:15:31 -0400 Subject: [PATCH] Add real class to handle CSRF protection --- include/class.csrf.php | 111 +++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 55 deletions(-) diff --git a/include/class.csrf.php b/include/class.csrf.php index d55dcea0c..633828d9d 100644 --- a/include/class.csrf.php +++ b/include/class.csrf.php @@ -4,15 +4,19 @@ Provides mechanisms to protect against cross-site request forgery attacks. This is accomplished by using a token that is not stored in a - cookie, but required to make changes to the system. + session, but required to make changes to the system. This can be accomplished by emitting a hidden field in a form, or - sending a separate header (X-CSRFToken) when forms are submitted. + sending a separate header (X-CSRFToken) when forms are submitted (e.g Ajax). This technique is based on the protection mechanism in the Django project, detailed at and thanks to https://docs.djangoproject.com/en/dev/ref/contrib/csrf/. + * TIMEOUT + Token can be expired after X seconds of inactivity (timeout) independent of the session. + + Jared Hancock Copyright (c) 2006-2012 osTicket http://www.osticket.com @@ -23,69 +27,66 @@ vim: expandtab sw=4 ts=4 sts=4: **********************************************************************/ -function csrf_token() { - ?> - <input type="hidden" name="__CSRFToken__" value="<?php - echo csrf_get_token(); ?>" /> - <?php -} +Class CSRF { + + var $name; + var $timeout; + + var $csrf; -function csrf_get_token($length=32) { - if (!isset($_SESSION['CSRFToken'])) { - for ($i = 0; $i <= $length; $i++) - $r .= chr(mt_rand(0, 255)); - $_SESSION['CSRFToken'] = base64_encode($r); + function CSRF($name='__CSRFToken__', $timeout=0) { + + $this->name = $name; + $this->timeout = $timeout; + $this->csrf = &$_SESSION['csrf']; } - return $_SESSION['CSRFToken']; -} -function csrf_ensure_cookie() { - global $csrf_unprotected; - if ($csrf_unprotected) - return true; + function reset() { + $this->csrf = array(); + } - $token = csrf_get_token(); - if (isset($_POST['__CSRFToken__'])) { - if ($token == $_POST['__CSRFToken__']) - return true; + function isExpired() { + return ($this->timeout && (time()-$this->csrf['time'])>$this->timeout); } - elseif (isset($_SERVER['HTTP_X_CSRFTOKEN'])) { - if ($token == $_SERVER['HTTP_X_CSRFTOKEN']) - return true; + + function getTokenName() { + return $this->name; } - Http::response(400, 'CSRF Token Required'); -} -function csrf_unprotect() { - global $csrf_unprotected; - $csrf_unprotected = true; -} + function getToken($len=32) { + + if(!$this->csrf['token'] || $this->isExpired()) { + + $len = $len>8?$len:32; + for ($i = 0; $i <= $len; $i++) + $r .= chr(mt_rand(0, 255)); + + $this->csrf['token'] = base64_encode(sha1(session_id().$r.SECRET_SALT)); + $this->csrf['time'] = time(); + } else { + //Reset the timer + $this->csrf['time'] = time(); + } -# Many thanks to https://docs.djangoproject.com/en/dev/ref/contrib/csrf/ -function csrf_enable_ajax() { ?> -<script type="text/javascript"> -jQuery(document).ajaxSend(function(event, xhr, settings) { - function sameOrigin(url) { - // url could be relative or scheme relative or absolute - var host = document.location.host; // host + port - var protocol = document.location.protocol; - var sr_origin = '//' + host; - var origin = protocol + sr_origin; - // Allow absolute or scheme relative URLs to same origin - return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || - (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') || - // or any other URL that isn't scheme relative or absolute i.e - // relative. - !(/^(\/\/|http:|https:).*/.test(url)); + return $this->csrf['token']; } - function safeMethod(method) { - return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); + + function validateToken($token) { + return ($token && trim($token)==$this->getToken() && !$this->isExpired()); } - if (!safeMethod(settings.type) && sameOrigin(settings.url)) { - xhr.setRequestHeader("X-CSRFToken", "<?php echo csrf_get_token(); ?>"); + + function getFormInput($name='') { + if(!$name) $name = $this->name; + + return sprintf('<input type="hidden" name="%s" value="%s" />', $name, $this->getToken()); } -}); -</script> -<?php } +} +/* global function to add hidden token input with to forms */ +function csrf_token() { + global $ost; + + if($ost && $ost->getCSRF()) + echo $ost->getCSRFFormInput(); +} ?> -- GitLab