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

上一篇文章講了 Nginx 中的變量和運行原理,下面就來講一個主要提供變量並修改變量的值的模塊,也就是咱們要講的防盜鏈模塊:referer 模塊。html

簡單有效的防盜鏈手段

場景

若是作過我的站點的同窗,可能會遇到別人盜用本身站點資源連接的狀況,這就是盜鏈。說到盜鏈就要說一個 HTTP 協議的 頭部,referer 頭部。當其餘網站經過 URL 引用了你的頁面,用戶在瀏覽器上點擊 URL 時,HTTP 請求的頭部會經過 referer 頭部將該網站當前頁面的 URL 帶上,告訴服務器本次請求是由誰發起的。nginx

例如,在谷歌中搜索 Nginx 而後點擊連接:git

在打開的新頁面中查看請求頭會發現,請求頭中包含了 referer 頭部且值是 www.google.com/。github

像谷歌這種咱們是容許的,可是有一些其餘的網站想要引用咱們本身網站的資源時,就須要作一些管控了,否則豈不是誰均可以拿到連接。正則表達式

目的

這裏目的其實已經很明確了,就是要拒絕非正常的網站訪問咱們站點的資源。算法

思路

  • invalid_referer 變量
    • referer 提供了這個變量,能夠用來配置哪些 referer 頭部合法,也就是,你容許哪些網站引用你的資源。

referer 模塊

要實現上面的目的,referer 模塊可得算頭一號,一塊兒看下 referer 模塊怎麼用的。shell

  • 默認編譯進 Nginx,經過 --without-http_referer_module 禁用

referer 模塊有三個指令,下面看一下。express

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

實戰

下面來看一個配置文件。

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';
    }
}
複製代碼

那麼對於這個配置文件而言,如下哪些請求會被拒絕呢?

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 指令配置了哪些值呢?

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 先後都是正則匹配

下面就實際看下響應:

# 返回 403,沒有匹配到任何規則
➜  ~ curl -H 'referer: http://www.ziyang.org.cn/ttt' referer.ziyang.com/
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>
➜  ~ curl -H 'referer: http://image.baidu.com/search/detail' referer.ziyang.com/
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>
# 匹配到了 *.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複製代碼

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
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";
    • 按照什麼樣的順序構造原始字符串

實戰

下面是一個實際的配置文件,我這裏就不作演示了,感興趣的能夠本身作下實驗。

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,全部配置文件我已經放在了 Nginx 配置文件,你們能夠自取。

相關文章
相關標籤/搜索