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/...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。