Skip to content

Commit 7429c2d

Browse files
committed
Input Filter support. See README.input_filter for details.
@- Input Filter support added. See README.input_filter. (Rasmus)
1 parent 0458bb5 commit 7429c2d

File tree

8 files changed

+212
-3
lines changed

8 files changed

+212
-3
lines changed

README.input_filter

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
Input Filter Support in PHP5
2+
----------------------------
3+
4+
XSS (Cross Site Scripting) hacks are becoming more and more prevalent,
5+
and can be quite difficult to prevent. Whenever you accept user data
6+
and somehow display this data back to users, you are likely vulnerable
7+
to XSS hacks.
8+
9+
The Input Filter support in PHP5 is aimed at providing the framework
10+
through which a company-wide or site-wide security policy can be
11+
enforced. It is implemented as a SAPI hook and is called from the
12+
treat_data and post handler functions. To implement your own security
13+
policy you will need to write a standard PHP extension.
14+
15+
A simple implementation might look like the following. This stores the
16+
original raw user data and adds a my_get_raw() function while the normal
17+
$_POST, $_GET and $_COOKIE arrays are only populated with stripped
18+
data. In this simple example all I am doing is calling strip_tags() on
19+
the data. If register_globals is turned on, the default globals that
20+
are created will be stripped ($foo) while a $RAW_foo is created with the
21+
original user input.
22+
23+
ZEND_BEGIN_MODULE_GLOBALS(my_input_filter)
24+
zval *post_array;
25+
zval *get_array;
26+
zval *cookie_array;
27+
ZEND_END_MODULE_GLOBALS(my_input_filter)
28+
29+
#ifdef ZTS
30+
#define IF_G(v) TSRMG(my_input_filter_globals_id, zend_my_input_filter_globals *, v)
31+
#else
32+
#define IF_G(v) (my_input_filter_globals.v)
33+
#endif
34+
35+
ZEND_DECLARE_MODULE_GLOBALS(my_input_filter)
36+
37+
function_entry my_input_filter_functions[] = {
38+
PHP_FE(my_get_raw, NULL)
39+
{NULL, NULL, NULL}
40+
};
41+
42+
zend_module_entry my_input_filter_module_entry = {
43+
STANDARD_MODULE_HEADER,
44+
"my_input_filter",
45+
my_input_filter_functions,
46+
PHP_MINIT(my_input_filter),
47+
PHP_MSHUTDOWN(my_input_filter),
48+
NULL,
49+
PHP_RSHUTDOWN(my_input_filter),
50+
PHP_MINFO(my_input_filter),
51+
"0.1",
52+
STANDARD_MODULE_PROPERTIES
53+
};
54+
55+
PHP_MINIT_FUNCTION(my_input_filter)
56+
{
57+
ZEND_INIT_MODULE_GLOBALS(my_input_filter, php_my_input_filter_init_globals, NULL);
58+
59+
REGISTER_LONG_CONSTANT("POST", PARSE_POST, CONST_CS | CONST_PERSISTENT);
60+
REGISTER_LONG_CONSTANT("GET", PARSE_GET, CONST_CS | CONST_PERSISTENT);
61+
REGISTER_LONG_CONSTANT("COOKIE", PARSE_COOKIE, CONST_CS | CONST_PERSISTENT);
62+
63+
sapi_register_input_filter(my_sapi_input_filter);
64+
return SUCCESS;
65+
}
66+
67+
PHP_RSHUTDOWN_FUNCTION(my_input_filter)
68+
{
69+
if(IF_G(get_array)) {
70+
zval_ptr_dtor(&IF_G(get_array));
71+
IF_G(get_array) = NULL;
72+
}
73+
if(IF_G(post_array)) {
74+
zval_ptr_dtor(&IF_G(post_array));
75+
IF_G(post_array) = NULL;
76+
}
77+
if(IF_G(cookie_array)) {
78+
zval_ptr_dtor(&IF_G(cookie_array));
79+
IF_G(cookie_array) = NULL;
80+
}
81+
return SUCCESS;
82+
}
83+
84+
PHP_MINFO_FUNCTION(my_input_filter)
85+
{
86+
php_info_print_table_start();
87+
php_info_print_table_row( 2, "My Input Filter Support", "enabled" );
88+
php_info_print_table_row( 2, "Revision", "$Revision$");
89+
php_info_print_table_end();
90+
}
91+
92+
unsigned int my_sapi_input_filter(int arg, char *var, char *val, unsigned int val_len)
93+
{
94+
zval new_var;
95+
zval *array_ptr = NULL;
96+
char *raw_var;
97+
int var_len;
98+
99+
assert(val != NULL);
100+
101+
switch(arg) {
102+
case PARSE_GET:
103+
if(!IF_G(get_array)) {
104+
ALLOC_ZVAL(array_ptr);
105+
array_init(array_ptr);
106+
INIT_PZVAL(array_ptr);
107+
}
108+
IF_G(get_array) = array_ptr;
109+
break;
110+
case PARSE_POST:
111+
if(!IF_G(post_array)) {
112+
ALLOC_ZVAL(array_ptr);
113+
array_init(array_ptr);
114+
INIT_PZVAL(array_ptr);
115+
}
116+
IF_G(post_array) = array_ptr;
117+
break;
118+
case PARSE_COOKIE:
119+
if(!IF_G(cookie_array)) {
120+
ALLOC_ZVAL(array_ptr);
121+
array_init(array_ptr);
122+
INIT_PZVAL(array_ptr);
123+
}
124+
IF_G(cookie_array) = array_ptr;
125+
break;
126+
}
127+
Z_STRLEN(new_var) = val_len;
128+
Z_STRVAL(new_var) = estrndup(val, val_len);
129+
Z_TYPE(new_var) = IS_STRING;
130+
131+
var_len = strlen(var);
132+
raw_var = emalloc(var_len+5); /* RAW_ and a \0 */
133+
strcpy(raw_var, "RAW_");
134+
strlcat(raw_var,var,var_len+5);
135+
136+
php_register_variable_ex(raw_var, &new_var, array_ptr TSRMLS_DC);
137+
138+
php_strip_tags(val, val_len, NULL, NULL, 0);
139+
140+
return strlen(val);
141+
}
142+
143+
PHP_FUNCTION(my_get_raw)
144+
{
145+
long arg;
146+
char *var;
147+
int var_len;
148+
zval **tmp;
149+
zval *array_ptr = NULL;
150+
HashTable *hash_ptr;
151+
char *raw_var;
152+
153+
if(zend_parse_parameters(2 TSRMLS_CC, "ls|l", &arg, &var, &var_len) == FAILURE) {
154+
return;
155+
}
156+
157+
switch(arg) {
158+
case PARSE_GET:
159+
array_ptr = IF_G(get_array);
160+
break;
161+
case PARSE_POST:
162+
array_ptr = IF_G(post_array);
163+
break;
164+
case PARSE_COOKIE:
165+
array_ptr = IF_G(post_array);
166+
break;
167+
}
168+
169+
if(!array_ptr) RETURN_FALSE;
170+
171+
/*
172+
* I'm changing the variable name here because when running with register_globals on,
173+
* the variable will end up in the global symbol table
174+
*/
175+
raw_var = emalloc(var_len+5); /* RAW_ and a \0 */
176+
strcpy(raw_var, "RAW_");
177+
strlcat(raw_var,var,var_len+5);
178+
hash_ptr = HASH_OF(array_ptr);
179+
180+
if(zend_hash_find(hash_ptr, raw_var, var_len+5, (void **)&tmp) == SUCCESS) {
181+
*return_value = **tmp;
182+
zval_copy_ctor(return_value);
183+
} else {
184+
RETVAL_FALSE;
185+
}
186+
efree(raw_var);
187+
}
188+

