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_row( 2, "Revision", "$Id: e2941d029ee4e89f8880c46d41a7e8fc60a7fbc3 $"); 90 php_info_print_table_end(); 91} 92 93/* The filter handler. If you return 1 from it, then PHP also registers the 94 * (modified) variable. Returning 0 prevents PHP from registering the variable; 95 * you can use this if your filter already registers the variable under a 96 * different name, or if you just don't want the variable registered at all. */ 97SAPI_INPUT_FILTER_FUNC(my_sapi_input_filter) 98{ 99 zval new_var; 100 zval *array_ptr = NULL; 101 char *raw_var; 102 int var_len; 103 104 assert(*val != NULL); 105 106 switch(arg) { 107 case PARSE_GET: 108 if(!IF_G(get_array)) { 109 ALLOC_ZVAL(array_ptr); 110 array_init(array_ptr); 111 INIT_PZVAL(array_ptr); 112 } 113 IF_G(get_array) = array_ptr; 114 break; 115 case PARSE_POST: 116 if(!IF_G(post_array)) { 117 ALLOC_ZVAL(array_ptr); 118 array_init(array_ptr); 119 INIT_PZVAL(array_ptr); 120 } 121 IF_G(post_array) = array_ptr; 122 break; 123 case PARSE_COOKIE: 124 if(!IF_G(cookie_array)) { 125 ALLOC_ZVAL(array_ptr); 126 array_init(array_ptr); 127 INIT_PZVAL(array_ptr); 128 } 129 IF_G(cookie_array) = array_ptr; 130 break; 131 } 132 Z_STRLEN(new_var) = val_len; 133 Z_STRVAL(new_var) = estrndup(*val, val_len); 134 Z_TYPE(new_var) = IS_STRING; 135 136 var_len = strlen(var); 137 raw_var = emalloc(var_len+5); /* RAW_ and a \0 */ 138 strcpy(raw_var, "RAW_"); 139 strlcat(raw_var,var,var_len+5); 140 141 php_register_variable_ex(raw_var, &new_var, array_ptr TSRMLS_DC); 142 143 php_strip_tags(*val, val_len, NULL, NULL, 0); 144 145 *new_val_len = strlen(*val); 146 return 1; 147} 148 149PHP_FUNCTION(my_get_raw) 150{ 151 long arg; 152 char *var; 153 int var_len; 154 zval **tmp; 155 zval *array_ptr = NULL; 156 157 if(zend_parse_parameters(2 TSRMLS_CC, "ls", &arg, &var, &var_len) == FAILURE) { 158 return; 159 } 160 161 switch(arg) { 162 case PARSE_GET: 163 array_ptr = IF_G(get_array); 164 break; 165 case PARSE_POST: 166 array_ptr = IF_G(post_array); 167 break; 168 case PARSE_COOKIE: 169 array_ptr = IF_G(post_array); 170 break; 171 } 172 173 if(!array_ptr) { 174 RETURN_FALSE; 175 } 176 177 if(zend_hash_find(HASH_OF(array_ptr), var, var_len+5, (void **)&tmp) == SUCCESS) { 178 *return_value = **tmp; 179 zval_copy_ctor(return_value); 180 } else { 181 RETVAL_FALSE; 182 } 183} 184 185