资讯专栏INFORMATION COLUMN

【PHP预定义变量】$_GET,$_POST,$_REQUEST生成

Aceyclee / 728人阅读

摘要:的预定义变量和,这些变量的生成过程。主要是由于之前看到一篇文章通过构造冲突实现各种语言的拒绝服务攻击。其中看似是相当于注册声明对应的预定义变量名,而才是真正的将值写入到和变量中的操作。提交最大变量数限制,中做限制,参考资料

PHP的预定义变量:$_SERVER,$_POST,$_GET,$_COOKIE,$_ENV,$_FILES和$_REQUEST,这些变量的生成过程。
主要是由于之前看到一篇文章通过构造Hash冲突实现各种语言的拒绝服务攻击。
看完之后思考这些变量是什么时候生成的,是由web服务器生成的还是PHP生成的?

猜想:

客户端将请求发送到web服务器,web服务器在收到请求后,将请求携带的参数写入到缓冲区stdin,然后php写入预定义变量的时候,会从stdin中取出这些参数然后装入到对应的预定义变量中$_GET,$_POST,$_REQUEST中

自己跟踪代码看整个php的流程

1>执行main/mian.c中的php_module_startup函数
2>执行php_startup_auto_globals函数,该函数在php_variables.c中定义的

//php_variable.c
void php_startup_auto_globals(void)
{
    zend_register_auto_global(zend_string_init("_GET", sizeof("_GET")-1, 1), 0, php_auto_globals_create_get);
    zend_register_auto_global(zend_string_init("_POST", sizeof("_POST")-1, 1), 0, php_auto_globals_create_post);
    zend_register_auto_global(zend_string_init("_COOKIE", sizeof("_COOKIE")-1, 1), 0, php_auto_globals_create_cookie);
    zend_register_auto_global(zend_string_init("_SERVER", sizeof("_SERVER")-1, 1), PG(auto_globals_jit), php_auto_globals_create_server);
    zend_register_auto_global(zend_string_init("_ENV", sizeof("_ENV")-1, 1), PG(auto_globals_jit), php_auto_globals_create_env);
    zend_register_auto_global(zend_string_init("_REQUEST", sizeof("_REQUEST")-1, 1), PG(auto_globals_jit), php_auto_globals_create_request);
    zend_register_auto_global(zend_string_init("_FILES", sizeof("_FILES")-1, 1), 0, php_auto_globals_create_files);
}

//zend_compile.c,将各个预定义变量写入
int zend_register_auto_global(zend_string *name, zend_bool jit, zend_auto_global_callback auto_global_callback){
    zend_auto_global auto_global;
    int retval;
    auto_global.name = zend_new_interned_string(name);
    auto_global.auto_global_callback = auto_global_callback;
    auto_global.jit = jit;

    retval = zend_hash_add_mem(CG(auto_globals), auto_global.name, &auto_global, sizeof(zend_auto_global)) != NULL ? SUCCESS : FAILURE;

    zend_string_release(name);
    return retval;
}

//zend_compile.c,将各个变量的key-value写入到hashtable中
static zend_always_inline void *zend_hash_add_mem(HashTable *ht, zend_string *key, void *pData, size_t size){
    zval tmp, *zv;

    ZVAL_PTR(&tmp, NULL);
    if ((zv = zend_hash_add(ht, key, &tmp))) {    //这一步的$_REQUEST可能被攻击
        Z_PTR_P(zv) = pemalloc(size, ht->u.flags & HASH_FLAG_PERSISTENT);
        memcpy(Z_PTR_P(zv), pData, size);
        return Z_PTR_P(zv);
    }
    return NULL;
}
laruence的博客:

要知道PHP是怎么处理的,首先我们要了解,$_GET, $_POST, $_COOKIE等变量的构造过程。
每个请求到来以后,apache处理到response阶段的时候, 会将控制权交给PHP模块, PHP模块会在处理请求之前首先间接调用 php_request_startup函数,在php_request_startup中:

