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