|
8 P H% ?9 e: O4 I H
基础配置经常会有针对某个版本的nginx安全漏洞出现,隐藏nginx版本号就成了主要的安全优化手段之一,当然最重要的是及时升级修复漏洞http {% r+ \1 {' S8 u" z3 \
server_tokensoff; N- e1 n( F) M" G- Y
}Header头设置。 : q0 g0 y a! M
在通常的请求响应中,浏览器会根据Content-Type来分辨响应的类型,但当响应类型未指定或错误指定时,浏览会尝试启用MIME-sniffing来猜测资源的响应类型,这是非常危险的X-Frame-Options。
; E. i# O& }0 z+ ^ G8 r6 G7 U6 k :响应头表示是否允许浏览器加载frame等属性,有三个配置DENY禁止任何网页被嵌入,SAMEORIGIN只允许本网站的嵌套,ALLOW-FROM允许指定地址的嵌套X-XSS-Protection:表示启用XSS过滤(禁用过滤为X-XSS-Protection: 0),mode=block表示若检查到XSS攻击则停止渲染页面
# Q8 N. U7 b' `3 |1 j4 Y+ y! u X-Content-Type-Options:响应头用来指定浏览器对未指定或错误指定Content-Type资源真正类型的猜测行为,nosniff 表示不允许任何猜测例如一个.jpg的图片文件被恶意嵌入了可执行的js代码,在开启资源类型猜测的情况下,浏览器将执行嵌入的js代码,可能会有意想不到的后果
- n) w! H1 Q! W; b9 C& z5 c$ z: z add_header X-Frame-Options SAMEORIGIN;% X" W, U; s) k7 ]# A0 C" F
# 禁止嗅探文件类型add_header X-Content-Type-Options nosniff;add_header
" W( B: j# \ ^" N6 S* u X-XSS-Protection "1; mode=block"; 开启HSTS统一https访问add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" 3 q7 G# u+ A8 Z
;账号认证server {
4 p" ~2 x0 c8 {% n! o location / {) |/ V. M! ^9 i# P/ H" n
auth_basic"please input user&passwd";
# C/ r T4 c/ u0 n* _+ K auth_basic_user_file # o& v# p9 b4 c% S9 X; b; }
key/auth.key;
5 `( Q" p: d# F. q' Z. ~ }
! t7 w. [- M" M. `. J' e }限制请求方法$request_method能够获取到请求nginx的method这里配置只允许GET/POST方法访问,其他的method返回405if ($request_method # ~4 H& H% t: {2 B
!~ ^(GET|POST)$ ) {
: x9 }- n5 A7 A& c1 D& L/ f9 {6 B8 G return405;7 J; n }' U* T0 E* x `, g
}拒绝指定User-Agent可能有一些不法者会利用wget/curl等工具扫描我们的网站,我们可以通过禁止相应的user-agent
@; [5 X3 R5 J8 h" ` 来简单的防范Nginx的444状态比较特殊,如果返回444那么客户端将不会收到服务端返回的信息,就像是网站无法连接一样#禁止Scrapy等工具的抓取if ($http_user_agent~* (Scrapy|Curl|HttpClient))
4 D4 g$ q3 i3 e% A( B {
% Y/ {3 o7 k( `6 H return444;7 p3 d2 Q+ `6 h5 X: @
}
( v# |+ T, A& S #禁止指定UA及UA为空的访问if ($http_user_agent~ "WinHttp|WebZIP|FetchURL|node-superagent|java/|FeedDemon|Jullo|JikeSpider|Indy 6 d- E0 B9 {- ~! g1 F- L5 J5 u
Library|Alexa Toolbar|AskTbFXTV|AhrefsBot|CrawlDaddy|Java|Feedly|Apache-HttpAsyncClient|UniversalFeedParser|ApacheBench|Microsoft URL Control|Swiftbot|ZmEu|oBot|jaunty|Python-urllib|lightDeckReports Bot|YYSpider|DigExt|HttpClient|MJ12bot|heritrix|EasouSpider|Ezooms|BOT/
2 H' i" ^# A4 ~1 a9 H6 d 0.1|YandexBot|FlightDeckReports|Linguee Bot|^$" ) {
2 Y0 r+ |- f, ?5 [% P return 444;
w3 w p! b) G }图片防盗链location /images/ {
0 f7 r$ p4 j& M& [, O
4 l$ h. ]. y- c+ s N1 M valid_referersnoneblocked www.host.com;
% o* e% X, ~ {2 A9 O5 _ if ($invalid_referer) {
4 V* N5 y# m4 h' Z/ w return403;
( B$ Q' x3 \. ^' O }2 j7 n7 D8 R% P" ^4 c" u
}valid_referers S1 _ E4 _0 t! j0 [; h& j
: 验证referer,其中none允许referer为空,blocked允许不带协议的请求,除了以上两类外仅允许referer为 www.host.com 时访问images下的图片资源,否则返回403
& {7 X- c7 X& U7 A, k& l 当然你也可以给不符合referer规则的请求重定向到一个默认的图片,比如下边这样location /images/ {* R8 o3 j- S9 K9 t
valid_referersblocked www.host.com9 i( @) S$ _* B$ {+ O( z5 K3 h
if (
) d" K9 R& i! s, n- j $invalid_referer) {
( V7 x0 k: L8 t1 j! s# L rewrite ^/images/.*.(gif|jpg|jpeg|png)$ /static/qrcode.jpg last;
: A1 P& j1 W+ s9 @, B2 y }
4 @4 L5 T( [% c3 N; c" d }限制并发及请求数
+ F( D# ?" I5 A) u4 | 网站安全配置(Nginx)防止网站被CC攻击(包括使用了CDN加速之后的配置方法)Nginx 有 2 个模块用于控制访问“数量”和“速度”简单的说,控制你最多同时有 多少个访问,并且控制你每秒钟最多访问多少次, 你的同时并发访问不能太多,也不能太快,不然就“杀无赦”。
1 `' j* s6 S4 v5 a' n- N; _8 s1 Z HttpLimitZoneModule 限制同时并发访问的数量HttpLimitReqModule 限制访问数据,每秒内最多几个请求现在我们面对的最直接的问题就是, 经过这么多层加速,我怎么得到“最前面普通用户的 IP 地址”呢? 0 v. z9 O4 O/ t, ], x; }
当一个 CDN 或者透明代理服务器把用户的请求转到后面服务器的时候,这个 CDN 服务器会在 Http 的头中加入 一个记录X-Forwarded-For : 用户IP, 代理服务器IPX-Forwarded-For : 用户IP, 代理服务器1-IP, 代理服务器2-IP, 代理服务器3-IP, …. ) n" a \" w' v$ q
可以看到经过好多层代理之后, 用户的真实IP 在第一个位置, 后面会跟一串 中间代理服务器的IP地址,从这里取到用户真实的IP地址,针对这个 IP 地址做限制就可以了,标准配置Nginx 里面设置一个限制,配置如下: + a7 p% \4 x! B6 o) l
## 用户的 IP 地址 $binary_remote_addr 作为 Key,每个 IP 地址最多有 50 个并发连接## 你想开 几千个连接 刷死我? 超过 50 个连接,直接返回 503 错误给你,根本不处理你的请求了
9 U+ G" V* d) e# J$ P limit_conn_zone$binary_remote_addr zone=TotalConnLimitZone:10m ;6 N# `0 F6 W* ~
limit_conn TotalConnLimitZone 50;
2 ~2 ]8 S& k2 @1 W/ ^
& s( u" J7 z/ a' \# P7 o! {5 c2 S limit_conn_log_levelnotice;
( g6 L- h9 \/ T# D ## 用户的 IP 地址 $binary_remote_addr 作为 Key,每个 IP 地址每秒处理 10 个请求## 你想用程序每秒几百次的刷我,没戏,再快了就不处理了,直接返回 503 错误给你 / T( [% T# b% S) G, ]; P
limit_req_zone$binary_remote_addr zone=ConnLimitZone:10m rate=10r/s;- X& L' Q" ~& c1 b3 w( v
limit_req_log_levelnotice;
: k9 ^2 S( m; l1 v% b7 z$ r! d ## 具体服务器配置
3 ?$ U! O' b) m0 z server {$ {3 n0 I9 q5 K# `/ `
listen80;
+ D+ x+ @6 Z% l5 a4 U: c+ \3 J location~ \.php$ {
) Z! h# r' C1 U0 a _) ]% A, }1 f # 最多 5 个排队, 由于每秒处理 10 个请求 + 5个排队,你一秒最多发送 15 个请求过来,再多就直接返回 503 错误给你了 ! ]) K5 w/ v9 f( t" B5 z
limit_req zone=ConnLimitZone burst=5 nodelay;
$ B: S6 I' [+ V, G' h3 r% | fastcgi_pass127.0.0.1:9000;: g4 y' @0 Q7 D5 H/ E7 v! H
fastcgi_index
: \3 s) b: |) h: e: p index.php;
! Z4 H% D! a$ [" D4 R+ u# G8 R include fastcgi_params;' V7 u p% l4 e
}& C/ `1 ^9 W* Z! G1 G
}这样一个最简单的服务器安全限制访问就完成了,这个基本上你 Google 一搜索能搜索到 90% 的网站都是这个例子,而且还强调用“$binary_remote_addr”可以节省内存之类的废话 4 X% G, ?5 G: q$ l5 @
经过多层CDN之后取得原始用户的IP地址Nginx 配置取得用户的原始地址map $http_x_forwarded_for $clientRealIp {
/ e, z* n; f g5 s. c J ## 没有通过代理,直接用
: l1 T1 |* o. X E7 d; T ' T+ ?) Z& x, g9 m8 U- w
remote_addr "" $remote_addr;$ i; [: E& i t/ }* R, C0 R
## 用正则匹配,从 x_forwarded_for 中取得用户的原始IP ## 例如 X-Forwarded-For: 202.123.123.11, 208.22.22.234, 192.168.2.100,... + k9 n# Z! `1 y0 Z
## 这里第一个 202.123.123.11 是用户的真实 IP,后面其它都是经过的 CDN 服务器! ~6 i8 ]% j- J( Y' `& e( M
~^(?P[0-9\.]+),?.*$ $firstAddr;# K5 J/ M7 F$ s) l, ^* [
}9 W2 j, K3 s, b# h1 ?
4 `+ ~5 w+ }/ _% W8 ]& z' Q/ a2 K
## 通过 map 指令,我们为 nginx 创建了一个变量 $clientRealIp ,这个就是 原始用户的真实 IP 地址,## 不论用户是直接访问,还是通过一串 CDN 之后的访问,我们都能取得正确的原始IP地址 3 z3 r' K( a5 V |" B
根据用户的真实 IP 做连接限制下面是修改之后的 Nginx 配置:CDN环境下 Nginx 的安全配置Shell## 这里取得原始用户的IP地址map$http_x_forwarded_for $clientRealIp { ; A5 j0 ^, {/ h9 l9 z# N
""$remote_addr; ~^(?P[0-9\.]+),?.*$ $firstAddr;}## 针对原始用户 IP 地址做限制limit_conn_zone$clientRealIp zone=TotalConnLimitZone:20m ;
$ q* {, J7 ], W+ d1 v* ~ limit_connTotalConnLimitZone 50;limit_conn_log_levelnotice;## 针对原始用户 IP 地址做限制limit_req_zone$clientRealIp zone=ConnLimitZone:20m rate=10r/s; 3 a, k# i; a4 M0 ~$ [+ k1 q
#limit_req zone=ConnLimitZone burst=10 nodelay;limit_req_log_levelnotice;## 具体服务器配置server{ listen80;
# b' A _9 D" g$ I: { location~ \.php$ { ## 最多 5 个排队, 由于每秒处理 10 个请求 + 5个排队,你一秒最多发送 15 个请求过来,再多就直接返回 503 错误给你了 limit_req # V2 }1 w% W1 A# ]; f4 {) F2 O9 i' N
zone=ConnLimitZone burst=5 nodelay;}} 3 v! E! |) _& ]& R# S; r5 r/ D! h
5 p( K) F. j2 k- h: j; q2 c& g; k! v% K7 _; n
& ]" ~ L" ~; K! `: p2 K4 b
1 y( v ^+ S* E% V8 ^+ v. ^ |