xref: /PHP-7.3/README.input_filter (revision 3362620b)
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