Nginx配置指令location匹配符優先順序和安全問題

double2li發表於2017-07-01
使用nginx 很久了,它的效能高,穩定性表現也很好,得到了很多人的認可。特別是它的配置,有點像寫程式一樣,每行命令結尾一個”;”號,語句塊用”{}”括起來。 配製好,直接nginx -t 檢查配製情況,配製成功,直接執行:service nginx reload .伺服器沒有任何當機情況下,實現平穩修改配置

最近一直在做location 配置,遇到優先順序別問題(如果配置不當可能存在安全隱患哦),以下是個人學習一點體會。

一、 location 的匹配符
1.等於匹配符:=
等於匹配符就是等號,特點可以概括為兩點:
精確匹配
不支援正規表示式
2.空匹配符
空匹配符的特點是:
匹配以指定模式開始的 URI
不支援正規表示式
3.正則匹配符:~
正則匹配符是可以使用正規表示式的匹配符。不過這裡要強調的是,一般來說~是指:
區分大小寫的正則匹配
而~*表示:
不區分大小寫的正則匹配
但是對於一些對大小寫不敏感的作業系統,這兩者沒有區別。另外一個就是^~,其表示以指定模式開始的正則匹配。

4.內部訪問符:@
一般用於錯誤頁面等,這個暫不討論。

二、匹配符優先順序
1.=
2.空匹配符,滿足精確匹配時
3.^~
4.~或~*
5.空匹配符,滿足以指定模式開始時的匹配時
這樣說比較抽象,我們來看例子吧。

2.1 等於匹配符與精確匹配時的空匹配符

看下面的例子(用到我們此前一起完成的Hello World模組):

複製程式碼 程式碼如下:

location /poechant {
    hello_world no1;
}

 

location = /poechant {
    hello_world no2;
}

如果我們的請求是http://my.domian/poechant,則我們發現兩個location都與請求的 URI 匹配,這時根據我們的優先順序順序,第一個是精確匹配時的空匹配符,第二個是等於匹配符,所以第二個的優先順序高,也就是應該輸出:

 

hello_world, no2
同時也說明 Nginx 的 locatoin 不是按照配置檔案中的書寫順序來匹配的。

2.2 精確匹配時的空匹配符與正則匹配的^~

下面這個例子中,兩者開始都精確匹配了,連這個正則匹配都是精確匹配。

複製程式碼 程式碼如下:

location ^~ ^/poechant$ {
    hello_world no1;
}

 

location /poechant {
    hello_world no2;
}

匹配哪一個?你測試一下,會得到:

 

hello_world, no2
與我們上面說的優先順序順序相吻合。
2.3 其他匹配優先順序比較的例項

三、實戰經驗總結

1.location 匹配的優先順序(來自實踐總結中)
(location =) > (location 完整路徑 >) >(location ^~ 路徑) >(location ~* 正則) >(location 路徑)
只要匹配到,其它的都會忽略,然後返回到改匹配。
用以下例子來測試:

 

複製程式碼 程式碼如下:

#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;
}

 

說明:測試的時候,先要將#2全部註釋掉,不然會認為#2 與#4 完全一樣。會提示:重複配置,提示如下

複製程式碼 程式碼如下:

D:
ginx-0.8.7>nginx -s reload
[emerg]: duplicate location “/a/” in D:
ginx-0.8.7/conf/nginx.conf:53

 

瀏覽測試:每次都是訪問:http://localhost:9999/a/1.jpg (在windows 安裝測試,然後埠是9999) 檔案a/1.jpg 根本不存在。關鍵是測試看頁面返回情況。

a.用上面的配置請求後的結果

 

複製程式碼 程式碼如下:
400 Bad Request
——————————————————————————–
nginx/0.8.7

從測試中可以看到,優先順序最高的是:= 號。 它會最先匹配到。
b.接下來我們 遮蔽掉 #6 如下:

複製程式碼 程式碼如下:
#6
#    location = /a/1.jpg {
#        return 400;
#    }

 

然後過載配置:D:
ginx-0.8.7> nginx -s reload  並訪問:http://localhost:9999/a/1.jpg ,返回以下結果:

複製程式碼 程式碼如下:

401 Authorization Required
——————————————————————————–
nginx/0.8.7

 

結論:從這個測試發現,沒有“=”情況下,location 後面直接接完整路徑是優先匹配。 通過測試發現,如果將:location/a/1.jpg   改成:location /a/1.jpg
會出現意外情況,直接出現是:return 402.  從這一點,可以推測到nginx 匹配優先是:網站路徑,並且不帶正規表示式的優先。

c.同理測試 遮蔽掉 #5 如下:註釋及重新載入同上.
訪問:http://localhost:9999/a/1.jpg 返回如下結果。

複製程式碼 程式碼如下:

402 Payment Required
——————————————————————————–
nginx/0.8.7

 