#ifndef APACHE_HOOKS
int php_request_startup(void)
{
    int retval = SUCCESS;

#ifdef HAVE_DTRACE
    DTRACE_REQUEST_STARTUP(SAFE_FILENAME(SG(request_info).path_translated), SAFE_FILENAME(SG(request_info).request_uri), (char *)SAFE_FILENAME(SG(request_info).request_method));
#endif /* HAVE_DTRACE */

#ifdef PHP_WIN32
# if defined(ZTS)
    _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
# endif
    PG(com_initialized) = 0;
#endif

#if PHP_SIGCHILD
    signal(SIGCHLD, sigchld_handler);
#endif

    zend_try {
        PG(in_error_log) = 0;
        PG(during_request_startup) = 1;

        php_output_activate();

        /* initialize global variables */
        PG(modules_activated) = 0;
        PG(header_is_being_sent) = 0;
        PG(connection_status) = PHP_CONNECTION_NORMAL;
        PG(in_user_include) = 0;

        zend_activate();
        sapi_activate();

#ifdef ZEND_SIGNALS
        zend_signal_activate();
#endif

        if (PG(max_input_time) == -1) {
            zend_set_timeout(EG(timeout_seconds), 1);
        } else {
            zend_set_timeout(PG(max_input_time), 1);
        }

        /* Disable realpath cache if an open_basedir is set */
        if (PG(open_basedir) && *PG(open_basedir)) {
            CWDG(realpath_cache_size_limit) = 0;
        }

        if (PG(expose_php)) {
            sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1);
        }

        if (PG(output_handler) && PG(output_handler)[0]) {
            zval oh;

            ZVAL_STRING(&oh, PG(output_handler));
            php_output_start_user(&oh, 0, PHP_OUTPUT_HANDLER_STDFLAGS);
            zval_ptr_dtor(&oh);
        } else if (PG(output_buffering)) {
            php_output_start_user(NULL, PG(output_buffering) > 1 ? PG(output_buffering) : 0, PHP_OUTPUT_HANDLER_STDFLAGS);
        } else if (PG(implicit_flush)) {
            php_output_set_implicit_flush(1);
        }

        /* We turn this off in php_execute_script() */
        /* PG(during_request_startup) = 0; */

        php_hash_environment();
        zend_activate_modules();
        PG(modules_activated)=1;
    } zend_catch {
        retval = FAILURE;
    } zend_end_try();

    SG(sapi_started) = 1;

    return retval;
}

其中的zend_variables.c文件中php_hash_environment函数:

PHPAPI int php_hash_environment(void)
{
    memset(PG(http_globals), 0, sizeof(PG(http_globals)));
    zend_activate_auto_globals();
    if (PG(register_argc_argv)) {
        php_build_argv(SG(request_info).query_string, &PG(http_globals)[TRACK_VARS_SERVER]);
    }
    return SUCCESS;
}

//回调zend_variables.c中的zend_activate_auto_globals函数将请求的value写入到对应的$_POST和$_GET预定义变量中
ZEND_API void zend_activate_auto_globals(void) /* {{{ */
{
    zend_auto_global *auto_global;

    ZEND_HASH_FOREACH_PTR(CG(auto_globals), auto_global) {
        if (auto_global->jit) {
            auto_global->armed = 1;
        } else if (auto_global->auto_global_callback) {
            //这里会回调php_auto_globals_create_post,php_auto_globals_create_get,php_auto_globals_create_request函数处理对应的变量
            auto_global->armed = auto_global->auto_global_callback(auto_global->name);
        } else {
            auto_global->armed = 0;
        }
    } ZEND_HASH_FOREACH_END();
}

拿其中一个$_REQUEST数据来看,php_variables.c文件中php_auto_globals_create_request函数对应代码:

