【Nginx】图片不完整,竟然是Nginx的锅!!
2022-12-31 00:3:2 Author: 利刃信安(查看原文) 阅读量:13 收藏


前言

最近,安全运维过程中遇到一个奇葩问题,通过浏览器访问某静态图片,只能访问大约八分之一,其余的部分无法完全加载出来。最初我们以为是某信Waf拦截导致图片无法下载,为了解决问题,增加某0 WAf路线,结果,某信异常,某0正常,问题越来越复杂,经过一系列排查(过程省略,直奔主题),最终定位是Nginx的问题。

问题定位

1.首先尝试关闭某信Waf然后查看图片情况,经确认,图片依然异常,排除某信Waf规则拦截导致图片不完整。

image-20220313172125833

2.抓包分析,发现Transfer-Encoding: chunked字段,猜测服务器给客户端返回数据过程中采用分块传输。

image-20220313172546623

3.根据分块传输原理,搜索到一些资料,增大缓冲可以解决问题。

客户端(PC浏览器或者手机浏览器)在接受到Nginx代理响应的时候,头信息通常都会带上Content-Length,一般情况下客户端会在接受完Content-Length长度的数据之后才会开始解析。而在Nginx代理上,页面处理过程中会将数据都放在缓存中,然后一次性的返回给客户端。

另外一种情况就是头信息中不存在Content-Length ,取而代之的是Tansfer-Encoding:chunked ,这个头信息的的意思是response的内容会被Nginx代理分成一块一块的发送,客户端也就不需要等到内容都传输完毕了才解析其中的内容。因为这个时候被传送的数据长度是无法预计的,所以存在Tansfer-Encoding:chunked的话也没有存在Content-Length 的意义了。

Nginx代理服务器测试后配置进行如下修改后可以完整传输

# 代理互联网地址
location /acore {
client_max_body_size    150m;
client_body_buffer_size 128k;
proxy_connect_timeout   130;
proxy_send_timeout      300;
proxy_read_timeout      1800;

# 默认开启
# proxy_buffering on;

# 以下配置以proxy_buffers配置为中心适配的缓冲大小策略,因为nginx对缓存值相互制约,
# 从proxy_buffers设置思路比较清晰,当然也可以针对不同情况选择不同的配置项为主作为开端突破口。
# 以下配置在包资源1.7m并小于proxy_buffers 2M的话是可以正常运行的
# 可以设置很小 该指令设置缓冲区大小,从代理后端服务器取得的第一部分的响应内容,会放到这里.小的响应header通常位于这部分响应内容里边.
# 默认来说,该缓冲区大小等于指令 proxy_buffers所设置的一个块区大小;可以把它设置得更小。
# 但是最大值不能超过 proxy_buffers 减一个缓冲区的大小 3X500k (1500k)
# proxy_buffer_size  128k;

# 该指令设置缓冲区的数量(最少2)和大小,从被代理的后端服务器取得的响应内容,会放置到这里. 默认情况下,一个缓冲区的大小等于内存页面大小,可能是4K也可能是8K,这取决于平台、
#数据响应包不能大于proxy_buffers总值(在不支持分块传输条件下)如果超过需要增大proxy_buffers
proxy_buffers 4 512k;

# 默认值:proxy_buffer_size * 2; (官方文档是这样的,但是如果我proxy_buffer_size不按照推荐来设置 ,proxy_buffer_size * 2没有满足此项的最小值是不能使用的)
# 此选项最小值要大于或者等于 128k(proxy_buffer_size 128k)和512k(proxy_buffers的一个区512k)的最大值 512k
# 此选项最大值小于等于proxy_buffers 减一个缓冲区的大小 3X512K(1536k)
# proxy_busy_buffers_size 512k;

# 同时如果response的内容很大的话,Nginx会接收并把他们写入到temp_file里去。大小由proxy_max_temp_file_size控制。
# 如果busy的buffer传输完了会从temp_file里面接着读数据,直到传输完毕。
# proxy_temp_file_write_size 4m;

# proxy_redirect http:// https://;
proxy_pass http://xxx.xxx.xxx/core/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-Port $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header x-agent $http_user_agent;
proxy_set_header X-Forwarded-scheme $scheme;
}

分析后真正起到作用的是

proxy_buffers 4 512k;

只要缓冲总大小4*512k=2048k大于图片大小,图片就可以完整下载。

一波三折

问题看似解决,实际上其他问题又来了,问题就是之前的缓冲大小不足的时候,某信通道异常,某0通道却是正常的,这又是怎么回事?

我们已经确定问题出在某信通道的Nginx代理上面,查看Nginx代理报错信息,信息如下:

2022/03/13 17:06:10 [crit] 92638#0: *26924337 open() "/usr/local/nginx/proxy_temp/9/40/0000005409" failed (13: Permission denied) while reading upstream, client: 0.0.0.0, server: 8.8.8.8, request: "GET /acore/download.shtml?p=image&f=pdf/img/190618new.jpg HTTP/1.1", upstream: "http://1.1.1.1:80/core//download.shtml?p=image&f=pdf/img/190618new.jpg", host: "8.8.8.8"

