1# Input filter support in PHP 2 3XSS (Cross Site Scripting) hacks are becoming more and more prevalent, and can 4be quite difficult to prevent. Whenever you accept user data and somehow display 5this data back to users, you are likely vulnerable to XSS hacks. 6 7The Input Filter support in PHP is aimed at providing the framework through 8which a company-wide or site-wide security policy can be enforced. It is 9implemented as a SAPI hook and is called from the `treat_data` and post handler 10functions. To implement your own security policy you will need to write a 11standard PHP extension. There is also a powerful standard implementation in 12`ext/filter` that should suit most peoples' needs. However, if you want to 13implement your own security policy, read on. 14 15A simple implementation might look like the following. This stores the original 16raw user data and adds a `my_get_raw()` function while the normal `$_POST`, 17`$_GET` and `$_COOKIE` arrays are only populated with stripped data. In this 18simple example all I am doing is calling `strip_tags()` on the data. 19 20```c 21ZEND_BEGIN_MODULE_GLOBALS(my_input_filter) 22 zval *post_array; 23 zval *get_array; 24 zval *cookie_array; 25ZEND_END_MODULE_GLOBALS(my_input_filter) 26 27#ifdef ZTS 28#define IF_G(v) TSRMG(my_input_filter_globals_id, zend_my_input_filter_globals *, v) 29#else 30#define IF_G(v) (my_input_filter_globals.v) 31#endif 32 33ZEND_DECLARE_MODULE_GLOBALS(my_input_filter) 34 35zend_function_entry my_input_filter_functions[] = { 36 PHP_FE(my_get_raw, NULL) 37 {NULL, NULL, NULL} 38}; 39 40zend_module_entry my_input_filter_module_entry = { 41 STANDARD_MODULE_HEADER, 42 "my_input_filter", 43 my_input_filter_functions, 44 PHP_MINIT(my_input_filter), 45 PHP_MSHUTDOWN(my_input_filter), 46 NULL, 47 PHP_RSHUTDOWN(my_input_filter), 48 PHP_MINFO(my_input_filter), 49 "0.1", 50 STANDARD_MODULE_PROPERTIES 51}; 52 53PHP_MINIT_FUNCTION(my_input_filter) 54{ 55 ZEND_INIT_MODULE_GLOBALS(my_input_filter, php_my_input_filter_init_globals, NULL); 56 57 REGISTER_LONG_CONSTANT("POST", PARSE_POST, CONST_CS | CONST_PERSISTENT); 58 REGISTER_LONG_CONSTANT("GET", PARSE_GET, CONST_CS | CONST_PERSISTENT); 59 REGISTER_LONG_CONSTANT("COOKIE", PARSE_COOKIE, CONST_CS | CONST_PERSISTENT); 60 61 sapi_register_input_filter(my_sapi_input_filter); 62 return SUCCESS; 63} 64 65PHP_RSHUTDOWN_FUNCTION(my_input_filter) 66{ 67 if(IF_G(get_array)) { 68 zval_ptr_dtor(&IF_G(get_array)); 69 IF_G(get_array) = NULL; 70 } 71 if(IF_G(post_array)) { 72 zval_ptr_dtor(&IF_G(post_array)); 73 IF_G(post_array) = NULL; 74 } 75 if(IF_G(cookie_array)) { 76 zval_ptr_dtor(&IF_G(cookie_array)); 77 IF_G(cookie_array) = NULL; 78 } 79 return SUCCESS; 80} 81 82PHP_MINFO_FUNCTION(my_input_filter) 83{ 84 php_info_print_table_start(); 85 php_info_print_table_row( 2, "My Input Filter Support", "enabled" ); 86 php_info_print_table_end(); 87} 88 89/* The filter handler. If you return 1 from it, then PHP also registers the 90 * (modified) variable. Returning 0 prevents PHP from registering the variable; 91 * you can use this if your filter already registers the variable under a 92 * different name, or if you just don't want the variable registered at all. */ 93SAPI_INPUT_FILTER_FUNC(my_sapi_input_filter) 94{ 95 zval new_var; 96 zval *array_ptr = NULL; 97 char *raw_var; 98 int var_len; 99 100 assert(*val != NULL); 101 102 switch(arg) { 103 case PARSE_GET: 104 if(!IF_G(get_array)) { 105 ALLOC_ZVAL(array_ptr); 106 array_init(array_ptr); 107 INIT_PZVAL(array_ptr); 108 } 109 IF_G(get_array) = array_ptr; 110 break; 111 case PARSE_POST: 112 if(!IF_G(post_array)) { 113 ALLOC_ZVAL(array_ptr); 114 array_init(array_ptr); 115 INIT_PZVAL(array_ptr); 116 } 117 IF_G(post_array) = array_ptr; 118 break; 119 case PARSE_COOKIE: 120 if(!IF_G(cookie_array)) { 121 ALLOC_ZVAL(array_ptr); 122 array_init(array_ptr); 123 INIT_PZVAL(array_ptr); 124 } 125 IF_G(cookie_array) = array_ptr; 126 break; 127 } 128 Z_STRLEN(new_var) = val_len; 129 Z_STRVAL(new_var) = estrndup(*val, val_len); 130 Z_TYPE(new_var) = IS_STRING; 131 132 var_len = strlen(var); 133 raw_var = emalloc(var_len+5); /* RAW_ and a \0 */ 134 strcpy(raw_var, "RAW_"); 135 strlcat(raw_var,var,var_len+5); 136 137 php_register_variable_ex(raw_var, &new_var, array_ptr); 138 139 php_strip_tags(*val, val_len, NULL, NULL, 0); 140 141 *new_val_len = strlen(*val); 142 return 1; 143} 144 145PHP_FUNCTION(my_get_raw) 146{ 147 long arg; 148 char *var; 149 int var_len; 150 zval **tmp; 151 zval *array_ptr = NULL; 152 153 if(zend_parse_parameters(2, "ls", &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) { 170 RETURN_FALSE; 171 } 172 173 if(zend_hash_find(HASH_OF(array_ptr), var, var_len+5, (void **)&tmp) == SUCCESS) { 174 *return_value = **tmp; 175 zval_copy_ctor(return_value); 176 } else { 177 RETVAL_FALSE; 178 } 179} 180``` 181