結論:通過這個測試可以得出:location ^~ 優先順序 高於 location ~* 優先順序 ,其中:^~ 主要後面接路徑。

c.同理測試 遮蔽掉 #4 如下:註釋及重新載入同上.
訪問:http://localhost:9999/a/1.jpg 返回如下結果。

複製程式碼 程式碼如下:

403 Forbidden
——————————————————————————–
nginx/0.8.7

結論:從以上比較得到,正則優先 未帶任何匹配符的路徑匹配

 

d.同理測試 遮蔽掉 #3 如下:註釋及重新載入同上. 並且去掉#2 的註釋“#”
訪問:http://localhost:9999/a/1.jpg 返回如下結果。

複製程式碼 程式碼如下:
404 Not Found
——————————————————————————–
nginx/0.8.7

結論:比較有意思是:/a/ 與 /  應該是 同種型別的匹配表示式, 可以從中得到,該匹配順序是,將路徑從右匹配, 可以推測形如逐個字元,那個先匹配到,就是那個優先。 因此得到是:/a/ 優先於 / .

 

以上測試,是我測試結果,優先順序別以以上規律。 在實際我們書寫中,經常會犯錯誤。 還記得前段時間:80後安全團隊曝nginx漏洞 其實,個人認為不能算是nginx 漏洞,只是,我們不瞭解nginx 配製規則,而出現一個配置上面致命漏洞而已。 其實,通過上面優先順序,我們在配置時候可能也一樣經常犯一個致命錯誤。

複製程式碼 程式碼如下:

#以下是隨便寫例子,個人可能各不相同
#假設站點在:/home/www/html/目錄下,所有的php 及上傳檔案都在這個目錄下面。
location ~* .php$ {
    proxy_pass http://www.a.com;
}

 

location  /upload/ {
    alias   /home/www/html/upload/;
}

 

而且,這個upload 目錄,是靜態目錄,我們想法是下面所有檔案是不能夠執行的,包括php檔案。
如果有使用者訪問:http://www.a.com/upload/1.css , 會直接顯示該css, 但是,如果有使用者訪問:http://www.a.com/upload/1.php  類似檔案,正如上面所說,實際匹配到:~* .php$  了。 upload 下面是執行了。
從這個裡面,我們發現一個問題,實際沒有達到我們要求。 靜態目錄下面的檔案一樣執行了。 這下比較麻煩了。 一旦出現個什麼上存漏洞的,別人上存了一個php,我們還以為,我們配置是ok的。 覺得很安全,缺在不知不覺中被別人開啟一扇門。

那麼我們怎麼樣修改呢?

 

複製程式碼 程式碼如下:

location ~* .php$ {
    proxy_pass http://www.a.com;
}
location ^~ /upload/ {
    alias   /home/www/html/upload/;
}

 

對,就是必須用:”^~” ,這樣是不是就已經安全了呢。 如果你再訪問下:http://www.a.com/upload/1.php  你會發現,這段程式碼原始碼顯示出來了。 這個其實對於我們而言也是不想見到了。 一段顯示原始碼,在各個搜尋引擎,很容易通過所有特殊關鍵字,搜尋到改檔案的。
那麼我們該怎麼樣配置安全的上存目錄呢? 對,你想到了:限制允許的特殊檔案型別。

複製程式碼 程式碼如下:

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/;
}

 

只要不是滿足上面副檔名檔案,就自動提示:403 不能訪問,有可以避免原始碼顯示。
剛才從匹配結果已經知道了,同級不帶任何匹配符的,是以右為準匹配。 那麼,如果都用正規表示式,以什麼方式匹配呢?
測試如下:(新建配置檔案,server 包含)

複製程式碼 程式碼如下:

    location ~* .jpg$ {
            return 402;
    }

 

    location ~* 1.jpg$ {
            return 403;
    }

 

結果如下:

 

複製程式碼 程式碼如下:

402 Payment Required
——————————————————————————–
nginx/0.8.7

 

看來是返回的是:402 上面一個呢。 按理論說,1.jpg 配置 比 .jpg 更準確,看來跟上面說的順序不同,那它會不會是那個在前以那個匹配呢? 我們再測試下:

複製程式碼 程式碼如下:

location ~* 1.jpg$ {
            return 403;
    }

 

    location ~* .jpg$ {
            return 402;
    }

 

返回結果是:

 

複製程式碼 程式碼如下:

403 Forbidden
——————————————————————————–
nginx/0.8.7

 

哈哈,恰好相反,看來我的推斷是正確的,如果都是正則,都能夠匹配,以配置檔案出現順序來,誰在前誰優先。 一口氣說了,不知道朋友你,明白我的思路嗎?這樣的比較會很多很多,大家可以逐一測試。 熟悉location 配置,對於熟練運用nginx 是一個必備基礎。 因為nginx 太靈活,也太流行了。上面的問題,也許朋友你,會遇到。希望對你有幫助。


相關文章