xref: /PHP-5.3/README.input_filter (revision f7b10aba)
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.  If register_globals is turned on, the default globals that
23are created will be stripped ($foo) while a $RAW_foo is created with the
24original user input.
25
26ZEND_BEGIN_MODULE_GLOBALS(my_input_filter)
27        zval *post_array;
28        zval *get_array;
29        zval *cookie_array;
30ZEND_END_MODULE_GLOBALS(my_input_filter)
31
32#ifdef ZTS
33#define IF_G(v) TSRMG(my_input_filter_globals_id, zend_my_input_filter_globals *, v)
34#else
35#define IF_G(v) (my_input_filter_globals.v)
36#endif
37
38ZEND_DECLARE_MODULE_GLOBALS(my_input_filter)
39
40zend_function_entry my_input_filter_functions[] = {
41    PHP_FE(my_get_raw,   NULL)
42    {NULL, NULL, NULL}
43};
44
45zend_module_entry my_input_filter_module_entry = {
46    STANDARD_MODULE_HEADER,
47    "my_input_filter",
48    my_input_filter_functions,
49    PHP_MINIT(my_input_filter),
50    PHP_MSHUTDOWN(my_input_filter),
51    NULL,
52    PHP_RSHUTDOWN(my_input_filter),
53    PHP_MINFO(my_input_filter),
54    "0.1",
55    STANDARD_MODULE_PROPERTIES
56};
57
58PHP_MINIT_FUNCTION(my_input_filter)
59{
60    ZEND_INIT_MODULE_GLOBALS(my_input_filter, php_my_input_filter_init_globals, NULL);
61
62    REGISTER_LONG_CONSTANT("POST", PARSE_POST, CONST_CS | CONST_PERSISTENT);
63    REGISTER_LONG_CONSTANT("GET", PARSE_GET, CONST_CS | CONST_PERSISTENT);
64    REGISTER_LONG_CONSTANT("COOKIE", PARSE_COOKIE, CONST_CS | CONST_PERSISTENT);
65
66    sapi_register_input_filter(my_sapi_input_filter);
67    return SUCCESS;
68}
69
70PHP_RSHUTDOWN_FUNCTION(my_input_filter)
71{
72    if(IF_G(get_array)) {
73        zval_ptr_dtor(&IF_G(get_array));
74        IF_G(get_array) = NULL;
75    }
76    if(IF_G(post_array)) {
77        zval_ptr_dtor(&IF_G(post_array));
78        IF_G(post_array) = NULL;
79    }
80    if(IF_G(cookie_array)) {
81        zval_ptr_dtor(&IF_G(cookie_array));
82        IF_G(cookie_array) = NULL;
83    }
84    return SUCCESS;
85}
86
87PHP_MINFO_FUNCTION(my_input_filter)
88{
89    php_info_print_table_start();
90    php_info_print_table_row( 2, "My Input Filter Support", "enabled" );
91    php_info_print_table_row( 2, "Revision", "$Id: 488ca82a7b15672b2186e25e65f9efa13ff650a0 $");
92    php_info_print_table_end();
93}
94
95/* The filter handler. If you return 1 from it, then PHP also registers the
96 * (modified) variable. Returning 0 prevents PHP from registering the variable;
97 * you can use this if your filter already registers the variable under a
98 * different name, or if you just don't want the variable registered at all. */
99SAPI_INPUT_FILTER_FUNC(my_sapi_input_filter)
100{
101    zval new_var;
102    zval *array_ptr = NULL;
103    char *raw_var;
104    int var_len;
105
106    assert(*val != NULL);
107
108    switch(arg) {
109        case PARSE_GET:
110            if(!IF_G(get_array)) {
111                ALLOC_ZVAL(array_ptr);
112                array_init(array_ptr);
113                INIT_PZVAL(array_ptr);
114            }
115            IF_G(get_array) = array_ptr;
116            break;
117        case PARSE_POST:
118            if(!IF_G(post_array)) {
119                ALLOC_ZVAL(array_ptr);
120                array_init(array_ptr);
121                INIT_PZVAL(array_ptr);
122            }
123            IF_G(post_array) = array_ptr;
124            break;
125        case PARSE_COOKIE:
126            if(!IF_G(cookie_array)) {
127                ALLOC_ZVAL(array_ptr);
128                array_init(array_ptr);
129                INIT_PZVAL(array_ptr);
130            }
131            IF_G(cookie_array) = array_ptr;
132            break;
133    }
134    Z_STRLEN(new_var) = val_len;
135    Z_STRVAL(new_var) = estrndup(*val, val_len);
136    Z_TYPE(new_var) = IS_STRING;
137
138    var_len = strlen(var);
139    raw_var = emalloc(var_len+5);  /* RAW_ and a \0 */
140    strcpy(raw_var, "RAW_");
141    strlcat(raw_var,var,var_len+5);
142
143    php_register_variable_ex(raw_var, &new_var, array_ptr TSRMLS_DC);
144
145    php_strip_tags(*val, val_len, NULL, NULL, 0);
146
147    *new_val_len = strlen(*val);
148    return 1;
149}
150
151PHP_FUNCTION(my_get_raw)
152{
153    long arg;
154    char *var;
155    int var_len;
156    zval **tmp;
157    zval *array_ptr = NULL;
158    HashTable *hash_ptr;
159    char *raw_var;
160
161    if(zend_parse_parameters(2 TSRMLS_CC, "ls", &arg, &var, &var_len) == FAILURE) {
162        return;
163    }
164
165    switch(arg) {
166        case PARSE_GET:
167            array_ptr = IF_G(get_array);
168            break;
169        case PARSE_POST:
170            array_ptr = IF_G(post_array);
171            break;
172        case PARSE_COOKIE:
173            array_ptr = IF_G(post_array);
174            break;
175    }
176
177    if(!array_ptr) RETURN_FALSE;
178
179    /*
180     * I'm changing the variable name here because when running with register_globals on,
181     * the variable will end up in the global symbol table
182     */
183    raw_var = emalloc(var_len+5);  /* RAW_ and a \0 */
184    strcpy(raw_var, "RAW_");
185    strlcat(raw_var,var,var_len+5);
186    hash_ptr = HASH_OF(array_ptr);
187
188    if(zend_hash_find(hash_ptr, raw_var, var_len+5, (void **)&tmp) == SUCCESS) {
189        *return_value = **tmp;
190        zval_copy_ctor(return_value);
191    } else {
192        RETVAL_FALSE;
193    }
194    efree(raw_var);
195}
196
197