基於NodeJS的HTTP server Plus 2:防盜鏈(referer)

什麼是 「盜鏈」?

「盜鏈」 說白了就是利用別人網站的資源連接放在本身的站點,在未經容許的狀況下去獲取別人網站裏面的圖片或者視頻等資源,致使資源全部者的網站的流量費用增長或收入減小,爲了防止資源連接隨意被人盜用的手段被稱爲 「防盜鏈」。javascript

模擬 「盜鏈」 場景

咱們先來模擬一下 「盜鏈」 場景,在本地啓動服務運行 hotlinking.html 文件,並在文件中盜用百度視頻的圖片資源,看看效果。html

文件:hotlinking.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>盜鏈</title>
</head>
<body>
    <img src="http://c.hiphotos.baidu.com/video/pic/item/e61190ef76c6a7ef8891a7c9f1faaf51f2de66ad.jpg">
</body>
</html>複製代碼

咱們經過 http-server 來啓動服務器訪問 hotlinking.html,使用 http-server 需全局安裝。java

http-server install -g瀏覽器

在服務中打開 hotlinking.html 後咱們發現圖片並非咱們盜用連接的資源,而是變成了下面這張圖片。bash


百度防盜鏈返回圖片

這張圖用來提醒咱們盜用了別人資源,是由於百度的服務器作了防盜鏈處理,若是全部盜用別人的資源都變成這樣,盜用也就沒有實際意義了,咱們本篇就經過 NodeJS 來實現防盜鏈處理,用來保護本身站點的資源。服務器

注意:具有防盜鏈處理的網站的資源連接能夠直接經過瀏覽器地址欄訪問,也能夠在文件域(file 協議)訪問,限制的是在未經容許的狀況下其餘服務器的訪問。

NodeJS 服務器實現防盜鏈

一、模擬兩個域名

在本地的 hosts 文件中加入兩個域名:async

127.0.0.1          panda.com
127.0.0.1          shen.comide

二、準備圖片資源

在根目錄建立文件夾 public 並存入兩張圖片,success.png 是正常請求的圖片資源,error.png 是通過防盜鏈處理後返回的圖片資源,兩張圖片以下。學習

正常返回的圖片資源 success.png:網站


正常返回資源

防盜鏈處理後返回的圖片資源 error.png:


防盜鏈返回資源

三、頁面 index.html

在頁面當中經過 img 標籤分別訪問 shen.companda.comlocalhost 域下的 success.png 文件。

文件:index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>盜鏈</title>
</head>
<body>
    <img src="http://panda.com:3000/success.png">
    <img src="http://shen.com:3000/success.png">
    <img src="http://localhost:3000/success.png">
</body>
</html>複製代碼

四、服務端 server.js

在寫服務端代碼以前須要介紹兩個重要的請求頭:

  • host:資源所在的域
  • referer:請求來源的域

其實資源防盜就是設置白名單,經過檢測 referer 是否在白名單中,若是在則正常返回資源,不存在則返回通過防盜鏈處理的資源。

注意:referer 請求頭在地址欄輸入地址時發送的請求是不存在的(如請求 index.html 頁面),在舊版本的 HTTP 協議中 referer 的寫法爲 referered,因此爲了兼容舊版本協議應該作兼容處理。

文件:server.js
// 引入依賴
const http = require("http");
const url = require("url");
const path = require("path");
const fs = require("mz/fs");

const server = http.createServer(responseImages); // 建立服務器
let static = path.resolve(__dirname, "public"); // 靜態資源目錄
let whiteList = ["shen.com"]; // 白名單

async function responseImages(req, res) {
    // 解析 url 中的文件目錄處理成絕對路徑
    let p = path.join(static, url.parse(req.url).pathname);

    // 檢測文件路徑是否合法,不合法直接返回 Not Found
    let isExist = await fs.exists(p);

    if (isExist) {
        // 獲取 referer
        let refer = req.headers["referer"] || req.headers["referered"];

        // 存在 referer 繼續檢測
        if (refer) {
            // 請求資源存在 referer,作防盜鏈處理
            let referHost = url.parse(refer).hostname;
            let host = req.headers["host"].split(":")[0];

            // 當訪問源的域和資源所在的域不是同一個域,作防盜鏈處理
            if (referHost !== host) {
                let isInWhiteList = whiteList.includes(refer);
                p = isInWhiteList ? p : path.join(static, "error.png");
            }
        }

        // 第一次訪問請求頁面 index.html,不存在 referer,將靜態資源返回
        // 第二次訪問請求圖片資源,若是 referer 和資源所本就是同一個域,直接將資源返回
        fs.createReadStream(p).pipe(res);
    } else {
        res.statusCode = 404;
        res.end("Not Found");
    }
}

server.listen(3000, () => {
    console.log("server start 3000");
});複製代碼

其實上面的服務器是 shen.companda.comlocalhost 所共用的,只是經過不一樣的域名訪問。

啓動服務器,而後經過 localhost:3000 訪問,此時因爲與 shen.companda.com 爲不一樣域,因此只有第三張圖片返回 success.png

經過 shen.com:3000 訪問,因爲存在白名單中,因此三張圖片都返回 success.png

經過 panda.com:3000 訪問,因爲 shen.com 在不一樣域,因此沒有返回 success.png

不管經過 shen.com 仍是 panda.com 訪問 localhost 的資源都是在同域的,因此都能獲取到。

總結

在上面咱們利用本地服務實現了一個最基本的防盜鏈,思路就是 referer 與資源同域,正常返回,不一樣域檢測白名單,在真實的開發場景可能會更細化,更復雜一些,其實整個防盜鏈實現的核心就是利用 HTTP 的 refererhost 請求頭作檢測,但願經過本篇的學習,你們能夠對資源防盜鏈有所瞭解,並在後面開發相似功能時提供思路。

相關文章
相關標籤/搜索