From 7a590cebe3e79c80cb20e31af1c4534335682e17 Mon Sep 17 00:00:00 2001 From: "Edward Z. Yang" Date: Wed, 6 Aug 2008 19:06:52 -0400 Subject: [PATCH] Make IP based tokens work by sending them if the user has no cookies. Signed-off-by: Edward Z. Yang --- csrf-magic.php | 50 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/csrf-magic.php b/csrf-magic.php index 08af83a..d06b376 100644 --- a/csrf-magic.php +++ b/csrf-magic.php @@ -127,16 +127,16 @@ function csrf_ob_handler($buffer, $flags) { return $buffer; } } - $token = csrf_get_token(); + $tokens = csrf_get_tokens(); $name = $GLOBALS['csrf']['input-name']; $endslash = $GLOBALS['csrf']['xhtml'] ? ' /' : ''; - $input = ""; + $input = ""; $buffer = preg_replace('#(]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer); if ($js = $GLOBALS['csrf']['rewrite-js']) { $buffer = preg_replace( '#()#i', ''. '$1', $buffer @@ -164,7 +164,7 @@ function csrf_check($fatal = true) { if (!isset($_POST[$name])) break; // we don't regenerate a token and check it because some token creation // schemes are volatile. - if (!csrf_check_token($_POST[$name])) break; + if (!csrf_check_tokens($_POST[$name])) break; $ok = true; } while (false); if ($fatal && !$ok) { @@ -176,22 +176,34 @@ function csrf_check($fatal = true) { } /** - * Retrieves a valid token for a particular context. + * Retrieves a valid token(s) for a particular context. Tokens are separated + * by semicolons. */ -function csrf_get_token() { +function csrf_get_tokens() { $secret = csrf_get_secret(); + $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:' . sha1($secret . $_SERVER['IP_ADDRESS']); + } else { + $ip = ''; + } csrf_start(); + // These are "strong" algorithms that don't require per se a secret - if (session_id()) return 'sid:' . sha1($secret . session_id()); - if ($GLOBALS['csrf']['key']) return 'key:' . sha1($secret . $GLOBALS['csrf']['key']); + if (session_id()) return 'sid:' . sha1($secret . session_id()) . $ip; + if ($GLOBALS['csrf']['key']) return 'key:' . sha1($secret . $GLOBALS['csrf']['key']) . $ip; // These further algorithms require a server-side secret if ($secret === '') return 'invalid'; if ($GLOBALS['csrf']['user'] !== false) { return 'user:' . sha1($secret . $GLOBALS['csrf']['user']); } if ($GLOBALS['csrf']['allow-ip']) { - // :TODO: Harden this against proxy-spoofing attacks - return 'ip:' . sha1($secret . $_SERVER['IP_ADDRESS']); + return ltrim($ip, ';'); } return 'invalid'; } @@ -203,6 +215,18 @@ function csrf_callback() { } /** + * 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) { @@ -223,9 +247,11 @@ function csrf_check_token($token) { if ($GLOBALS['csrf']['user'] === false) return false; return $value === sha1($secret . $GLOBALS['csrf']['user']); case 'ip': - if ($GLOBALS['csrf']['secret'] === '') return false; - // do not allow IP-based checks if the username is set + 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 === sha1($secret . $_SERVER['IP_ADDRESS']); } -- 2.11.4.GIT