static zend_bool php_auto_globals_create_post(zend_string *name)
{
    if (PG(variables_order) &&
            (strchr(PG(variables_order),"P") || strchr(PG(variables_order),"p")) &&
        !SG(headers_sent) &&
        SG(request_info).request_method &&
        !strcasecmp(SG(request_info).request_method, "POST")) {
        //写入数据
        sapi_module.treat_data(PARSE_POST, NULL, NULL);
    } else {
        zval_ptr_dtor(&PG(http_globals)[TRACK_VARS_POST]);
        array_init(&PG(http_globals)[TRACK_VARS_POST]);
    }

    zend_hash_update(&EG(symbol_table), name, &PG(http_globals)[TRACK_VARS_POST]);
    Z_ADDREF(PG(http_globals)[TRACK_VARS_POST]);

    return 0; /* don"t rearm */
}

可以看到,离成功不远了,sapi_module.treat_data 也就是php_default_treat_data,
在php_default_treat_data中,对于变量,都调用php_register_variable_safe来注册变量,
而php_register_variable_safe最终会调用php_register_variable_ex:

zend_variables.c文件中zend_register_auto_global和zend_activate_auto_globals之间的关系,应该有先后顺序的问题。
其中zend_register_auto_global看似是相当于注册声明对应的预定义变量名,而zend_activate_auto_globals才是真正的将值写入到$_GET和$_POST变量中的操作。
提交最大变量数限制,php_varialbles.c中add_post_vars做限制,SAPI_POST_HANDLER_FUNC(php_std_post_handler)

参考资料:
http://www.laruence.com/2008/...
http://www.laruence.com/2008/...
http://www.php-internals.com/...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/23225.html

相关文章

  • php超全局变量,魔术常量,魔术方法

    摘要:注意和是不同的变量,处理它们的方式不同变量说明默认情况下包含了,和的数组。包含相同的信息,但它不是一个超全局变量。这些特殊的常量不区分大小写,如下几个的魔术常量名称说明文件中的当前行号。 整理了下关于php的基础知识,参考了些资料,如下: 超全局变量 超全局变量 — 超全局变量是在全部作用域中始终可用的内置变量: $GLOBALS $GLOBALS — 引用全局作用域中可用的全部变量 ...

    mj 评论0 收藏0
  • PHP7源码分析】PHP中$_POST揭秘

    摘要:和进程的启动过程类似,启动过程有种进程角色启动进程进程和进程。直到请求到来,将连接赋值给对象的字段。注当进程执行完后会再次调用函数,准备监听新的请求。当读取到的时,会调用函数对进行解析,将中的以及存储到结构体中。 运营研发团队 季伟滨 一、前言 前几天的工作中,需要通过curl做一次接口测试。让我意外的是,通过$_POST竟然无法获取到Content-Type是application...

    sf190404 评论0 收藏0
  • PHP 手册阅读笔记 - 语言参考篇

    摘要:最近计划把手册,认真的先过一遍。语言参考类型新认知强制转换类型用。后期静态绑定从这里开始语言参考生成器新认知生成器汗水的核心是关键字。语言参考预定义变量超全局变量前一个错误信息原始数据以上 showImg(https://segmentfault.com/img/remote/1460000010147451); 最近计划把 PHP手册,认真的先过一遍。记录一些以前不知道,不明确的知识...

    Developer 评论0 收藏0
  • CSRF攻击是什么并且如何防止

    摘要:,意为跨网站请求伪造,也有写为。攻击者伪造目标用户的请求,然后此请求发送到有漏洞的网站,网站执行此请求后,引发跨站请求伪造攻击。 CSRF(Cross Site Request Forgeries),意为跨网站请求伪造,也有写为XSRF。攻击者伪造目标用户的HTTP请求,然后此请求发送到有CSRF漏洞的网站,网站执行此请 求后,引发跨站请求伪造攻击。攻击者利用隐蔽的HTTP连接,让目标...

    Flands 评论0 收藏0
  • Swoole完美支持ThinkPHP5

    摘要:这里创建的对象可以在进程生命周期内使用目的加载框架中的内容定义应用目录加载基础文件把接收的信息转换为可识别的对于超全局数组不会释放函数输出打印 Swoole完美支持ThinkPHP5 1、首先要开启http的server 可以在thinkphp的目录下创建一个server目录,里面创建一个HTTPServer的php 2、需要在WorkerStart回调事件做两件事 定义应用目录:d...

    XiNGRZ 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<