Issues (4868)

api/src/Csrf.php (1 issue)

Severity
1
<?php
2
/**
3
 * EGroupware API: CSRF (Cross Site Request Forgery) protection
4
 *
5
 * @link http://www.egroupware.org
6
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
7
 * @package api
8
 * @author Ralf Becker <[email protected]>
9
 * @copyright (c) 2014-16 by Ralf Becker <[email protected]>
10
 * @version $Id$
11
 */
12
13
namespace EGroupware\Api;
14
15
/**
16
 * Class supplying methods to prevent successful CSRF by requesting a random token,
17
 * stored on server and validated when request get posted.
18
 *
19
 * CSRF token generation used openssl_random_pseudo_bytes, if available, otherwise
20
 * mt_rand based Auth::randomstring is used.
21
 *
22
 * CSRF tokens are stored (incl. optional purpose) in user session.
23
 *
24
 * If a token does not validate (incl. purpose, if specified in generation)
25
 * the request will be imediatly terminated.
26
 */
27
class Csrf
28
{
29
	/**
30
	 * Get a CSRF token for an optional $purpose, which can be validated
31
	 *
32
	 * @param mixed $_purpose =true if given it need to be used in validate too! (It must NOT be NULL)
33
	 * @return string CSRF token
34
	 * @throws Exception\WrongParameter
35
	 * @throws \Exception if it was not possible to gather sufficient entropy.
36
	 */
37
	public static function token($_purpose=true)
38
	{
39
		if (is_null($_purpose))
40
		{
41
			throw new Exception\WrongParameter(__METHOD__.'(NULL) $_purspose must NOT be NULL!');
42
		}
43
		// generate random token (using oppenssl if available otherwise mt_rand based Auth::randomstring)
44
		$token = base64_encode(random_bytes(32));
45
46
		// store it in session for later validation
47
		Cache::setSession(__CLASS__, $token, $_purpose);
48
49
		return $token;
50
	}
51
52
	/**
53
	 * Validate a CSRF token or teminate the request
54
	 *
55
	 * @param string $_token CSRF token generated with egw_csrf::token()
56
	 * @param string $_purpose =true optional purpose string passed to token method
57
	 * @param boolean $_delete_token =true true if token should be deleted after validation, it will validate no second time
58
	 */
59
	public static function validate($_token, $_purpose=true, $_delete_token=true)
60
	{
61
		$stored_purpose = Cache::getSession(__CLASS__, $_token);
62
63
		// if token and purpose dont validate, log and terminate request
64
		if (!isset($stored_purpose) || $stored_purpose !== $_purpose)
65
		{
66
			error_log('CSRF detected from IP '.$_SERVER['REMOTE_ADDR'].' to '.$_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI']);
67
			if ($_POST) error_log(array2string($_POST));
68
			// we are not throwing an exception here, but die, to not allow catching it!
69
			die("CSRF detected, request terminated!");
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
70
		}
71
		if ($_delete_token) Cache::unsetSession(__CLASS__, $_token);
72
	}
73
}