ext/mbstring/mb_gpc.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ MBSTRING_API SAPI_TREAT_DATA_FUNC(mbstr_treat_data)
169169
break;
170170
}
171171

172-
_php_mb_encoding_handler_ex(array_ptr, res, separator, 0, 0 TSRMLS_CC);
172+
_php_mb_encoding_handler_ex(arg, array_ptr, res, separator, 0, 0 TSRMLS_CC);
173173

174174
if (MBSTRG(http_input_identify) != mbfl_no_encoding_invalid) {
175175
switch(arg){
@@ -199,7 +199,7 @@ MBSTRING_API SAPI_TREAT_DATA_FUNC(mbstr_treat_data)
199199
/* }}} */
200200

201201
/* {{{ int _php_mb_encoding_handler_ex() */
202-
int _php_mb_encoding_handler_ex(zval *arg, char *res, char *separator, int force_register_globals, int report_errors TSRMLS_DC)
202+
int _php_mb_encoding_handler_ex(int data_type, zval *arg, char *res, char *separator, int force_register_globals, int report_errors TSRMLS_DC)
203203
{
204204
char *var, *val, *s1, *s2;
205205
char *strtok_buf = NULL, **val_list = NULL;
@@ -342,6 +342,7 @@ int _php_mb_encoding_handler_ex(zval *arg, char *res, char *separator, int force
342342
val_len = len_list[n];
343343
}
344344
n++;
345+
val_len = sapi_module.input_filter(data_type, var, val, val_len TSRMLS_CC);
345346
/* add variable to symbol table */
346347
php_register_variable_safe(var, val, val_len, array_ptr TSRMLS_CC);
347348
if (convd != NULL){

ext/mbstring/mbstring.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1281,7 +1281,7 @@ PHP_FUNCTION(mb_parse_str)
12811281

12821282
encstr = estrndup(encstr, encstr_len);
12831283

1284-
RETVAL_BOOL(_php_mb_encoding_handler_ex(track_vars_array, encstr, separator, (track_vars_array == NULL), 1 TSRMLS_CC));
1284+
RETVAL_BOOL(_php_mb_encoding_handler_ex(PARSE_STRING, track_vars_array, encstr, separator, (track_vars_array == NULL), 1 TSRMLS_CC));
12851285

12861286
if (encstr != NULL) efree(encstr);
12871287
if (separator != NULL) efree(separator);

main/SAPI.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,11 @@ SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zva
823823
return SUCCESS;
824824
}
825825

826+
SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char *val, unsigned int val_len TSRMLS_DC))
827+
{
828+
sapi_module.input_filter = input_filter;
829+
return SUCCESS;
830+
}
826831

827832
SAPI_API int sapi_flush(TSRMLS_D)
828833
{

main/SAPI.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ SAPI_API int sapi_register_post_entry(sapi_post_entry *post_entry);
178178
SAPI_API void sapi_unregister_post_entry(sapi_post_entry *post_entry);
179179
SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRMLS_D));
180180
SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC));
181+
SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char *val, unsigned int val_len TSRMLS_DC));
181182

