/
form_api.php
204 lines (168 loc) · 6.25 KB
/
form_api.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
<?php
# MantisBT - A PHP based bugtracking system
# MantisBT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# MantisBT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with MantisBT. If not, see <http://www.gnu.org/licenses/>.
/**
* Form API
*
* Handles form security and validation. Security methods are targeted to
* work with both GET and POST form types and should allow multiple
* simultaneous edits of the form to be submitted out-of-order.
*
* @package CoreAPI
* @subpackage FormAPI
* @copyright Copyright 2000 - 2002 Kenzaburo Ito - kenito@300baud.org
* @copyright Copyright 2002 - 2013 MantisBT Team - mantisbt-dev@lists.sourceforge.net
* @link http://www.mantisbt.org
*
* @uses config_api.php
* @uses constant_inc.php
* @uses crypto_api.php
* @uses gpc_api.php
* @uses php_api.php
* @uses session_api.php
*/
require_api( 'config_api.php' );
require_api( 'constant_inc.php' );
require_api( 'crypto_api.php' );
require_api( 'gpc_api.php' );
require_api( 'php_api.php' );
require_api( 'session_api.php' );
/**
* Generate a random security token, prefixed by date, store it in the
* user's session, and then return the string to be used as a form element
* element with the security token as the value.
* @param string $p_form_name Form name
* @return string Security token string
*/
function form_security_token( $p_form_name ) {
if ( PHP_CLI == php_mode() || OFF == config_get_global( 'form_security_validation' ) ) {
return '';
}
$t_tokens = session_get( 'form_security_tokens', array() );
# Create a new array for the form name if necessary
if( !isset( $t_tokens[$p_form_name] ) || !is_array( $t_tokens[$p_form_name] ) ) {
$t_tokens[$p_form_name] = array();
}
# Generate a nonce prefixed by date.
# With a base64 output encoded nonce length of 32 characters, we are
# generating a 192bit nonce.
$t_date = date( 'Ymd' );
$t_string = $t_date . crypto_generate_uri_safe_nonce( 32 );
# Add the token to the user's session
if ( !isset( $t_tokens[$p_form_name][$t_date] ) ) {
$t_tokens[$p_form_name][$t_date] = array();
}
$t_tokens[$p_form_name][$t_date][$t_string] = true;
session_set( 'form_security_tokens', $t_tokens );
# The token string
return $t_string;
}
/**
* Get a hidden form element containing a generated form security token.
* @param string $p_form_name Form name
* @return string Hidden form element to output
*/
function form_security_field( $p_form_name ) {
if ( PHP_CLI == php_mode() || OFF == config_get_global( 'form_security_validation' ) ) {
return '';
}
$t_string = form_security_token( $p_form_name );
# Create the form element HTML string for the security token
$t_form_token = $p_form_name . '_token';
$t_element = '<input type="hidden" name="%s" value="%s"/>';
$t_element = sprintf( $t_element, $t_form_token, $t_string );
return $t_element;
}
/**
* Get a URL parameter containing a generated form security token.
* @param string $p_form_name Form name
* @return string Hidden form element to output
*/
function form_security_param( $p_form_name ) {
if ( PHP_CLI == php_mode() || OFF == config_get_global( 'form_security_validation' ) ) {
return '';
}
$t_string = form_security_token( $p_form_name );
# Create the GET parameter to be used in a URL for a secure link
$t_form_token = $p_form_name . '_token';
$t_param = '&%s=%s';
$t_param = sprintf( $t_param, $t_form_token, $t_string );
return $t_param;
}
/**
* Validate the security token for the given form name based on tokens
* stored in the user's session. While checking stored tokens, any that
* are more than 3 days old will be purged.
* @param string $p_form_name Form name
* @return boolean Form is valid
* @throws MantisBT\Exception\Security\CSRFTokenInvalid
*/
function form_security_validate( $p_form_name ) {
if ( PHP_CLI == php_mode() || OFF == config_get_global( 'form_security_validation' ) ) {
return true;
}
$t_tokens = session_get( 'form_security_tokens', array() );
# Short-circuit if we don't have any tokens for the given form name
if( !isset( $t_tokens[$p_form_name] ) || !is_array( $t_tokens[$p_form_name] ) || count( $t_tokens[$p_form_name] ) < 1 ) {
throw new MantisBT\Exception\Security\CSRFTokenInvalid();
}
# Get the form input
$t_form_token = $p_form_name . '_token';
$t_input = gpc_get_string( $t_form_token, '' );
# No form input
if( '' == $t_input ) {
throw new MantisBT\Exception\Security\CSRFTokenInvalid();
}
# Get the date claimed by the token
$t_date = mb_substr( $t_input, 0, 8 );
# Check if the token exists
if ( isset( $t_tokens[$p_form_name][$t_date][$t_input] ) ) {
return true;
}
# Token does not exist
throw new MantisBT\Exception\Security\CSRFTokenInvalid();
}
/**
* Purge form security tokens that are older than 3 days, or used
* for form validation.
* @param string $p_form_name Form name
*/
function form_security_purge( $p_form_name ) {
if ( PHP_CLI == php_mode() || OFF == config_get_global( 'form_security_validation' ) ) {
return;
}
$t_tokens = session_get( 'form_security_tokens', array() );
# Short-circuit if we don't have any tokens for the given form name
if( !isset( $t_tokens[$p_form_name] ) || !is_array( $t_tokens[$p_form_name] ) || count( $t_tokens[$p_form_name] ) < 1 ) {
return;
}
# Get the form input
$t_form_token = $p_form_name . '_token';
$t_input = gpc_get_string( $t_form_token, '' );
# Get the date claimed by the token
$t_date = mb_substr( $t_input, 0, 8 );
# Generate a date string of three days ago
$t_purge_date = date( 'Ymd', time() - ( 3 * 24 * 60 * 60 ) );
# Purge old token data, and the currently-used token
unset( $t_tokens[$p_form_name][$t_date][$t_input] );
foreach( $t_tokens as $t_form_name => $t_dates ) {
foreach( $t_dates as $t_date => $t_date_tokens ) {
if ( $t_date < $t_purge_date ) {
unset( $t_tokens[$t_form_name][$t_date] );
}
}
}
session_set( 'form_security_tokens', $t_tokens );
return;
}