1. 程式人生 > >聽說你的資源被盜用了,那你知道 Nginx 怎麼防盜鏈嗎?

聽說你的資源被盜用了,那你知道 Nginx 怎麼防盜鏈嗎?

上一篇文章講了 [Nginx 中的變數和執行原理](https://iziyang.github.io/2020/06/09/7-nginx/),下面就來說一個主要提供變數並修改變數的值的模組,也就是我們要講的防盜鏈模組:referer 模組。 ## 簡單有效的防盜鏈手段 ### 場景 如果做過個人站點的同學,可能會遇到別人盜用自己站點資源連結的情況,這就是盜鏈。說到盜鏈就要說一個 HTTP 協議的 頭部,referer 頭部。當其他網站通過 URL 引用了你的頁面,使用者在瀏覽器上點選 URL 時,HTTP 請求的頭部會通過 referer 頭部將該網站當前頁面的 URL 帶上,告訴伺服器本次請求是由誰發起的。 例如,在谷歌中搜索 Nginx 然後點選連結: 在開啟的新頁面中檢視請求頭會發現,請求頭中包含了 referer 頭部且值是 https://www.google.com/。 像谷歌這種我們是允許的,但是有一些其他的網站想要引用我們自己網站的資源時,就需要做一些管控了,不然豈不是誰都可以拿到連結。 ### 目的 這裡目的其實已經很明確了,就是要拒絕非正常的網站訪問我們站點的資源。 ### 思路 - invalid_referer 變數 - referer 提供了這個變數,可以用來配置哪些 referer 頭部合法,也就是,你允許哪些網站引用你的資源。 ## referer 模組 要實現上面的目的,referer 模組可得算頭一號,一起看下 referer 模組怎麼用的。 - 預設編譯進 Nginx,通過 `--without-http_referer_module` 禁用 referer 模組有三個指令,下面看一下。 ```nginx Syntax: valid_referers none | blocked | server_names | string ...; Default: — Context: server, location 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 ``` - `valid_referers` 指令,配置是否允許 referer 頭部以及允許哪些 referer 訪問。 - `referer_hash_bucket_size` 表示這些配置的值是放在雜湊表中的,指定雜湊表的大小。 - `referer_hash_max_size` 則表示雜湊表的最大大小是多大。 這裡面最重要的是 `valid_referers` 指令,需要重點來說明一下。 ### valid_referers 指令 可以同時攜帶多個引數,表示多個 referer 頭部都生效。 **引數值** - none - 允許缺失 referer 頭部的請求訪問 - block:允許 referer 頭部沒有對應的值的請求訪問。例如可能經過了反向代理或者防火牆 - server_names:若 referer 中站點域名與 server_name 中本機域名某個匹配,則允許該請求訪問 - string:表示域名及 URL 的字串,對域名可在字首或者字尾中含有 * 萬用字元,若 referer 頭部的值匹配字串後,則允許訪問 - 正則表示式:若 referer 頭部的值匹配上了正則,就允許訪問 **invalid_referer 變數** - 允許訪問時變數值為空 - 不允許訪問時變數值為 1 ### 實戰 下面來看一個配置檔案。 ```nginx server { server_name referer.ziyang.com; listen 80; error_log logs/myerror.log debug; root html; location /{ valid_referers none blocked server_names *.ziyang.com www.ziyang.org.cn/nginx/ ~\.google\.; if ($invalid_referer) { return 403; } return 200 'valid\n'; } } ``` 那麼對於這個配置檔案而言,以下哪些請求會被拒絕呢? ```shell curl -H 'referer: http://www.ziyang.org.cn/ttt' referer.ziyang.com/ curl -H 'referer: http://www.ziyang.com/ttt' referer.ziyang.com/ curl -H 'referer: ' referer.ziyang.com/ curl referer.ziyang.com/ curl -H 'referer: http://www.ziyang.com' referer.ziyang.com/ curl -H 'referer: http://referer.ziyang.com' referer.ziyang.com/ curl -H 'referer: http://image.baidu.com/search/detail' referer.ziyang.com/ curl -H 'referer: http://image.google.com/search/detail' referer.ziyang.com/ ``` 我們需要先來解析一下這個配置檔案。`valid_referers` 指令配置了哪些值呢? ```nginx valid_referers none blocked server_names *.ziyang.com www.ziyang.org.cn/nginx/ ~\.google\.; ``` - none:表示沒有 referer 的可以訪問 - blocked:表示 referer 沒有值的可以訪問 - server_names:表示本機 server_name 也就是 referer.ziyang.com 可以訪問 - *.ziyang.com:匹配上了正則的可以訪問 - www.ziyang.org.cn/nginx/:該頁面發起的請求可以訪問 - ~\\.google\\.:google 前後都是正則匹配 下面就實際看下響應: ```shell # 返回 403,沒有匹配到任何規則 ➜ ~ curl -H 'referer: http://www.ziyang.org.cn/ttt' referer.ziyang.com/

403 Forbidden


nginx/1.17.8
➜ ~ curl -H 'referer: http://image.baidu.com/search/detail' referer.ziyang.com/

403 Forbidden


nginx/1.17.8
# 匹配到了 *.ziyang.com ➜ ~ curl -H 'referer: http://www.ziyang.com/ttt' referer.ziyang.com/ valid ➜ ~ curl -H 'referer: http://www.ziyang.com' referer.ziyang.com/ valid # 匹配到了 server name ➜ ~ curl -H 'referer: http://referer.ziyang.com' referer.ziyang.com/ valid # 匹配到了 blocked ➜ ~ curl -H 'referer: ' referer.ziyang.com/ valid # 匹配到了 none ➜ ~ curl referer.ziyang.com/ valid # 匹配到了 ~\.google\. ➜ ~ curl -H 'referer: http://image.google.com/search/detail' referer.ziyang.com/ valid ``` ## 防盜鏈另外一種解決方案:secure_link 模組 referer 模組是一種簡單的防盜鏈手段,必須依賴瀏覽器發起請求才會有效,如果攻擊者偽造 referer 頭部的話,這種方式就失效了。 secure_link 模組是另外一種解決的方案。 它的主要原理是,通過驗證 URL 中雜湊值的方式防盜鏈。 基本過程是這個樣子的: - 由伺服器(可以是 Nginx,也可以是其他 Web 伺服器)生成加密的安全連結 URL,返回給客戶端 - 客戶端使用安全 URL 訪問 Nginx,由 Nginx 的 secure_link 變數驗證是否通過 原理如下: - 雜湊演算法是不可逆的 - 客戶端只能拿到執行過雜湊演算法的 URL - 僅生成 URL 的伺服器,驗證 URL 是否安全的 Nginx,這兩者才儲存原始的字串 - 原始字串通常由以下部分有序組成: - 資源位置。如 HTTP 中指定資源的 URI,防止攻擊者拿到一個安全 URI 後可以訪問任意資源 - 使用者資訊。如使用者的 IP 地址,限制其他使用者盜用 URL - 時間戳。使安全 URL 及時過期 - 金鑰。僅伺服器端擁有,增加攻擊者猜測出原始字串的難度 模組: - ngx_http_secure_link_module - 未編譯進 Nginx,需要通過 --with-http_secure_link_module 新增 - 變數 - secure_link - secure_link_expires ```nginx 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 - 值為空字串:驗證不通過 - 值為 0:URL 過期 - 值為 1:驗證通過 - secure_link_expires - 時間戳的值 **命令列生成安全連結** - 生成 md5 ``` echo -n '時間戳URL客戶端IP金鑰' | openssl md5 -binary | openssl base64 | tr +/ - | tr -d = ``` - 構造請求 URL ``` /test1.txt?md5=md5生成值&expires=時間戳(如 2147483647) ``` **Nginx 配置** - secure_link \$arg_md5,$arg_expires; - secure_link 後面必須跟兩個值,一個是引數中的 md5,一個是時間戳 - secure_link_md5 \"\$secure_link_expires\$uri\$remote_addr secret\"; - 按照什麼樣的順序構造原始字串 ### 實戰 下面是一個實際的配置檔案,我這裡就不做演示了,感興趣的可以自己做下實驗。 ```nginx server { server_name securelink.ziyang.com; listen 80; error_log logs/myerror.log info; default_type text/plain; location /{ 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; } return 200 '$secure_link:$secure_link_expires\n'; } location /p/ { secure_link_secret mysecret2; if ($secure_link = "") { return 403; } rewrite ^ /secure/$secure_link; } location /secure/ { alias html/; internal; } } ``` ### 僅對 URI 進行雜湊的簡單辦法 除了上面這種相對複雜的方式防盜鏈,還有一種相對簡單的防盜鏈方式,就是隻對 URI 進行雜湊,這樣當 URI 傳 - 將請求 URL 分為三個部分:/prefix/hash/link - Hash 生成方式:對 “link 金鑰” 做 md5 雜湊 - 用 `secure_link_secret secret;` 配置金鑰 **命令列生成安全連結** - 原請求 - link - 生成的安全請求 - /prefix/md5/link - 生成 md5 - `echo -n 'linksecret' | openssl md5 –hex` **Nginx 配置** - `secure_link_secret secret;` 這個防盜鏈的方法比較簡單,那麼具體是怎麼用呢?大家都在網上下載過資源對吧,不管是電子書還是軟體,很多網站你點選下載的時候往往會彈出另外一個頁面去下載,這個新的頁面其實就是請求的 Nginx 生成的安全 URL。如果這個 URL 被拿到的話,其實還是可以用的,所以需要經常的更新金鑰來確保 URL 不會被盜用。 今天這篇文章詳細講了防盜鏈的具體用法,最近的這兩篇文章都是說的已有的變數用法,下一篇文章講一下怎麼生成新的變數。 --- 本文首發於我的個人部落格:[iziyang.github.io](https://iziyang.github.io),所有配置檔案我已經放在了 [Nginx 配置檔案](https://www.yuque.com/docs/share/b7c304d3-fde8-4168-9808-368cd3494e04?#),大家可以