xref: /PHP-5.5/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.
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