最近一直在做location 配置,遇到優(yōu)先級(jí)別問題(如果配置不當(dāng)可能存在安全隱患哦),以下是個(gè)人學(xué)習(xí)一點(diǎn)體會(huì)。
一、 location 的匹配符
1.等于匹配符:=
等于匹配符就是等號(hào),特點(diǎn)可以概括為兩點(diǎn):
精確匹配
不支持正則表達(dá)式
2.空匹配符
空匹配符的特點(diǎn)是:
匹配以指定模式開始的 URI
不支持正則表達(dá)式
3.正則匹配符:~
正則匹配符是可以使用正則表達(dá)式的匹配符。不過這里要強(qiáng)調(diào)的是,一般來說~是指:
區(qū)分大小寫的正則匹配
而~*表示:
不區(qū)分大小寫的正則匹配
但是對(duì)于一些對(duì)大小寫不敏感的操作系統(tǒng),這兩者沒有區(qū)別。另外一個(gè)就是^~,其表示以指定模式開始的正則匹配。
4.內(nèi)部訪問符:@
一般用于錯(cuò)誤頁面等,這個(gè)暫不討論。
二、匹配符優(yōu)先級(jí)
1.=
2.空匹配符,滿足精確匹配時(shí)
3.^~
4.~或~*
5.空匹配符,滿足以指定模式開始時(shí)的匹配時(shí)
這樣說比較抽象,我們來看例子吧。
2.1 等于匹配符與精確匹配時(shí)的空匹配符
看下面的例子(用到我們此前一起完成的Hello World模塊):
location /poechant {
hello_world no1;
}
location = /poechant {
hello_world no2;
}
如果我們的請(qǐng)求是http://my.domian/poechant,則我們發(fā)現(xiàn)兩個(gè)location都與請(qǐng)求的 URI 匹配,這時(shí)根據(jù)我們的優(yōu)先級(jí)順序,第一個(gè)是精確匹配時(shí)的空匹配符,第二個(gè)是等于匹配符,所以第二個(gè)的優(yōu)先級(jí)高,也就是應(yīng)該輸出:
hello_world, no2
同時(shí)也說明 Nginx 的 locatoin 不是按照配置文件中的書寫順序來匹配的。
2.2 精確匹配時(shí)的空匹配符與正則匹配的^~
下面這個(gè)例子中,兩者開始都精確匹配了,連這個(gè)正則匹配都是精確匹配。
location ^~ ^/poechant$ {
hello_world no1;
}
location /poechant {
hello_world no2;
}
匹配哪一個(gè)?你測(cè)試一下,會(huì)得到:
hello_world, no2
與我們上面說的優(yōu)先級(jí)順序相吻合。
2.3 其他匹配優(yōu)先級(jí)比較的實(shí)例
略
三、實(shí)戰(zhàn)經(jīng)驗(yàn)總結(jié)
1.location 匹配的優(yōu)先級(jí)(來自實(shí)踐總結(jié)中)
(location =) > (location 完整路徑 >) >(location ^~ 路徑) >(location ~* 正則) >(location 路徑)
只要匹配到,其它的都會(huì)忽略,然后返回到改匹配。
用以下例子來測(cè)試:
#1
location / {
return 500;
}
#2
location /a/ {
return 404;
}
#3
location ~* \.jpg$ {
return 403;
}
#4
location ^~ /a/ {
return 402;
}
#5
location /a/1.jpg {
return 401;
}
#6
location = /a/1.jpg {
return 400;
}
說明:測(cè)試的時(shí)候,先要將#2全部注釋掉,不然會(huì)認(rèn)為#2 與#4 完全一樣。會(huì)提示:重復(fù)配置,提示如下
D:\nginx-0.8.7>nginx -s reload
[emerg]: duplicate location "/a/" in D:\nginx-0.8.7/conf/nginx.conf:53
瀏覽測(cè)試:每次都是訪問:http://localhost:9999/a/1.jpg (在windows 安裝測(cè)試,然后端口是9999) 文件a/1.jpg 根本不存在。關(guān)鍵是測(cè)試看頁面返回情況。
a.用上面的配置請(qǐng)求后的結(jié)果
--------------------------------------------------------------------------------
nginx/0.8.7
從測(cè)試中可以看到,優(yōu)先級(jí)最高的是:= 號(hào)。 它會(huì)最先匹配到。
b.接下來我們 屏蔽掉 #6 如下:
# location = /a/1.jpg {
# return 400;
# }
然后重載配置:D:\nginx-0.8.7> nginx -s reload 并訪問:http://localhost:9999/a/1.jpg ,返回以下結(jié)果:
401 Authorization Required
--------------------------------------------------------------------------------
nginx/0.8.7
結(jié)論:從這個(gè)測(cè)試發(fā)現(xiàn),沒有“=”情況下,location 后面直接接完整路徑是優(yōu)先匹配。 通過測(cè)試發(fā)現(xiàn),如果將:location/a/1.jpg 改成:location /a/1\.jpg
會(huì)出現(xiàn)意外情況,直接出現(xiàn)是:return 402. 從這一點(diǎn),可以推測(cè)到nginx 匹配優(yōu)先是:網(wǎng)站路徑,并且不帶正則表達(dá)式的優(yōu)先。
c.同理測(cè)試 屏蔽掉 #5 如下:注釋及重新加載同上.
訪問:http://localhost:9999/a/1.jpg 返回如下結(jié)果。
402 Payment Required
--------------------------------------------------------------------------------
nginx/0.8.7
結(jié)論:通過這個(gè)測(cè)試可以得出:location ^~ 優(yōu)先級(jí) 高于 location ~* 優(yōu)先級(jí) ,其中:^~ 主要后面接路徑。
c.同理測(cè)試 屏蔽掉 #4 如下:注釋及重新加載同上.
訪問:http://localhost:9999/a/1.jpg 返回如下結(jié)果。
403 Forbidden
--------------------------------------------------------------------------------
nginx/0.8.7
結(jié)論:從以上比較得到,正則優(yōu)先 未帶任何匹配符的路徑匹配
d.同理測(cè)試 屏蔽掉 #3 如下:注釋及重新加載同上. 并且去掉#2 的注釋“#”
訪問:http://localhost:9999/a/1.jpg 返回如下結(jié)果。
--------------------------------------------------------------------------------
nginx/0.8.7
結(jié)論:比較有意思是:/a/ 與 / 應(yīng)該是 同種類型的匹配表達(dá)式, 可以從中得到,該匹配順序是,將路徑從右匹配, 可以推測(cè)形如逐個(gè)字符,那個(gè)先匹配到,就是那個(gè)優(yōu)先。 因此得到是:/a/ 優(yōu)先于 / .
以上測(cè)試,是我測(cè)試結(jié)果,優(yōu)先級(jí)別以以上規(guī)律。 在實(shí)際我們書寫中,經(jīng)常會(huì)犯錯(cuò)誤。 還記得前段時(shí)間:80后安全團(tuán)隊(duì)曝nginx漏洞 其實(shí),個(gè)人認(rèn)為不能算是nginx 漏洞,只是,我們不了解nginx 配制規(guī)則,而出現(xiàn)一個(gè)配置上面致命漏洞而已。 其實(shí),通過上面優(yōu)先級(jí),我們?cè)谂渲脮r(shí)候可能也一樣經(jīng)常犯一個(gè)致命錯(cuò)誤。
#以下是隨便寫例子,個(gè)人可能各不相同
#假設(shè)站點(diǎn)在:/home/www/html/目錄下,所有的php 及上傳文件都在這個(gè)目錄下面。
location ~* \.php$ {
proxy_pass http://www.a.com;
}
location /upload/ {
alias /home/www/html/upload/;
}
而且,這個(gè)upload 目錄,是靜態(tài)目錄,我們想法是下面所有文件是不能夠執(zhí)行的,包括php文件。
如果有用戶訪問:http://www.a.com/upload/1.css , 會(huì)直接顯示該css, 但是,如果有用戶訪問:http://www.a.com/upload/1.php 類似文件,正如上面所說,實(shí)際匹配到:~* \.php$ 了。 upload 下面是執(zhí)行了。
從這個(gè)里面,我們發(fā)現(xiàn)一個(gè)問題,實(shí)際沒有達(dá)到我們要求。 靜態(tài)目錄下面的文件一樣執(zhí)行了。 這下比較麻煩了。 一旦出現(xiàn)個(gè)什么上存漏洞的,別人上存了一個(gè)php,我們還以為,我們配置是ok的。 覺得很安全,缺在不知不覺中被別人打開一扇門。
那么我們?cè)趺礃有薷哪兀?/p>
location ~* \.php$ {
proxy_pass http://www.a.com;
}
location ^~ /upload/ {
alias /home/www/html/upload/;
}
對(duì),就是必須用:"^~" ,這樣是不是就已經(jīng)安全了呢。 如果你再訪問下:http://www.a.com/upload/1.php 你會(huì)發(fā)現(xiàn),這段代碼源碼顯示出來了。 這個(gè)其實(shí)對(duì)于我們而言也是不想見到了。 一段顯示源碼,在各個(gè)搜索引擎,很容易通過所有特殊關(guān)鍵字,搜索到改文件的。
那么我們?cè)撛趺礃优渲冒踩纳洗婺夸浤兀?對(duì),你想到了:限制允許的特殊文件類型。
location ~* \.php$ {
proxy_pass http://www.a.com;
}
location ^~ /upload/ {
if ($request_filename ! ~* \.(jpg|jpeg|gif|png|swf|zip|rar|txt)$) {
return 403;
}
alias /home/www/html/upload/;
}
只要不是滿足上面擴(kuò)展名文件,就自動(dòng)提示:403 不能訪問,有可以避免源代碼顯示。
剛才從匹配結(jié)果已經(jīng)知道了,同級(jí)不帶任何匹配符的,是以右為準(zhǔn)匹配。 那么,如果都用正則表達(dá)式,以什么方式匹配呢?
測(cè)試如下:(新建配置文件,server 包含)
location ~* \.jpg$ {
return 402;
}
location ~* 1\.jpg$ {
return 403;
}
結(jié)果如下:
402 Payment Required
--------------------------------------------------------------------------------
nginx/0.8.7
看來是返回的是:402 上面一個(gè)呢。 按理論說,1.jpg 配置 比 .jpg 更準(zhǔn)確,看來跟上面說的順序不同,那它會(huì)不會(huì)是那個(gè)在前以那個(gè)匹配呢? 我們?cè)贉y(cè)試下:
location ~* 1\.jpg$ {
return 403;
}
location ~* \.jpg$ {
return 402;
}
返回結(jié)果是:
403 Forbidden
--------------------------------------------------------------------------------
nginx/0.8.7
哈哈,恰好相反,看來我的推斷是正確的,如果都是正則,都能夠匹配,以配置文件出現(xiàn)順序來,誰在前誰優(yōu)先。 一口氣說了,不知道朋友你,明白我的思路嗎?這樣的比較會(huì)很多很多,大家可以逐一測(cè)試。 熟悉location 配置,對(duì)于熟練運(yùn)用nginx 是一個(gè)必備基礎(chǔ)。 因?yàn)閚ginx 太靈活,也太流行了。上面的問題,也許朋友你,會(huì)遇到。希望對(duì)你有幫助。