伺服器作防盜鏈圖片中轉,Node.js 上手專案簡明教程

LeanCloud發表於2019-03-04

文/李信棟
前幾天隨手寫的 chrome 外掛遇到了防盜鏈問題,由於外掛不能用 js iframe 的方法反防盜鏈,於是想用伺服器做箇中轉。記錄一下上手專案的各個點,以後再用 nodejs 就不用到處查資料了。

之前沒有一套特別熟悉的 web 開發框架,加上外掛儲存服務依賴的平臺 LeanCloud 剛好支援部署 nodejs 網站,剛好拿這個小專案作為 nodejs 上手專案。

怎麼「破解防盜鏈」呢?
想要破解,就得先知道目標——防盜鏈如何實現。
大多數站點的策略很簡單: 判斷request請求頭的refer是否來源於本站。若不是,拒絕訪問真實圖片。而我們知道: 請求頭是來自於客戶端,是可偽造的。

思路
那麼,我們偽造一個正確的refer來訪問不就行了?
整個業務邏輯大概像這樣:

  • 自己的伺服器後臺接受帶目標圖片url引數的請求
  • 偽造refer請求目標圖片
  • 把請求到的資料作為response返回

這就起到了圖片中轉的作用。

1. 專案是什麼樣子

1.1 介面的樣子?

1.2 應該怎麼做?

  • 把伺服器跑起來
  • 處理 GET 請求
  • 分析請求引數
  • 下載原圖
  • response 原圖

2. 學習路徑(在對目標未知的前提下提出疑問)

  • 如何開始,建立伺服器
  • 如何處理基本請求 GET POST
  • 如何下載圖片並轉發
  • 完成基本功能,上線
  • 優化

2.1 如何開始,建立伺服器

主要是 http.createServer().listen(port) 這組方法,建立伺服器、監聽埠一鍵搞定。

var http = require(`http`);

http.createServer(function (request, response) {
     // do things here
}).listen(8888);

console.log(`Server running at: 8888`);複製程式碼

2.2 如何處理基本請求 GET POST

createServer 回撥方法的兩個引數 req res 是 http request 和 response 的內容,列印一下他們的內容。
request 是 InComingMessage 類,列印它的 url 欄位。

var http = require(`http`);
var url = require(`url`);
var util = require(`util`);
http.createServer(function(req, res){
    res.writeHead(200, {`Content-Type`: `text/plain`});
    res.end(util.inspect(url.parse(req.url, true)));
}).listen(3000);複製程式碼

請求
http://localhost:3000/api?url=http://abc.com/image.png

請求結果

Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: `?url=http://abc.com/image.png`,
  query: { url: `http://abc.com/image.png` },
  pathname: `/api`,
  path: `/api?url=http://abc.com/image.png`,
  href: `/api?url=http://abc.com/image.png` }複製程式碼

query 欄位剛好是我們想要的內容,下載這個欄位對應的圖片。

2.3 如何下載圖片並轉發

request 模組支援管道方法,可以和 shell 的管道一樣理解。

這可以省很多事,不需要在本地儲存圖片,不需要處理雜七雜八的事情,甚至不需要再去了解 nodejs 的流。一個方法全搞定。

關鍵方法: request(options).pipe(res)

    var options = {
      uri: imgUrl, // 這個 uri 為空時,會認為該欄位不存在,報異常
      headers: {
         `Referer`: referrer // 解決部分防盜鏈選項
      }
    };
    request(options).pipe(res);複製程式碼

2.4 完成基本功能,上線

專案地址

完整程式碼

   `use strict`;
    var router = require(`express`).Router();
    var http = require(`http`);
    var url = require(`url`);
    var util = require(`util`);
    var fs = require(`fs`);
    var callfile = require(`child_process`);
    var request = require(`request`);

    router.get(`/`, function(req, res, next) {
        var imgUrl = url.parse(req.url, true).query.url;
        console.log(url.parse(req.url,true).query); 

        console.log(`get a request for ` + imgUrl);
        if (imgUrl == null || imgUrl == "" || imgUrl == undefined) {
            console.log(`end`);
            res.end();
            return;
        }

        var parsedUrl = url.parse(imgUrl);
        // 這裡暫時使用圖片伺服器主機名做Referer
        var referrer = parsedUrl.protocol + `//` + parsedUrl.host; 
        console.log(`referrer ` + referrer);

        var options = {
          uri: imgUrl,
          headers: {
             `Referer`: referrer
          }
        };

        function callback(error, response, body) {
          if (!error && response.statusCode == 200) {
            console.log("type " + response.headers[`content-type`]);
          }
          res.end(response.body);
        }

        // request(options, callback);
        request(options)
            .on(`error`, function(err) {
                console.log(err)
            })
            .pipe(res);
    });

    module.exports = router;複製程式碼

2.5 優化

這部分主要是防盜鏈部分的優化。

單就 Referer 來說,使用空值和主機名都只能滿足部分需求。

一個優化方式是組合,當一種方式不能突破即採用另一種方式。
這種方式的有點在於擴大了適用面積,並且方法對任何場景比較通用。

一個優化方式是介面請求引數帶源引用連線。這種方式對很多人來說不太通用,因為很多場景下並不清楚源引用連線在哪。但是對我的外掛來說非常適用,外掛本身保留了源引用。因此可以很好的繞過防盜鏈限制。

相關文章