你不知道的 HTTP Referer

2023-07-27 12:13:37

前言

上週突然發現自己的自己站點的圖片全都403了,之前還是好好的,圖片咋就全都存取不了呢?由於我每次發文章都是先發了掘金,然後再從掘金拷貝到我自己的站點,這樣我就不用在自己的站點去上傳圖片了,非常方便。

啥也沒幹,圖片咋就403了呢?估計又是整了什麼開源節流,降本增效吧,說白了就是大家都用他站點的圖片導致流量費用蹭蹭蹭的往上漲,人家肯定不願意了,這下給圖片都加上防盜了,非自己的站點全都給你返回403.

防盜原理

是不是很好奇這些圖片防盜是怎麼做的?

我們可以自己來實現一下這個場景:不受信任的域名存取我伺服器上的圖片資源全都返回403

準備幾個域名

這裡沒有域名也不用擔心,我們可以直接本地模擬就行了,比如我這裡使用SwitchHosts給本地新增的三個域名並且都指向我們的本地IP

這樣的話這三個域名都能夠存取我們的本地服務了。

伺服器端邏輯

靜態資源目錄

這裡就用之前的nest服務來做演示,之前我們在這個服務上指定了靜態資源目錄

app.useStaticAssets(join(__dirname, '../static'), {
  prefix: '/static',
}); // 靜態資源

前端存取圖片

<img class="my_img" src="http://nanjiu.com:3000/static/sy.jpg" />

這裡是使用nanjiu.com代理域名來存取的,圖片能夠正常存取

防盜中介軟體

這裡我們可以來實現一個全域性中介軟體用來處理圖片的存取,當存取域名不在我們信任的白名單內直接給他返回403

// 白名單
const whiteList = ['nanjiu.com', 'fenanjiu.com']
 // 圖片防盜中介軟體
function imgMiddleware(req, res, next) {
  console.log('--req', req.headers)
  
  // 獲取資源型別
  const type = req.headers.accept || ''
  if(!type.includes('image')) {
    // 不是圖片資源,直接放行
    next()
    return
  }

  const referer = req.headers.referer || ''
  // 獲取referer的域名
  const { hostname } = url.parse(referer, true)
  if(referer && whiteList.includes(hostname) || !referer) {
    // 存取域名在白名單內,放行 !referer表示直接存取圖片(比如瀏覽器位址列輸入圖片地址)
    res.status(200)
    next() 
  }else {
    // 存取域名不在白名單內,返回403
    res.status(403)
    res.send('逮到你了,又來偷我圖片是吧!')
  }
  
}

這裡需要注意的是,全域性中介軟體在使用時一定要在useStaticAssets之前

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  
  app.setGlobalPrefix('api'); // 全域性路由字首
  app.use(cors()); // 允許跨域
  app.use(json({ limit: '10mb' })); // 允許上傳大檔案
  app.use(urlencoded({ extended: true, limit: '10mb' })); // 允許上傳大檔案
  app.use(imgMiddleware) // 圖片防盜中介軟體
  app.useStaticAssets(join(__dirname, '../static'), {
    prefix: '/static',
  }); // 靜態資源
  
  await app.listen(3000);
  console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();

這上面的程式碼中我們可以看到,現在受信任的域名就只有nanjiu.comfenanjiu.com

當前端頁面使用sy.com這個域名去存取nanjiu.com域名下的圖片時,此時應該是會進入防盜邏輯,返回403

並且送他一句

Referer

從上面我們實現的防盜原理來看,這其中最關鍵的就是referer,那麼這個referer到底是什麼呢?為什麼可以用它來做圖片防盜

Referer是什麼

MDN解釋如下:

Referer 請求頭包含了當前請求頁面的來源頁面的地址,即表示當前頁面是通過此來源頁面裡的連結進入的。伺服器端一般使用 Referer 請求頭識別存取來源,可能會以此進行統計分析、紀錄檔記錄以及快取優化等。

從這裡我們就大概能知道圖片防盜的原理了,伺服器端可以通過請求頭中的Referer來識別存取來源,然後判斷應不應該給你返回圖片

Referer這個單詞實際上是Referrer的錯誤拼寫,這其實是個歷史原因,在早期 HTTP 規範當中就存在的拼寫錯誤,後面為了向下相容,所以將錯就錯。

拼寫錯誤只有 Request HeadersReferer,在其他地方比如General HeadersJavaScriptDOM 上,都是正確的拼寫。

Referrer-Policy

Referrer-Policy 首部用來監管哪些存取來源資訊——會在 Referer 中傳送——應該被包含在生成的請求當中。

它其實是用來控制 Referer 返回的具體內容的

它有以下屬性值:

  • no-referrer: 整個 Referer 首部會被移除。存取來源資訊不隨著請求一起傳送。
  • no-referrer-when-downgrade(預設值): 在沒有指定任何策略的情況下使用者代理的預設行為。在同等安全級別的情況下,參照頁面的地址會被傳送 (HTTPS->HTTPS),但是在降級的情況下不會被傳送 (HTTPS->HTTP)。
  • origin: 在任何情況下,僅傳送檔案的源作為參照地址。例如 https://example.com/page.html 會將 https://example.com/ 作為參照地址
  • origin-when-cross-origin: 對於同源的請求,會傳送完整的 URL 作為參照地址,但是對於非同源請求僅傳送檔案的源。
  • same-origin: 對於同源的請求會傳送參照地址,但是對於非同源請求則不傳送參照地址資訊。
  • strict-origin: 在同等安全級別的情況下,傳送檔案的源作為參照地址 (HTTPS->HTTPS),但是在降級的情況下不會傳送 (HTTPS->HTTP)。
  • strict-origin-when-cross-origin: 對於同源的請求,會傳送完整的 URL 作為參照地址;在同等安全級別的情況下,傳送檔案的源作為參照地址 (HTTPS->HTTPS);在降級的情況下不傳送此首部 (HTTPS->HTTP)。
  • unsafe-url: 無論是同源請求還是非同源請求,都傳送完整的 URL(移除引數資訊之後)作為參照地址。

這麼多referrer策略,我們怎麼使用呢?

使用

meta標籤

我們可以用一個 name 為 referrermeta元素為整個檔案設定 referrer 策略

<meta name="referrer" content="no-referrer">

我的個人站點就是使用該方法來解決圖片存取403問題的,但需要注意的是,如果你為頁面設定了no-referrer策略會導致頁面上所有的請求都不會傳送referer,使用時需要自己權衡利弊。

rel屬性

可以在aarealink標籤上通過rel屬性來單獨指定referrer的策略

<a href="xxx" rel="noreferrer">新地址</a>

referrerpolicy屬性

可以在aarealinkimgiframescript標籤上通過referrerpolicy屬性來單獨指定referrer策略

<img class="my_img" referrerpolicy="no-referrer" src="http://nanjiu.com:3000/static/sy.jpg" />

比如上面例子中的這張圖片我們加上referrerpolicy="no-referrer"再去存取,頁面還是在sy.com這個域名下面

可以看到請求頭中沒有攜帶referer,所以它就能夠躲過圖片防盜邏輯。

Headers請求頭

也可以更改 HTTP 頭資訊中的 Referer-Policy 值。比如你使用的是 Nginx,則可以設定 add_headers 設定請求頭。

add_header Referrer-Policy "no-referrer";

Headers 請求頭和其它頁面元素屬性同時存在時,確定元素的有效策略時的優先順序是:

  1. 元素級策略
  2. 頁面級策略
  3. 瀏覽器預設

如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發,關注 前端南玖 第一時間獲取最新文章~