182183
SAPI_API int sapi_flush(TSRMLS_D);
183184
SAPI_API struct stat *sapi_get_stat(TSRMLS_D);
@@ -238,6 +239,8 @@ struct _sapi_module_struct {
238239

239240
int (*get_target_uid)(uid_t * TSRMLS_DC);
240241
int (*get_target_gid)(gid_t * TSRMLS_DC);
242+
243+
unsigned int (*input_filter)(int arg, char *var, char *val, unsigned int val_len TSRMLS_DC);
241244
};
242245

243246

@@ -266,10 +269,12 @@ struct _sapi_post_entry {
266269
#define SAPI_POST_HANDLER_FUNC(post_handler) void post_handler(char *content_type_dup, void *arg TSRMLS_DC)
267270

268271
#define SAPI_TREAT_DATA_FUNC(treat_data) void treat_data(int arg, char *str, zval* destArray TSRMLS_DC)
272+
#define SAPI_INPUT_FILTER_FUNC(input_filter) unsigned int input_filter(int arg, char *var, char *val, unsigned int val_len TSRMLS_DC)
269273

270274
SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data);
271275
SAPI_API SAPI_POST_READER_FUNC(php_default_post_reader);
272276
SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data);
277+
SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter);
273278

274279
#define STANDARD_SAPI_MODULE_PROPERTIES
275280

main/php_content_types.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ int php_startup_sapi_content_types(void)
7777
sapi_register_post_entries(php_post_entries);
7878
sapi_register_default_post_reader(php_default_post_reader);
7979
sapi_register_treat_data(php_default_treat_data);
80+
sapi_register_input_filter(php_default_input_filter);
8081
return SUCCESS;
8182
}
8283
/* }}} */

main/php_variables.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,12 +226,19 @@ SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler)
226226
*val++ = '\0';
227227
php_url_decode(var, strlen(var));
228228
val_len = php_url_decode(val, strlen(val));
229+
val_len = sapi_module.input_filter(PARSE_POST, var, val, val_len TSRMLS_CC);
229230
php_register_variable_safe(var, val, val_len, array_ptr TSRMLS_CC);
230231
}
231232
var = php_strtok_r(NULL, "&", &strtok_buf);
232233
}
233234
}
234235

236+
SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter)
237+
{
238+
/* TODO: check .ini setting here and apply user-defined input filter */
239+
return val_len;
240+
}
241+
235242
SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data)
236243
{
237244
char *res = NULL, *var, *val, *separator=NULL;
@@ -314,6 +321,7 @@ SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data)
314321
*val++ = '\0';
315322
php_url_decode(var, strlen(var));
316323
val_len = php_url_decode(val, strlen(val));
324+
val_len = sapi_module.input_filter(arg, var, val, val_len TSRMLS_CC);
317325
php_register_variable_safe(var, val, val_len, array_ptr TSRMLS_CC);
318326
} else {
319327
php_url_decode(var, strlen(var));

main/rfc1867.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler)
805805
value = estrdup("");
806806
}
807807

808+
sapi_module.input_filter(PARSE_POST, param, value, strlen(value) TSRMLS_CC);
808809
safe_php_register_variable(param, value, array_ptr, 0 TSRMLS_CC);
809810
if (!strcmp(param, "MAX_FILE_SIZE")) {
810811
max_file_size = atol(value);

0 commit comments

Comments
 (0)