From: Rasmus Lerdorf Date: Wed, 19 Feb 2003 19:41:09 +0000 (+0000) Subject: Input Filter support. See README.input_filter for details. X-Git-Tag: RELEASE_0_5~864 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7429c2dc3f72ed9a6a41ccefc68595e76319cdae;p=php Input Filter support. See README.input_filter for details. @- Input Filter support added. See README.input_filter. (Rasmus) --- diff --git a/README.input_filter b/README.input_filter new file mode 100644 index 0000000000..011b167725 --- /dev/null +++ b/README.input_filter @@ -0,0 +1,188 @@ +Input Filter Support in PHP5 +---------------------------- + +XSS (Cross Site Scripting) hacks are becoming more and more prevalent, +and can be quite difficult to prevent. Whenever you accept user data +and somehow display this data back to users, you are likely vulnerable +to XSS hacks. + +The Input Filter support in PHP5 is aimed at providing the framework +through which a company-wide or site-wide security policy can be +enforced. It is implemented as a SAPI hook and is called from the +treat_data and post handler functions. To implement your own security +policy you will need to write a standard PHP extension. + +A simple implementation might look like the following. This stores the +original raw user data and adds a my_get_raw() function while the normal +$_POST, $_GET and $_COOKIE arrays are only populated with stripped +data. In this simple example all I am doing is calling strip_tags() on +the data. If register_globals is turned on, the default globals that +are created will be stripped ($foo) while a $RAW_foo is created with the +original user input. + +ZEND_BEGIN_MODULE_GLOBALS(my_input_filter) + zval *post_array; + zval *get_array; + zval *cookie_array; +ZEND_END_MODULE_GLOBALS(my_input_filter) + +#ifdef ZTS +#define IF_G(v) TSRMG(my_input_filter_globals_id, zend_my_input_filter_globals *, v) +#else +#define IF_G(v) (my_input_filter_globals.v) +#endif + +ZEND_DECLARE_MODULE_GLOBALS(my_input_filter) + +function_entry my_input_filter_functions[] = { + PHP_FE(my_get_raw, NULL) + {NULL, NULL, NULL} +}; + +zend_module_entry my_input_filter_module_entry = { + STANDARD_MODULE_HEADER, + "my_input_filter", + my_input_filter_functions, + PHP_MINIT(my_input_filter), + PHP_MSHUTDOWN(my_input_filter), + NULL, + PHP_RSHUTDOWN(my_input_filter), + PHP_MINFO(my_input_filter), + "0.1", + STANDARD_MODULE_PROPERTIES +}; + +PHP_MINIT_FUNCTION(my_input_filter) +{ + ZEND_INIT_MODULE_GLOBALS(my_input_filter, php_my_input_filter_init_globals, NULL); + + REGISTER_LONG_CONSTANT("POST", PARSE_POST, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("GET", PARSE_GET, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("COOKIE", PARSE_COOKIE, CONST_CS | CONST_PERSISTENT); + + sapi_register_input_filter(my_sapi_input_filter); + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(my_input_filter) +{ + if(IF_G(get_array)) { + zval_ptr_dtor(&IF_G(get_array)); + IF_G(get_array) = NULL; + } + if(IF_G(post_array)) { + zval_ptr_dtor(&IF_G(post_array)); + IF_G(post_array) = NULL; + } + if(IF_G(cookie_array)) { + zval_ptr_dtor(&IF_G(cookie_array)); + IF_G(cookie_array) = NULL; + } + return SUCCESS; +} + +PHP_MINFO_FUNCTION(my_input_filter) +{ + php_info_print_table_start(); + php_info_print_table_row( 2, "My Input Filter Support", "enabled" ); + php_info_print_table_row( 2, "Revision", "$Revision$"); + php_info_print_table_end(); +} + +unsigned int my_sapi_input_filter(int arg, char *var, char *val, unsigned int val_len) +{ + zval new_var; + zval *array_ptr = NULL; + char *raw_var; + int var_len; + + assert(val != NULL); + + switch(arg) { + case PARSE_GET: + if(!IF_G(get_array)) { + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + } + IF_G(get_array) = array_ptr; + break; + case PARSE_POST: + if(!IF_G(post_array)) { + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + } + IF_G(post_array) = array_ptr; + break; + case PARSE_COOKIE: + if(!IF_G(cookie_array)) { + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + } + IF_G(cookie_array) = array_ptr; + break; + } + Z_STRLEN(new_var) = val_len; + Z_STRVAL(new_var) = estrndup(val, val_len); + Z_TYPE(new_var) = IS_STRING; + + var_len = strlen(var); + raw_var = emalloc(var_len+5); /* RAW_ and a \0 */ + strcpy(raw_var, "RAW_"); + strlcat(raw_var,var,var_len+5); + + php_register_variable_ex(raw_var, &new_var, array_ptr TSRMLS_DC); + + php_strip_tags(val, val_len, NULL, NULL, 0); + + return strlen(val); +} + +PHP_FUNCTION(my_get_raw) +{ + long arg; + char *var; + int var_len; + zval **tmp; + zval *array_ptr = NULL; + HashTable *hash_ptr; + char *raw_var; + + if(zend_parse_parameters(2 TSRMLS_CC, "ls|l", &arg, &var, &var_len) == FAILURE) { + return; + } + + switch(arg) { + case PARSE_GET: + array_ptr = IF_G(get_array); + break; + case PARSE_POST: + array_ptr = IF_G(post_array); + break; + case PARSE_COOKIE: + array_ptr = IF_G(post_array); + break; + } + + if(!array_ptr) RETURN_FALSE; + + /* + * I'm changing the variable name here because when running with register_globals on, + * the variable will end up in the global symbol table + */ + raw_var = emalloc(var_len+5); /* RAW_ and a \0 */ + strcpy(raw_var, "RAW_"); + strlcat(raw_var,var,var_len+5); + hash_ptr = HASH_OF(array_ptr); + + if(zend_hash_find(hash_ptr, raw_var, var_len+5, (void **)&tmp) == SUCCESS) { + *return_value = **tmp; + zval_copy_ctor(return_value); + } else { + RETVAL_FALSE; + } + efree(raw_var); +} + diff --git a/ext/mbstring/mb_gpc.c b/ext/mbstring/mb_gpc.c index 028612baa6..61e6dd93d9 100644 --- a/ext/mbstring/mb_gpc.c +++ b/ext/mbstring/mb_gpc.c @@ -169,7 +169,7 @@ MBSTRING_API SAPI_TREAT_DATA_FUNC(mbstr_treat_data) break; } - _php_mb_encoding_handler_ex(array_ptr, res, separator, 0, 0 TSRMLS_CC); + _php_mb_encoding_handler_ex(arg, array_ptr, res, separator, 0, 0 TSRMLS_CC); if (MBSTRG(http_input_identify) != mbfl_no_encoding_invalid) { switch(arg){ @@ -199,7 +199,7 @@ MBSTRING_API SAPI_TREAT_DATA_FUNC(mbstr_treat_data) /* }}} */ /* {{{ int _php_mb_encoding_handler_ex() */ -int _php_mb_encoding_handler_ex(zval *arg, char *res, char *separator, int force_register_globals, int report_errors TSRMLS_DC) +int _php_mb_encoding_handler_ex(int data_type, zval *arg, char *res, char *separator, int force_register_globals, int report_errors TSRMLS_DC) { char *var, *val, *s1, *s2; 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 val_len = len_list[n]; } n++; + val_len = sapi_module.input_filter(data_type, var, val, val_len TSRMLS_CC); /* add variable to symbol table */ php_register_variable_safe(var, val, val_len, array_ptr TSRMLS_CC); if (convd != NULL){ diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c index 3f9558efc7..c228cf29b6 100644 --- a/ext/mbstring/mbstring.c +++ b/ext/mbstring/mbstring.c @@ -1281,7 +1281,7 @@ PHP_FUNCTION(mb_parse_str) encstr = estrndup(encstr, encstr_len); - RETVAL_BOOL(_php_mb_encoding_handler_ex(track_vars_array, encstr, separator, (track_vars_array == NULL), 1 TSRMLS_CC)); + RETVAL_BOOL(_php_mb_encoding_handler_ex(PARSE_STRING, track_vars_array, encstr, separator, (track_vars_array == NULL), 1 TSRMLS_CC)); if (encstr != NULL) efree(encstr); if (separator != NULL) efree(separator); diff --git a/main/SAPI.c b/main/SAPI.c index cbe23ba575..117d868fc8 100644 --- a/main/SAPI.c +++ b/main/SAPI.c @@ -823,6 +823,11 @@ SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zva return SUCCESS; } +SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char *val, unsigned int val_len TSRMLS_DC)) +{ + sapi_module.input_filter = input_filter; + return SUCCESS; +} SAPI_API int sapi_flush(TSRMLS_D) { diff --git a/main/SAPI.h b/main/SAPI.h index 4ecc77a8b2..1bc31b26cb 100644 --- a/main/SAPI.h +++ b/main/SAPI.h @@ -178,6 +178,7 @@ SAPI_API int sapi_register_post_entry(sapi_post_entry *post_entry); SAPI_API void sapi_unregister_post_entry(sapi_post_entry *post_entry); SAPI_API int sapi_register_default_post_reader(void (*default_post_reader)(TSRMLS_D)); SAPI_API int sapi_register_treat_data(void (*treat_data)(int arg, char *str, zval *destArray TSRMLS_DC)); +SAPI_API int sapi_register_input_filter(unsigned int (*input_filter)(int arg, char *var, char *val, unsigned int val_len TSRMLS_DC)); SAPI_API int sapi_flush(TSRMLS_D); SAPI_API struct stat *sapi_get_stat(TSRMLS_D); @@ -238,6 +239,8 @@ struct _sapi_module_struct { int (*get_target_uid)(uid_t * TSRMLS_DC); int (*get_target_gid)(gid_t * TSRMLS_DC); + + unsigned int (*input_filter)(int arg, char *var, char *val, unsigned int val_len TSRMLS_DC); }; @@ -266,10 +269,12 @@ struct _sapi_post_entry { #define SAPI_POST_HANDLER_FUNC(post_handler) void post_handler(char *content_type_dup, void *arg TSRMLS_DC) #define SAPI_TREAT_DATA_FUNC(treat_data) void treat_data(int arg, char *str, zval* destArray TSRMLS_DC) +#define SAPI_INPUT_FILTER_FUNC(input_filter) unsigned int input_filter(int arg, char *var, char *val, unsigned int val_len TSRMLS_DC) SAPI_API SAPI_POST_READER_FUNC(sapi_read_standard_form_data); SAPI_API SAPI_POST_READER_FUNC(php_default_post_reader); SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data); +SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter); #define STANDARD_SAPI_MODULE_PROPERTIES diff --git a/main/php_content_types.c b/main/php_content_types.c index 2db3ccd7ae..53c241cbe3 100644 --- a/main/php_content_types.c +++ b/main/php_content_types.c @@ -77,6 +77,7 @@ int php_startup_sapi_content_types(void) sapi_register_post_entries(php_post_entries); sapi_register_default_post_reader(php_default_post_reader); sapi_register_treat_data(php_default_treat_data); + sapi_register_input_filter(php_default_input_filter); return SUCCESS; } /* }}} */ diff --git a/main/php_variables.c b/main/php_variables.c index 233e6368db..a82c966ef8 100644 --- a/main/php_variables.c +++ b/main/php_variables.c @@ -226,12 +226,19 @@ SAPI_API SAPI_POST_HANDLER_FUNC(php_std_post_handler) *val++ = '\0'; php_url_decode(var, strlen(var)); val_len = php_url_decode(val, strlen(val)); + val_len = sapi_module.input_filter(PARSE_POST, var, val, val_len TSRMLS_CC); php_register_variable_safe(var, val, val_len, array_ptr TSRMLS_CC); } var = php_strtok_r(NULL, "&", &strtok_buf); } } +SAPI_API SAPI_INPUT_FILTER_FUNC(php_default_input_filter) +{ + /* TODO: check .ini setting here and apply user-defined input filter */ + return val_len; +} + SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data) { char *res = NULL, *var, *val, *separator=NULL; @@ -314,6 +321,7 @@ SAPI_API SAPI_TREAT_DATA_FUNC(php_default_treat_data) *val++ = '\0'; php_url_decode(var, strlen(var)); val_len = php_url_decode(val, strlen(val)); + val_len = sapi_module.input_filter(arg, var, val, val_len TSRMLS_CC); php_register_variable_safe(var, val, val_len, array_ptr TSRMLS_CC); } else { php_url_decode(var, strlen(var)); diff --git a/main/rfc1867.c b/main/rfc1867.c index 3c9b6d56e2..eea22ce6a8 100644 --- a/main/rfc1867.c +++ b/main/rfc1867.c @@ -805,6 +805,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) value = estrdup(""); } + sapi_module.input_filter(PARSE_POST, param, value, strlen(value) TSRMLS_CC); safe_php_register_variable(param, value, array_ptr, 0 TSRMLS_CC); if (!strcmp(param, "MAX_FILE_SIZE")) { max_file_size = atol(value);