1 | <?php |
||||
2 | /** |
||||
3 | * @package WPEmerge |
||||
4 | * @author Atanas Angelov <[email protected]> |
||||
5 | * @copyright 2018 Atanas Angelov |
||||
6 | * @license https://www.gnu.org/licenses/gpl-2.0.html GPL-2.0 |
||||
7 | * @link https://wpemerge.com/ |
||||
8 | */ |
||||
9 | |||||
10 | namespace WPEmerge\Csrf; |
||||
11 | |||||
12 | use WPEmerge\Requests\RequestInterface; |
||||
13 | |||||
14 | /** |
||||
15 | * Provide CSRF protection utilities through WordPress nonces. |
||||
16 | */ |
||||
17 | class Csrf { |
||||
18 | /** |
||||
19 | * Convenience header to check for the token. |
||||
20 | * |
||||
21 | * @var string |
||||
22 | */ |
||||
23 | protected $header = 'X-CSRF-TOKEN'; |
||||
24 | |||||
25 | /** |
||||
26 | * GET/POST parameter key to check for the token. |
||||
27 | * |
||||
28 | * @var string |
||||
29 | */ |
||||
30 | protected $key = ''; |
||||
31 | |||||
32 | /** |
||||
33 | * Maximum token lifetime. |
||||
34 | * |
||||
35 | * @link https://codex.wordpress.org/Function_Reference/wp_verify_nonce |
||||
36 | * @var integer |
||||
37 | */ |
||||
38 | protected $maximum_lifetime = 2; |
||||
39 | |||||
40 | /** |
||||
41 | * Last generated token. |
||||
42 | * |
||||
43 | * @var string |
||||
44 | */ |
||||
45 | protected $token = ''; |
||||
46 | |||||
47 | /** |
||||
48 | * Constructor. |
||||
49 | * |
||||
50 | * @codeCoverageIgnore |
||||
51 | * @param string $key |
||||
52 | * @param integer $maximum_lifetime |
||||
53 | */ |
||||
54 | public function __construct( $key = '__wpemergeCsrfToken', $maximum_lifetime = 2 ) { |
||||
55 | $this->key = $key; |
||||
56 | $this->maximum_lifetime = $maximum_lifetime; |
||||
57 | } |
||||
58 | |||||
59 | /** |
||||
60 | * Get the last generated token. |
||||
61 | * |
||||
62 | * @return string |
||||
63 | */ |
||||
64 | public function getToken() { |
||||
65 | if ( ! $this->token ) { |
||||
66 | $this->generateToken(); |
||||
67 | } |
||||
68 | return $this->token; |
||||
69 | } |
||||
70 | |||||
71 | /** |
||||
72 | * Get the csrf token from a request. |
||||
73 | * |
||||
74 | * @param RequestInterface $request |
||||
75 | * @return string |
||||
76 | */ |
||||
77 | public function getTokenFromRequest( RequestInterface $request ) { |
||||
78 | if ( $request->get( $this->key ) ) { |
||||
0 ignored issues
–
show
|
|||||
79 | return $request->get( $this->key ); |
||||
80 | } |
||||
81 | |||||
82 | if ( $request->post( $this->key ) ) { |
||||
0 ignored issues
–
show
The call to
WPEmerge\Requests\RequestInterface::post() has too many arguments starting with $this->key .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
83 | return $request->post( $this->key ); |
||||
84 | } |
||||
85 | |||||
86 | if ( $request->headers( $this->header ) ) { |
||||
0 ignored issues
–
show
The call to
WPEmerge\Requests\RequestInterface::headers() has too many arguments starting with $this->header .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() |
|||||
87 | return $request->headers( $this->header ); |
||||
88 | } |
||||
89 | |||||
90 | return ''; |
||||
91 | } |
||||
92 | |||||
93 | /** |
||||
94 | * Generate a new token. |
||||
95 | * |
||||
96 | * @param int|string $action |
||||
97 | * @return string |
||||
98 | */ |
||||
99 | public function generateToken( $action = -1 ) { |
||||
100 | $action = $action === -1 ? session_id() : $action; |
||||
101 | $this->token = wp_create_nonce( $action ); |
||||
102 | return $this->getToken(); |
||||
103 | } |
||||
104 | |||||
105 | /** |
||||
106 | * Check if a token is valid. |
||||
107 | * |
||||
108 | * @param string $token |
||||
109 | * @param int|string $action |
||||
110 | * @return boolean |
||||
111 | */ |
||||
112 | public function isValidToken( $token, $action = -1 ) { |
||||
113 | $action = $action === -1 ? session_id() : $action; |
||||
114 | $lifetime = (int) wp_verify_nonce( $token, $action ); |
||||
115 | return ( $lifetime > 0 && $lifetime <= $this->maximum_lifetime ); |
||||
116 | } |
||||
117 | |||||
118 | /** |
||||
119 | * Add the token to a URL. |
||||
120 | * |
||||
121 | * @param string $url |
||||
122 | * @return string |
||||
123 | */ |
||||
124 | public function url( $url ) { |
||||
125 | return add_query_arg( $this->key, $this->getToken(), $url ); |
||||
126 | } |
||||
127 | |||||
128 | /** |
||||
129 | * Return the markup for a hidden input which holds the current token. |
||||
130 | * |
||||
131 | * @return void |
||||
132 | */ |
||||
133 | public function field() { |
||||
134 | echo sprintf( |
||||
135 | '<input type="hidden" name="%1$s" value="%2$s" />', |
||||
136 | esc_attr( $this->key ), |
||||
137 | esc_attr( $this->getToken() ) |
||||
138 | ); |
||||
139 | } |
||||
140 | } |
||||
141 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.