You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
5.3 KiB

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