xref: /PHP-5.5/README.input_filter (revision f7b10aba)
13bf293ffSAndi GutmansInput Filter Support in PHP 5
23bf293ffSAndi Gutmans-----------------------------
37429c2dcSRasmus Lerdorf
47429c2dcSRasmus LerdorfXSS (Cross Site Scripting) hacks are becoming more and more prevalent,
57429c2dcSRasmus Lerdorfand can be quite difficult to prevent.  Whenever you accept user data
67429c2dcSRasmus Lerdorfand somehow display this data back to users, you are likely vulnerable
77429c2dcSRasmus Lerdorfto XSS hacks.
87429c2dcSRasmus Lerdorf
93bf293ffSAndi GutmansThe Input Filter support in PHP 5 is aimed at providing the framework
107429c2dcSRasmus Lerdorfthrough which a company-wide or site-wide security policy can be
117429c2dcSRasmus Lerdorfenforced.  It is implemented as a SAPI hook and is called from the
127429c2dcSRasmus Lerdorftreat_data and post handler functions.  To implement your own security
13228bb9e7SRasmus Lerdorfpolicy you will need to write a standard PHP extension.  There is also
14228bb9e7SRasmus Lerdorfa powerful standard implementation in ext/filter that should suit most
15228bb9e7SRasmus Lerdorfpeoples' needs.  However, if you want to implement your own security
16228bb9e7SRasmus Lerdorfpolicy, read on.
177429c2dcSRasmus Lerdorf
187429c2dcSRasmus LerdorfA simple implementation might look like the following.  This stores the
197429c2dcSRasmus Lerdorforiginal raw user data and adds a my_get_raw() function while the normal
207429c2dcSRasmus Lerdorf$_POST, $_GET and $_COOKIE arrays are only populated with stripped
217429c2dcSRasmus Lerdorfdata.  In this simple example all I am doing is calling strip_tags() on
227429c2dcSRasmus Lerdorfthe data.
237429c2dcSRasmus Lerdorf
247429c2dcSRasmus LerdorfZEND_BEGIN_MODULE_GLOBALS(my_input_filter)
257429c2dcSRasmus Lerdorf        zval *post_array;
267429c2dcSRasmus Lerdorf        zval *get_array;
277429c2dcSRasmus Lerdorf        zval *cookie_array;
287429c2dcSRasmus LerdorfZEND_END_MODULE_GLOBALS(my_input_filter)
297429c2dcSRasmus Lerdorf
307429c2dcSRasmus Lerdorf#ifdef ZTS
317429c2dcSRasmus Lerdorf#define IF_G(v) TSRMG(my_input_filter_globals_id, zend_my_input_filter_globals *, v)
327429c2dcSRasmus Lerdorf#else
337429c2dcSRasmus Lerdorf#define IF_G(v) (my_input_filter_globals.v)
347429c2dcSRasmus Lerdorf#endif
357429c2dcSRasmus Lerdorf
367429c2dcSRasmus LerdorfZEND_DECLARE_MODULE_GLOBALS(my_input_filter)
377429c2dcSRasmus Lerdorf
387429c2dcSRasmus Lerdorfzend_function_entry my_input_filter_functions[] = {
397429c2dcSRasmus Lerdorf    PHP_FE(my_get_raw,   NULL)
40684d68bbSfoobar    {NULL, NULL, NULL}
417429c2dcSRasmus Lerdorf};
427429c2dcSRasmus Lerdorf
437429c2dcSRasmus Lerdorfzend_module_entry my_input_filter_module_entry = {
447429c2dcSRasmus Lerdorf    STANDARD_MODULE_HEADER,
457429c2dcSRasmus Lerdorf    "my_input_filter",
467429c2dcSRasmus Lerdorf    my_input_filter_functions,
477429c2dcSRasmus Lerdorf    PHP_MINIT(my_input_filter),
487429c2dcSRasmus Lerdorf    PHP_MSHUTDOWN(my_input_filter),
497429c2dcSRasmus Lerdorf    NULL,
507429c2dcSRasmus Lerdorf    PHP_RSHUTDOWN(my_input_filter),
517429c2dcSRasmus Lerdorf    PHP_MINFO(my_input_filter),
527429c2dcSRasmus Lerdorf    "0.1",
537429c2dcSRasmus Lerdorf    STANDARD_MODULE_PROPERTIES
547429c2dcSRasmus Lerdorf};
557429c2dcSRasmus Lerdorf
567429c2dcSRasmus LerdorfPHP_MINIT_FUNCTION(my_input_filter)
577429c2dcSRasmus Lerdorf{
587429c2dcSRasmus Lerdorf    ZEND_INIT_MODULE_GLOBALS(my_input_filter, php_my_input_filter_init_globals, NULL);
597429c2dcSRasmus Lerdorf
607429c2dcSRasmus Lerdorf    REGISTER_LONG_CONSTANT("POST", PARSE_POST, CONST_CS | CONST_PERSISTENT);
617429c2dcSRasmus Lerdorf    REGISTER_LONG_CONSTANT("GET", PARSE_GET, CONST_CS | CONST_PERSISTENT);
627429c2dcSRasmus Lerdorf    REGISTER_LONG_CONSTANT("COOKIE", PARSE_COOKIE, CONST_CS | CONST_PERSISTENT);
637429c2dcSRasmus Lerdorf
647429c2dcSRasmus Lerdorf    sapi_register_input_filter(my_sapi_input_filter);
657429c2dcSRasmus Lerdorf    return SUCCESS;
667429c2dcSRasmus Lerdorf}
677429c2dcSRasmus Lerdorf
687429c2dcSRasmus LerdorfPHP_RSHUTDOWN_FUNCTION(my_input_filter)
697429c2dcSRasmus Lerdorf{
707429c2dcSRasmus Lerdorf    if(IF_G(get_array)) {
717429c2dcSRasmus Lerdorf        zval_ptr_dtor(&IF_G(get_array));
727429c2dcSRasmus Lerdorf        IF_G(get_array) = NULL;
737429c2dcSRasmus Lerdorf    }
747429c2dcSRasmus Lerdorf    if(IF_G(post_array)) {
757429c2dcSRasmus Lerdorf        zval_ptr_dtor(&IF_G(post_array));
767429c2dcSRasmus Lerdorf        IF_G(post_array) = NULL;
777429c2dcSRasmus Lerdorf    }
787429c2dcSRasmus Lerdorf    if(IF_G(cookie_array)) {
797429c2dcSRasmus Lerdorf        zval_ptr_dtor(&IF_G(cookie_array));
807429c2dcSRasmus Lerdorf        IF_G(cookie_array) = NULL;
817429c2dcSRasmus Lerdorf    }
827429c2dcSRasmus Lerdorf    return SUCCESS;
837429c2dcSRasmus Lerdorf}
847429c2dcSRasmus Lerdorf
857429c2dcSRasmus LerdorfPHP_MINFO_FUNCTION(my_input_filter)
867429c2dcSRasmus Lerdorf{
877429c2dcSRasmus Lerdorf    php_info_print_table_start();
887429c2dcSRasmus Lerdorf    php_info_print_table_row( 2, "My Input Filter Support", "enabled" );
897429c2dcSRasmus Lerdorf    php_info_print_table_row( 2, "Revision", "$Id: e2941d029ee4e89f8880c46d41a7e8fc60a7fbc3 $");
907429c2dcSRasmus Lerdorf    php_info_print_table_end();
91*f7b10abaSDavid Soria Parra}
927429c2dcSRasmus Lerdorf
937429c2dcSRasmus Lerdorf/* The filter handler. If you return 1 from it, then PHP also registers the
947429c2dcSRasmus Lerdorf * (modified) variable. Returning 0 prevents PHP from registering the variable;
95350d755eSDerick Rethans * you can use this if your filter already registers the variable under a
96350d755eSDerick Rethans * different name, or if you just don't want the variable registered at all. */
97350d755eSDerick RethansSAPI_INPUT_FILTER_FUNC(my_sapi_input_filter)
98350d755eSDerick Rethans{
99c73641afSDerick Rethans    zval new_var;
1007429c2dcSRasmus Lerdorf    zval *array_ptr = NULL;
1017429c2dcSRasmus Lerdorf    char *raw_var;
1027429c2dcSRasmus Lerdorf    int var_len;
1037429c2dcSRasmus Lerdorf
1047429c2dcSRasmus Lerdorf    assert(*val != NULL);
1057429c2dcSRasmus Lerdorf
106d08a0e99SRasmus Lerdorf    switch(arg) {
1077429c2dcSRasmus Lerdorf        case PARSE_GET:
1087429c2dcSRasmus Lerdorf            if(!IF_G(get_array)) {
1097429c2dcSRasmus Lerdorf                ALLOC_ZVAL(array_ptr);
1107429c2dcSRasmus Lerdorf                array_init(array_ptr);
1117429c2dcSRasmus Lerdorf                INIT_PZVAL(array_ptr);
1127429c2dcSRasmus Lerdorf            }
1137429c2dcSRasmus Lerdorf            IF_G(get_array) = array_ptr;
1147429c2dcSRasmus Lerdorf            break;
1157429c2dcSRasmus Lerdorf        case PARSE_POST:
1167429c2dcSRasmus Lerdorf            if(!IF_G(post_array)) {
1177429c2dcSRasmus Lerdorf                ALLOC_ZVAL(array_ptr);
1187429c2dcSRasmus Lerdorf                array_init(array_ptr);
1197429c2dcSRasmus Lerdorf                INIT_PZVAL(array_ptr);
1207429c2dcSRasmus Lerdorf            }
1217429c2dcSRasmus Lerdorf            IF_G(post_array) = array_ptr;
1227429c2dcSRasmus Lerdorf            break;
1237429c2dcSRasmus Lerdorf        case PARSE_COOKIE:
1247429c2dcSRasmus Lerdorf            if(!IF_G(cookie_array)) {
1257429c2dcSRasmus Lerdorf                ALLOC_ZVAL(array_ptr);
1267429c2dcSRasmus Lerdorf                array_init(array_ptr);
1277429c2dcSRasmus Lerdorf                INIT_PZVAL(array_ptr);
1287429c2dcSRasmus Lerdorf            }
1297429c2dcSRasmus Lerdorf            IF_G(cookie_array) = array_ptr;
1307429c2dcSRasmus Lerdorf            break;
1317429c2dcSRasmus Lerdorf    }
1327429c2dcSRasmus Lerdorf    Z_STRLEN(new_var) = val_len;
1337429c2dcSRasmus Lerdorf    Z_STRVAL(new_var) = estrndup(*val, val_len);
1347429c2dcSRasmus Lerdorf    Z_TYPE(new_var) = IS_STRING;
135d08a0e99SRasmus Lerdorf
1367429c2dcSRasmus Lerdorf    var_len = strlen(var);
1377429c2dcSRasmus Lerdorf    raw_var = emalloc(var_len+5);  /* RAW_ and a \0 */
1387429c2dcSRasmus Lerdorf    strcpy(raw_var, "RAW_");
1397429c2dcSRasmus Lerdorf    strlcat(raw_var,var,var_len+5);
1407429c2dcSRasmus Lerdorf
1417429c2dcSRasmus Lerdorf    php_register_variable_ex(raw_var, &new_var, array_ptr TSRMLS_DC);
1427429c2dcSRasmus Lerdorf
1437429c2dcSRasmus Lerdorf    php_strip_tags(*val, val_len, NULL, NULL, 0);
1447429c2dcSRasmus Lerdorf
145d08a0e99SRasmus Lerdorf    *new_val_len = strlen(*val);
1467429c2dcSRasmus Lerdorf    return 1;
147750b0338SDerick Rethans}
148350d755eSDerick Rethans
1497429c2dcSRasmus LerdorfPHP_FUNCTION(my_get_raw)
1507429c2dcSRasmus Lerdorf{
1517429c2dcSRasmus Lerdorf    long arg;
1527429c2dcSRasmus Lerdorf    char *var;
1537429c2dcSRasmus Lerdorf    int var_len;
1547429c2dcSRasmus Lerdorf    zval **tmp;
1557429c2dcSRasmus Lerdorf    zval *array_ptr = NULL;
1567429c2dcSRasmus Lerdorf
1577429c2dcSRasmus Lerdorf    if(zend_parse_parameters(2 TSRMLS_CC, "ls", &arg, &var, &var_len) == FAILURE) {
1587429c2dcSRasmus Lerdorf        return;
1597429c2dcSRasmus Lerdorf    }
1607429c2dcSRasmus Lerdorf
161476c9a3bSStefan Esser    switch(arg) {
1627429c2dcSRasmus Lerdorf        case PARSE_GET:
1637429c2dcSRasmus Lerdorf            array_ptr = IF_G(get_array);
1647429c2dcSRasmus Lerdorf            break;
1657429c2dcSRasmus Lerdorf        case PARSE_POST:
1667429c2dcSRasmus Lerdorf            array_ptr = IF_G(post_array);
1677429c2dcSRasmus Lerdorf            break;
1687429c2dcSRasmus Lerdorf        case PARSE_COOKIE:
1697429c2dcSRasmus Lerdorf            array_ptr = IF_G(post_array);
1707429c2dcSRasmus Lerdorf            break;
1717429c2dcSRasmus Lerdorf    }
1727429c2dcSRasmus Lerdorf
1737429c2dcSRasmus Lerdorf    if(!array_ptr) {
1747429c2dcSRasmus Lerdorf        RETURN_FALSE;
1757429c2dcSRasmus Lerdorf    }
1767429c2dcSRasmus Lerdorf
1777429c2dcSRasmus Lerdorf    if(zend_hash_find(HASH_OF(array_ptr), var, var_len+5, (void **)&tmp) == SUCCESS) {
1787429c2dcSRasmus Lerdorf        *return_value = **tmp;
1797429c2dcSRasmus Lerdorf        zval_copy_ctor(return_value);
1807429c2dcSRasmus Lerdorf    } else {
1817429c2dcSRasmus Lerdorf        RETVAL_FALSE;
1827429c2dcSRasmus Lerdorf    }
1837429c2dcSRasmus Lerdorf}
1847429c2dcSRasmus Lerdorf
1857429c2dcSRasmus Lerdorf