Nginx防盜鏈配置
1. 什么是盜鏈?
百度百科的解釋如下:
盜鏈是指服務提供商自己不提供服務的內容,通過技術手段繞過其它有利益的最終用戶界面(如廣告),直接在自己的網站上向最終用戶提供其它服務提供商的服務內容,騙取最終用戶的瀏覽和點擊率。受益者不提供資源或提供很少的資源,而真正的服務提供商卻得不到任何的收益。
盜鏈在如今的互聯網世界無處不在,盜圖,盜視頻、盜文章等等,都是通過獲取正規(guī)網站的圖片、視頻、文章等的 url 地址,直接放到自己網站上使用而未經授權。 盜資源是黑產界以最小成本獲取最高利益的一個常用手段。比如筆者最近考慮買房,在貝殼網上有房源的真是戶型圖以及VR。某些房產中介直接會盜用貝殼網上的真實戶型圖來騙取點擊。因此,對于任何一個大型網站而言,做好防盜措施,避免自身利益受損是至關重要的。Nginx 在代理這類靜態(tài)資源(圖片、視頻、文章等)時,可以通過配置實現防盜連的功能。
2. 如何防盜鏈?
前面介紹到,盜鏈是直接使用正規(guī)網站保存圖片、視頻等的 URL 以獲取相應的資源。最簡單的防盜想法就是根據客戶端請求資源時所攜帶的一些關鍵信息來驗證請求的合法性,比如客戶端 IP、請求 URL 中攜帶的 referer,如果不合法則直接拒絕請求。此外,由于這些基礎信息都可以偽造,因此這樣的基礎手段也不一定安全。此外,還有登錄認證、使用 cookie 等其他防盜連手段。另外,針對特定場景,比如流媒體直播中還有更為高級的防盜手段包括時間戳防盜鏈、swf 防盜鏈、回源鑒權防盜鏈等。
3. Nginx中防盜鏈配置
3.1 refer模塊防盜
Nginx 用于實現防盜鏈功能的模塊為 refer 模塊,其依據的原理是: 如果網站盜用了你的圖片,那么用戶在點擊或者查看這個盜鏈內容時,發(fā)送 http 請求的頭部中的 referer 字段將為該盜版網站的 url。這樣我們通過獲取這個頭部信息,知道 http 發(fā)起請求的頁面,然后判斷這個地址是否是我們的合法頁面,不是則判斷為盜鏈。Nginx 的 referer 模塊中有3個指令,用法分別如下:
Syntax: referer_hash_bucket_size size;
Default: referer_hash_bucket_size 64;
Context: server, location
Syntax: referer_hash_max_size size;
Default: referer_hash_max_size 2048;
Context: server, location
Syntax: valid_referers none | blocked | server_names | string ...;
Default: —
Context: server, location
最重要的是 valid_referers 指令,它后面可以帶上多個參數,表示多個 referer 頭都是有效的。它的參數形式有:
- none: 允許缺失 referer 頭部的請求訪問
- blocked: 有 referer 這個字段,但是其值被防火墻或者是代理給刪除了
- server_names: 若 referer 中的站點域名和 server_names 中的某個域名匹配,則允許訪問
- 任意字符或者正則表達式
Nginx 會通過查看 referer 字段和 valid_referers 后面的 referer 列表進行匹配,如果匹配到了就將內置的變量$invalid_referer值設置為0,否則設置該值為1
這樣一個簡單的 Nginx 防盜鏈配置如下:
...
location / {
valid_referers none blocked *.domain.pub www.domain.com/nginx server_names ~\.baidu\.;
if ($invalid_referer) {
return 403;
}
return 200 "valid\n";
}
...
3.2 secure_link模塊防盜
前面這種簡單檢查 referer 頭部值的防盜鏈方法過于脆弱,盜用者很容易通過偽造 referer 的值輕而易舉跳過防盜措施。在 Nginx 中有一種更為高級的防盜方式,即基于 secure_link 模塊,該模塊能夠檢查請求鏈接的權限以及是否過期,多用于下載服務器防盜鏈。這個模塊默認未編譯進 Nginx,需要在源碼編譯時候使用 --with-secure_link_module 添加。
該模塊的通過驗證 URL 中的哈希值的方式防盜鏈。它的防盜過程如下:
- 由服務器或者 Nginx 生成安全的加密后的 URL, 返回給客戶端;
- 客戶端使用安全的 URL 訪問 Nginx,獲取圖片等資源,由 Nginx 的 secure_link 變量判斷是否驗證通過;
secure_link 模塊中總共有3個指令,其格式和說明分別如下:
Syntax: secure_link expression;
Default: —
Context: http, server, location
Syntax: secure_link_md5 expression;
Default: —
Context: http, server, location
Syntax: secure_link_secret word;
Default: —
Context: location
通過配置 secure_link, secure_link_md5 指令,可實現對鏈接進行權限以及過期檢查判斷的功能。
和 referer 模塊中的 $invalid_referer 變量一樣,secure_link 模塊也是通過內置變量 KaTeX parse error: Expected 'EOF', got '判' at position 14: secure\_link 判?斷驗證是否通過。secure_link 的值有如下三種情況:
- 空字符串: 驗證不通過
- 0: URL 過期
- 1: 驗證通過
通常使用這個模塊進行 URL 校驗,我們需要考慮的是如何生成合法的 URL ?另外,需要在 Nginx 中做怎樣的配置才可以校驗這個 URL?
對于第一個問題,生成合法的 URL 和 指令 secure_link_md5 有關。例如:
secure_link_md5 "$secure_link_expires$uri$remote_addr secret";
如果 Nginx 中secure_link_md5 是上述配置,那么生成合法 url 的命令如下:
# 2020-02-05 21:00:00 轉換成時間戳為1580907600
echo -n '1580907600/test.png127.0.0.1 secret' | \
openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =
通過上述命令,我們得到了一個 md5 值:cPnjBG9bAZvY_jbPOj13mA,這個非常重要。接下來,構造合的 URL 和指令 secure_link 相關。如果 secure_link 指令的配置如下:
secure_link $arg_md5,$arg_expires;
那么我們的請求的 url 中必須帶上 md5 和 expires 參數,例如:
http://180.76.152.113:9008/test.png?md5=cPnjBG9bAZvY_jbPOj13mA&expires=1580907600
對于 Nginx 中的校驗配置示例如下:
location ~* .(gif|jpg|png|swf|flv|mp4)$ {
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr secret";
# 空字符串,校驗不通過
if ($secure_link = "") {
return 403;
}
# 時間過期
if ($secure_link = "0") {
return 410 "URL過期,請重新生成";
}
root /root/test;
}
在 Nginx 的配置中,除了前面提到的 secure_link 和 secure_link_md5 指令外,我們對通過校驗和校驗失敗的情況進行了處理。接下來請看實驗部分。
4. 案例實戰(zhàn)
4.1 refer 模塊防盜鏈測試
在 nginx.conf 中加入如下防盜配置:
...
http {
...
server {
listen 9008;
location / {
valid_referers none blocked *.domain.pub www.domain.com/nginx server_names ~\.baidu\.;
if ($invalid_referer) {
return 403;
}
return 200 "valid\n";
}
}
...
}
...
重新加載或者啟動 Nginx 后,我們進行如下操作:
[shen@shen Desktop]$ curl -H 'referer: http://www.domain.com/test' http://180.76.152.113:9008
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.17.6</center>
</body>
</html>
[shen@shen Desktop]$ curl -H 'referer: http://www.domain.com/nginx' http://180.76.152.113:9008
valid
[shen@shen Desktop]$ curl -H 'referer: ' http://180.76.152.113:9008
valid
[shen@shen Desktop]$ curl http://180.76.152.113:9008
valid
[shen@shen Desktop]$ curl -H 'referer: http://www.domain.pub/test' http://180.76.152.113:9008
valid
第一個 http 請求 referer 的值存在,但是沒有匹配后面的域名,所以返回403。其余的請求中 referer 值要么不存在,要么沒有這個頭部,要么匹配了后面的域名正則表達,都通過了 referer 校驗,所以都返回 “valid” 字符串。我們通過構造不同的 referer 頭部字段成功的繞過了 Nginx 的referer 模塊校驗,也說明了這種防盜的方式極不靠譜。
4.2 secure_link 防盜鏈測試
我們準備一個靜態(tài)圖片, 名為 test.png,放到搭建了 Nginx 的服務器上,全路徑為 /root/test/test.png。
我們準備 Nginx 配置如下:
...
http {
...
server {
listen 8000;
location / {
# return 200 "$remote_addr";
root /root/test;
}
}
server {
listen 8001;
location ~* .(jpg|png|flv|mp4)$ {
secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri$remote_addr secret";
# 空字符串,校驗不通過
if ($secure_link = "") {
return 403;
}
# 時間過期
if ($secure_link = "0") {
return 410;
}
# 校驗通過,訪問對的靜態(tài)資源
root /root/test;
}
}
}
...
首先,在瀏覽器上訪問8000端口我們可以獲取對應的 $remote_addr 變量值(打開 return 的注釋配置),結果為103.46.244.69, 這是客戶端請求時的對外 IP。訪問瀏覽器上訪問8000端口,URI=/test.png, 可以看到這個靜態(tài)圖片。
接下來,我們在訪問8001端口,URI=/test.png時,可以發(fā)現返回403頁面,說明安全模塊生效。
當前時間為2020年02月05日晚上9點半,我們找一個過期時間晚上10點,得到相應的時間戳為1580911200。按照 secure_link_md5 指令格式,使用如下 shell 命令生成 md5 值:
[shen@shen Desktop]$ echo -n '1580911200/test.png103.46.244.69 secret' | openssl md5 -binary | openssl base64 | tr +/ -_ | tr -d =
KnJx3J6fN_0Qc1W5TqEVXw
這樣可以得到我們的安全訪問 URL 為:
# 訪問靜態(tài)資源test.png的安全URL為:
http://180.76.152.113:8001/test.png?md5=KnJx3J6fN_0Qc1W5TqEVXw&expires=1580911200
再次到瀏覽器上訪問時候,我就可以看到靜態(tài)圖片了。
此外,我們還可以等到10點之后,測試過期后的結果。在過期之后再用這個 URL 訪問時無法查看圖片,而且返回的是 410 的狀態(tài)碼,這說明 Nginx 成功檢測到這個密鑰值已經過期。
5. 小結
本節(jié)我們介紹了一些防盜鏈的知識,然后開始介紹 Nginx 中的防盜鏈配置。一般的 Nginx 防盜鏈手段都是通過 referer 字段來判斷請求的來源地,由此去判定請求是否合法。但是該字段容易偽造,所以很少用該方法實現防盜功能。而Nginx 的 secure_link 模塊主要是使用 hash 算法加密方式,一般用于圖片、視頻下載,生成下載 URL,安全性高。此外,我們也可以使用一些第三方的模塊增強 Nginx 的防盜鏈功能,比如常用的第三放模塊ngx_http_accesskey_module 可用于實現文件下載的防盜功能。