1
|
|
|
<?php /** CsrfFilterMicro */ |
2
|
|
|
|
3
|
|
|
namespace Micro\Filter; |
4
|
|
|
|
5
|
|
|
use Micro\Base\Exception; |
6
|
|
|
use Micro\Web\ISession; |
7
|
|
|
use Micro\Web\RequestInjector; |
8
|
|
|
use Micro\Web\SessionInjector; |
9
|
|
|
use Psr\Http\Message\ServerRequestInterface; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Class CsrfFilter |
13
|
|
|
* |
14
|
|
|
* @author Oleg Lunegov <[email protected]> |
15
|
|
|
* @link https://github.com/linpax/microphp-framework |
16
|
|
|
* @copyright Copyright (c) 2013 Oleg Lunegov |
17
|
|
|
* @license https://github.com/linpax/microphp-framework/blob/master/LICENSE |
18
|
|
|
* @package Micro |
19
|
|
|
* @subpackage Filter |
20
|
|
|
* @version 1.0 |
21
|
|
|
* @since 1.0 |
22
|
|
|
*/ |
23
|
|
|
class CsrfFilter extends Filter |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* @param array $params |
27
|
|
|
* @return bool |
28
|
|
|
* @throws Exception |
29
|
|
|
*/ |
30
|
|
|
public function pre(array $params) |
31
|
|
|
{ |
32
|
|
|
/** @var ServerRequestInterface $session */ |
33
|
|
|
$server = (new RequestInjector)->build(); |
34
|
|
|
/** @var ISession $session */ |
35
|
|
|
$session = (new SessionInjector)->build(); |
36
|
|
|
|
37
|
|
|
/** @var array $params */ |
38
|
|
|
$params = $server->getServerParams(); |
39
|
|
|
/** @var array $post */ |
40
|
|
|
$post = $server->getParsedBody(); |
41
|
|
|
|
42
|
|
|
if ($params['REQUEST_METHOD'] !== 'POST') { |
43
|
|
|
return true; |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
$postCSRF = $post['csrf']; |
47
|
|
|
|
48
|
|
View Code Duplication |
if (!$postCSRF) { |
|
|
|
|
49
|
|
|
$this->result = [ |
50
|
|
|
'redirect' => !empty($rule['redirect']) ? $rule['redirect'] : null, |
|
|
|
|
51
|
|
|
'message' => !empty($rule['message']) ? $rule['message'] : 'Not allowed!' |
52
|
|
|
]; |
53
|
|
|
|
54
|
|
|
return false; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** @var array $csrf */ |
58
|
|
|
$csrf = $session->csrf; |
|
|
|
|
59
|
|
|
|
60
|
|
|
if (($key = in_array(md5($postCSRF), $session->csrf, true)) !== null) { |
|
|
|
|
61
|
|
|
unset($session->csrf[md5($postCSRF)]); |
62
|
|
|
|
63
|
|
|
$session->csrf = $csrf; |
|
|
|
|
64
|
|
|
|
65
|
|
|
return true; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
$this->result = [ |
69
|
|
|
'redirect' => !empty($rule['redirect']) ? $rule['redirect'] : null, |
70
|
|
|
'message' => !empty($rule['message']) ? $rule['message'] : 'Bad request!' |
71
|
|
|
]; |
72
|
|
|
|
73
|
|
|
return false; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @inheritdoc |
78
|
|
|
*/ |
79
|
|
|
public function post(array $params) |
80
|
|
|
{ |
81
|
|
|
return preg_replace_callback('/(<form[^>]*>)(.*?)(<\/form>)/m', [$this, 'insertProtect'], $params['data']); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Insert CSRF protect into forms |
86
|
|
|
* |
87
|
|
|
* @access public |
88
|
|
|
* |
89
|
|
|
* @param array $matches Form |
90
|
|
|
* |
91
|
|
|
* @return string |
92
|
|
|
* @throws Exception |
93
|
|
|
*/ |
94
|
|
|
public function insertProtect(array $matches = []) |
95
|
|
|
{ |
96
|
|
|
$gen = md5(mt_rand()); |
97
|
|
|
|
98
|
|
|
/** @var ISession $s */ |
99
|
|
|
$s = (new SessionInjector)->build(); |
100
|
|
|
$s->csrf = array_merge(is_array($s->csrf) ? $s->csrf : [], [md5($gen)]); |
|
|
|
|
101
|
|
|
|
102
|
|
|
return $matches[1].'<input type="hidden" name="csrf" value="'.$gen.'" />'.$matches[2].$matches[3]; |
103
|
|
|
} |
104
|
|
|
} |
105
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.