Skip to content

Commit 163badc

Browse files
author
Branko Vukelic
committed
Added existing CsrfToken code.
0 parents  commit 163badc

File tree

2 files changed

+256
-0
lines changed

2 files changed

+256
-0
lines changed

CsrfToken.php

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
<?php
2+
/**
3+
* CsrfToken.php
4+
*
5+
* This file contains the CsrfToken class that handles genration and checking
6+
* of Synchronization tokens (http://bit.ly/owasp_synctoken).
7+
*
8+
* The basic usage involves initializing an instance at some point, calling
9+
* either the getHiddenField() or generateToken() methods. The former produces
10+
* an XHTML-compliant input element, whereas the latter produces a raw
11+
* Base64-encoded string. In another request, the request can be tested for
12+
* authenticity (to the best of this script's author's knowledge) by calling
13+
* the checkToken() method.
14+
*
15+
* The generateHiddenField() and generateToken() create a $_SESSION['csrf']
16+
* array, which contains the material for token creation. This data is
17+
* preserved so that the token can be checked later.
18+
*
19+
* DISCLAIMER: This script has not been widely tested (actually, it's been only
20+
* tested on a local host), so I do not recommend using it without sufficient
21+
* testing. That said, I do think it will work as expected.
22+
*
23+
* TODO: Write unit tests.
24+
*
25+
* @author Branko Vukelic <studio@brankovukelic.com>
26+
* @version 0.1
27+
* @package Csrf
28+
*/
29+
namespace Csrf;
30+
31+
32+
/**
33+
* Token generation and checking class
34+
*
35+
* This class encapsulates all of the functionality of the Csrf package. On
36+
* initialization, it checks for session ID, and it will throw an exception is
37+
* one is not found, so it is best you initialize right after session_start().
38+
*
39+
* Since the time used to generate the token is not the time when
40+
* initialization takes place, you can initialize at any time before token
41+
* generation.
42+
*
43+
* @package Csrf
44+
* @subpackage classes
45+
*/
46+
class CsrfToken {
47+
48+
/**
49+
* Flag to determine whether GET HTTP verb is checked for a token.
50+
* Otherwise, only POST will be checked (default).
51+
* @access protected
52+
* @var boolean
53+
*/
54+
protected $acceptGet = FALSE;
55+
56+
/**
57+
* Default timeout for token check. If the request is made outside of this
58+
* time frame, it will be considered invalid. This parameter can be
59+
* manually overriden at check time by supplying the appropriate arugment
60+
* to the {@link checkToken()} method.
61+
* @access protected
62+
* @var integer
63+
*/
64+
protected $timeout = 300;
65+
66+
/**
67+
* While initializing this class, it is possible to specify the {@link
68+
* $timeout} parameter. The timeout is 300 seconds (5 minutes) by default.
69+
* The {@link acceptGet} argument can be set to TRUE if you wish to
70+
* include GET requests in the check. Otherwise, all GET requests will be
71+
* considered invalid (default).
72+
*/
73+
public function __construct($timeout=300, $acceptGet=\FALSE){
74+
$this->timeout = $timeout;
75+
if (\session_id()) {
76+
$this->acceptGet = (bool) $acceptGet;
77+
} else {
78+
throw new Exception('Could not find session id', 1);
79+
}
80+
}
81+
82+
/**
83+
* Utility function for random string generation of the $len length.
84+
* @param integer $len (defaults to 10) length of the generated string
85+
* @return string
86+
*/
87+
public function randomString($len = 10) {
88+
// Characters that may look like other characters in different fonts
89+
// have been omitted.
90+
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789';
91+
$charsTotal = \strlen($chars);
92+
for ($i = 0; $i < $len; $i++) {
93+
$string .= $chars[\mt_rand(0, $charsTotal)];
94+
}
95+
return $string;
96+
}
97+
98+
/**
99+
* Calculates the SHA1 hash from the csrf token material found in
100+
* $_SESSION['csrf']. This function is not used directly. It is called by
101+
* other public CsrfToken method.
102+
* @see generateToken(), generateHiddenField(), checkToken()
103+
* @visibility protected
104+
* @return string
105+
*/
106+
protected function calculateHash() {
107+
return \sha1(\implode('', $_SESSION['csrf']));
108+
}
109+
110+
/**
111+
* Generates the token string encoded using Base64 algorythm. When this
112+
* method is called, it also resets any data in the $_SESSION['csrf']
113+
* array, so it can be called multiple times. It is not wise to call this
114+
* method before performing a chek for an earlier request.
115+
* @see generateHiddenField()
116+
* @visibility public
117+
* @return string
118+
*/
119+
public function generateToken() {
120+
// Create or overwrite the csrf entry in the seesion
121+
$_SESSION['csrf'] = array();
122+
$_SESSION['csrf']['time'] = \time();
123+
$_SESSION['csrf']['salt'] = $this->randomString(32);
124+
$_SESSION['csrf']['sessid'] = \session_id();
125+
$_SESSION['csrf']['ip'] = $_SERVER['REMOTE_ADDR'];
126+
// Generate the SHA1 hash
127+
$hash = $this->calculateHash();
128+
// Generate and return the token
129+
return \base64_encode($hash);
130+
}
131+
132+
/**
133+
* Generate the entire hiddent form element containing the token. Since
134+
* Sychronize Token CSRF protection is most effective with POST requests,
135+
* this convenience method allows you to generate a prefabricated hidden
136+
* element that you will insert into your forms. The markup is XHTML
137+
* compliant. Since it will not break regular HTML or HTML5 markup, there
138+
* are no options for customization. You can use the
139+
* {@link generateToken()} method if you want a custom markup, or just
140+
* want the raw token string.
141+
* @see generateToken()
142+
* @visibility public
143+
* @return string
144+
*/
145+
public function generateHiddenField() {
146+
// Shortcut method to generate the entire form
147+
// element containing the CSRF protection token
148+
$token = $this->generateToken();
149+
return "<input type=\"hidden\" name=\"csrf\" value=\"$token\" />";
150+
}
151+
152+
/**
153+
* Check the timeliness of the request. This method is not meant to be
154+
* called directly, but is called by the {@link checkToken()} method. It
155+
* checks the time recorded in the session against the time of request,
156+
* and returns TRUE if the request was just in time, or FALSE if the
157+
* request broke the time limit.
158+
* @see checkToken()
159+
* @visibility protected
160+
* @param integer $timeout request timeout in seconds
161+
* @return boolean
162+
*/
163+
protected function checkTimeout($timeout=\NULL) {
164+
if (!$timeout) {
165+
$timeout = $this->timeout;
166+
}
167+
return ($_SERVER['REQUEST_TIME'] - $_SESSION['csrf']['time']) < $timeout;
168+
}
169+
170+
/**
171+
* Checks the token to authenticate the request. The check will fail if
172+
* the session wasn't started (or the session id got lost somehow), if the
173+
* $_SESSION['csrf'] wasn't set (probably the form page didn't do its part
174+
* in generating and using the token), if the request did not contain the
175+
* 'csrf' parameter, or if the 'csrf' parameter does not match the
176+
* generated from the information in the $_SESSION['csrf']. The check will
177+
* also fail if the request was made outside of the time limit specified
178+
* by the optional $timeout parameter or took longer than the default 5
179+
* minutes. For multi-page scenarios, or for longer forms (like blog posts
180+
* and user comments) it is recommended that you manually extend the time
181+
* limit to a more reasonable time frame.
182+
* @visibility public
183+
* @param integer $timeout
184+
* @return boolean
185+
*/
186+
public function checkToken($timeout=\NULL) {
187+
// Default timeout is 300 seconds (5 minutes)
188+
189+
// First check if csrf information is present in the session
190+
if (isset($_SESSION['csrf'])) {
191+
192+
// Check the timeliness of the request
193+
if (!$this->checkTimeout($timeout)) {
194+
return \FALSE;
195+
}
196+
197+
// Check if there is a session id
198+
if (\session_id()) {
199+
// Check if response contains a usable csrf token
200+
$isCsrfGet = isset($_GET['csrf']);
201+
$isCsrfPost = isset($_POST['csrf']);
202+
if (($this->acceptGet and $isCsrfGet) or $isCsrfPost) {
203+
// Decode the received token hash
204+
$tokenHash = \base64_decode($_REQUEST['csrf']);
205+
// Generate a new hash from the data we have
206+
$generatedHash = $this->calculateHash();
207+
// Compare and return the result
208+
if ($tokenHash and $generatedHash) {
209+
return $tokenHash == $generatedHash;
210+
}
211+
}
212+
}
213+
}
214+
215+
// In all other cases return FALSE
216+
return \FALSE;
217+
}
218+
219+
}
220+
221+
?>

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
CSRF4PHP: Cross-Site Request Forgery kit for for PHP
2+
====================================================
3+
4+
This file contains the CsrfToken class that handles genration and checking
5+
of [Synchronization tokens](http://bit.ly/owasp_synctoken).
6+
7+
In future more features will be incorporated into this kit, but the CsrfToken
8+
class is the most important part of the puzzle.
9+
10+
Basic usage scenario
11+
--------------------
12+
13+
The basic usage involves initializing an instance at some point, calling
14+
either the getHiddenField() or generateToken() methods. The former produces
15+
an XHTML-compliant input element, whereas the latter produces a raw
16+
Base64-encoded string. In another request, the request can be tested for
17+
authenticity (to the best of this script's author's knowledge) by calling
18+
the checkToken() method.
19+
20+
The generateHiddenField() and generateToken() create a $_SESSION['csrf']
21+
array, which contains the material for token creation. This data is
22+
preserved so that the token can be checked later.
23+
24+
Disclaimer
25+
----------
26+
27+
This script has not been widely tested (actually, it's been only tested on
28+
a local host), so I do not recommend using it without sufficient testing.
29+
That said, I do think it will work as expected.
30+
31+
TODO
32+
----
33+
34+
* Write unit tests for the CsrfToken class.
35+
* Implement a helper function or class for checking the HTTP Referrer header.

0 commit comments

Comments
 (0)