Implement MooTools, also improve previous test code.
[csrf-magic.git] / csrf-magic.php
blob544de8034e23b75073468ff429b48970b948b2f2
1 <?php
3 /**
4 * @file
6 * csrf-magic is a PHP library that makes adding CSRF-protection to your
7 * web applications a snap. No need to modify every form or create a database
8 * of valid nonces; just include this file at the top of every
9 * web-accessible page (or even better, your common include file included
10 * in every page), and forget about it! (There are, of course, configuration
11 * options for advanced users).
13 * This library is PHP4 and PHP5 compatible.
16 // CONFIGURATION:
18 /**
19 * The name of the magic CSRF token that will be placed in all forms, i.e.
20 * the contents of <input type="hidden" name="$name" value="CSRF-TOKEN" />
22 $GLOBALS['csrf']['input-name'] = '__csrf';
24 /**
25 * Whether or not CSRF Magic should be allowed to start a new session in order
26 * to determine the key.
28 $GLOBALS['csrf']['auto-session'] = true;
30 /**
31 * A secret key used when hashing items. Please generate a random string and
32 * place it here. If you change this value, all previously generated tokens
33 * will become invalid.
35 $GLOBALS['csrf']['secret'] = '';
37 /**
38 * Whether or not to use IP addresses when binding a user to a token. This is
39 * less reliable and less secure than sessions, but is useful when you need
40 * to give facilities to anonymous users and do not wish to maintain a database
41 * of valid keys.
42 * @warning Not implemented yet
44 $GLOBALS['csrf']['allow-ip'] = true;
46 /**
47 * Whether or not to include our JavaScript library which also rewrites
48 * AJAX requests on this domain. Set this to the path.
50 $GLOBALS['csrf']['rewrite-js'] = false;
52 // FUNCTIONS:
54 /**
55 * Rewrites <form> on the fly to add CSRF tokens to them. This can also
56 * inject our JavaScript library.
58 function csrf_ob_handler($buffer, $flags) {
59 $token = csrf_get_token();
60 $name = $GLOBALS['csrf']['input-name'];
61 $input = "<input type='hidden' name='$name' value=\"$token\" />";
62 $buffer = preg_replace('#(<form[^>]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer);
63 if ($js = $GLOBALS['csrf']['rewrite-js']) {
64 $buffer = preg_replace(
65 '#(</head>)#i',
66 '<script type="text/javascript">'.
67 'var csrfMagicToken = "'.$token.'";'.
68 'var csrfMagicName = "'.$name.'";</script>'.
69 '<script src="'.$js.'" type="text/javascript"></script>$1',
70 $buffer
73 return $buffer;
76 /**
77 * Checks if this is a post request, and if it is, checks if the nonce is valid.
78 * @param bool $fatal Whether or not to fatally error out if there is a problem.
80 function csrf_check($fatal = true) {
81 if ($_SERVER['REQUEST_METHOD'] !== 'POST') return;
82 csrf_start();
83 $name = $GLOBALS['csrf']['input-name'];
84 $ok = false;
85 do {
86 if (!isset($_POST[$name])) break;
87 if (!csrf_check_token($_POST[$name])) break;
88 $ok = true;
89 } while (false);
90 if ($fatal && !$ok) {
91 echo '<html><body>You failed CSRF protection</body></html>'.PHP_EOL;
92 exit;
96 /**
97 * Retrieves a valid token for a particular context.
99 function csrf_get_token() {
100 $secret = $GLOBALS['csrf']['secret'];
101 csrf_start();
102 if (session_id()) return 'sid:' . sha1($secret . session_id());
103 // Ok, session failed, let's see if we've got an IP address
105 // Uh-oh, you need some configuration!
106 return 'invalid';
110 * Checks if a token is valid.
112 function csrf_check_token($token) {
113 if (strpos($token, ':') === false) return false;
114 list($type, $value) = explode(':', $token, 2);
115 $secret = $GLOBALS['csrf']['secret'];
116 switch ($type) {
117 case 'sid':
118 return $value === sha1($secret . session_id());
119 default:
120 return false;
122 return false;
126 * Sets a configuration value.
128 function csrf_conf($key, $val) {
129 if (!isset($GLOBALS['csrf'][$key])) {
130 trigger_error('No such configuration ' . $key, E_USER_WARNING);
131 return;
133 $GLOBALS['csrf'][$key] = $val;
137 * Starts a session if we're allowed to.
139 function csrf_start() {
140 if ($GLOBALS['csrf']['auto-session'] && !session_id()) {
141 session_start();
145 // Initialize our handler
146 ob_start('csrf_ob_handler');
147 // Load user configuration
148 if (function_exists('csrf_startup')) csrf_startup();
149 // Perform check
150 csrf_check();