直接在后台服务器上用后台服务器的IP地址去访问,发现速度相当快,于是怀疑是Nginx的配置问题。

注意:当下载大的附件,或是页面中有大图片时,就会下载中断或是图片无法显示,也许你会说我用的Nginx缺省的配置也从来没有碰到过这种问题呀!我想说的是:那是因为你的网站没有大文件,至少没有大到使用Nginx的默认配置加载不出来。

诚如之前所说,问题就出在proxy_buffers上,当服务器上的文件超过该参数设置的大小时,Nginx会先将文件写入临时目录(缺省为Nginx安装目下/proxy_temp目录),缺省Nginx是以nobody身份启动的,用ls -al 命令查看proxy_temp目录,nobody是proxy_temp目录的所有者,接下来查看proxy_temp的父目录即Nginx安装目录,发现nobody没权限。

问题解决

可以使用两种方式解决这个问题,如下所示。

设置任何人都可以写proxy_temp目录,重启Nginx即可解决。直接更改proxy_buffers的值,将其修改为大于图片和文件的大小,重启Nginx。

如果是以第一种方式解决问题的话,比如proxy_temp目录是/usr/local/nginx/proxy_temp,用如下命令将/usr/local/nginx/proxy_temp目录设置为任何人都可以写,问题解决。

chmod -R 777 /usr/local/nginx/proxy_temp/

如果问题还存在,那就用如下命令将/usr/local/nginx/目录设置为任何人都可以执行,问题解决。

chmod -R 755 /usr/local/nginx/

如果是使用第二种方式解决问题的话,就可以直接修改nginx.conf文件,如下所示。

# 代理互联网地址
location /acore {
client_max_body_size    150m;
client_body_buffer_size 128k;
proxy_connect_timeout   130;
proxy_send_timeout      300;
proxy_read_timeout      1800;

# 默认开启
# proxy_buffering on;

# 以下配置以proxy_buffers配置为中心适配的缓冲大小策略,因为nginx对缓存值相互制约,
# 从proxy_buffers设置思路比较清晰,当然也可以针对不同情况选择不同的配置项为主作为开端突破口。
# 以下配置在包资源1.7m并小于proxy_buffers 2M的话是可以正常运行的
# 可以设置很小 该指令设置缓冲区大小,从代理后端服务器取得的第一部分的响应内容,会放到这里.小的响应header通常位于这部分响应内容里边.
# 默认来说,该缓冲区大小等于指令 proxy_buffers所设置的一个块区大小;可以把它设置得更小。
# 但是最大值不能超过 proxy_buffers 减一个缓冲区的大小 3X500k (1500k)
# proxy_buffer_size  128k;

# 该指令设置缓冲区的数量(最少2)和大小,从被代理的后端服务器取得的响应内容,会放置到这里. 默认情况下,一个缓冲区的大小等于内存页面大小,可能是4K也可能是8K,这取决于平台、
#数据响应包不能大于proxy_buffers总值(在不支持分块传输条件下)如果超过需要增大proxy_buffers
proxy_buffers 4 512k;

# 默认值:proxy_buffer_size * 2; (官方文档是这样的,但是如果我proxy_buffer_size不按照推荐来设置 ,proxy_buffer_size * 2没有满足此项的最小值是不能使用的)
# 此选项最小值要大于或者等于 128k(proxy_buffer_size 128k)和512k(proxy_buffers的一个区512k)的最大值 512k
# 此选项最大值小于等于proxy_buffers 减一个缓冲区的大小 3X512K(1536k)
# proxy_busy_buffers_size 512k;

# 同时如果response的内容很大的话,Nginx会接收并把他们写入到temp_file里去。大小由proxy_max_temp_file_size控制。
# 如果busy的buffer传输完了会从temp_file里面接着读数据,直到传输完毕。
# proxy_temp_file_write_size 4m;

# proxy_redirect http:// https://;
proxy_pass http://xxx.xxx.xxx/core/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-Port $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header x-agent $http_user_agent;
proxy_set_header X-Forwarded-scheme $scheme;
}

写在最后

问题原因已经很明确了,图片传输过程中,采用分块传输,缓冲区大小不足,导致文件下载不完整。由于某信通道和某0通道代理服务器配置存在差异,导致某信异常,某0正常,配置修改后,给定/usr/local/nginx/执行权限后,两个通道均正常。

配置差异如下:



文章来源: http://mp.weixin.qq.com/s?__biz=MzU1Mjk3MDY1OA==&mid=2247499689&idx=4&sn=cf401ba60a31a1dfb5db7dd4d0359b47&chksm=fbfb4f64cc8cc672df024558e51e0c04df2e4f8bd4410608c69dfa99729e46d4b043b46d17a9#rd
如有侵权请联系:admin#unsafe.sh