HTTP 規範中的那些暗坑

来源:https://www.cnblogs.com/skychx/archive/2020/05/28/HTTP-Rules.html
-Advertisement-
Play Games

HTTP 雖然有諸多的優點,但是在協議定義時因為諸多的博弈和限制,還是隱藏了不少暗坑,讓人一不小心就會陷入其中。本文總結了 HTTP 規範中常見的幾個暗坑,希望大家開發中有意識的規避它們,提升開發體驗。 ...


HTTP 協議可以說是開發者最熟悉的一個網路協議,「簡單易懂」和「易於擴展」兩個特點讓它成為應用最廣泛的應用層協議。

雖然有諸多的優點,但是在協議定義時因為諸多的博弈和限制,還是隱藏了不少暗坑,讓人一不小心就會陷入其中。本文總結了 HTTP 規範中常見的幾個暗坑,希望大家開發中有意識的規避它們,提升開發體驗。

1.Referer

HTTP 標準把 Referrer 寫成 Referer(少些了一個 r),可以說是電腦歷史上最著名的一個錯別字了。

Referer 的主要作用是攜帶當前請求的來源地址,常用在反爬蟲和防盜鏈上。前段時間鬧的沸沸揚揚的新浪圖床掛圖事件,就是因為新浪圖床突然開始檢查 HTTP Referer 頭,非新浪功能變數名稱就不返回圖片,導致很多蹭流量的中小博客圖都掛了。

雖然 HTTP 標準里把 Referer 寫錯了,但是其它可以控制 Referer 的標準並沒有將錯就錯。

例如禁止網頁自動攜帶 Referer 頭的 <meta> 標簽,相關關鍵字拼寫就是正確的:

<!-- 全局禁止發送 referrer -->
<meta name="referrer" content="no-referrer" />

還有一個值得註意的是瀏覽器的網路請求。從安全性和穩定性上考慮,Referer 等請求頭在網路請求時,只能由瀏覽器控制,不能直接操作,我們只能通過一些屬性進行控制。比如說 Fetch 函數,我們可以通過 referrerreferrerPolicy 控制,而它們的拼寫也是正確的:

fetch('/page', {
  headers: {
    "Content-Type""text/plain;charset=UTF-8"
  },
  referrer"https://demo.com/anotherpage"// <-
  referrerPolicy: "no-referrer-when-downgrade"// <-
});

一句話總結:

凡是涉及到 Referrer 的,除了 HTTP 欄位是錯的,瀏覽器的相關配置欄位拼寫都是正確的。

二.「靈異」的空格

1.%20 還是 +

這個是個史詩級的大坑,我曾經被這個協議衝突坑了一天。

開始講解前先看個小測試,在瀏覽器里輸入 blank testblanktest 間有個空格),我們看看瀏覽器如何處理的:

從動圖可以看出瀏覽器把空格解析為一個加號「+」。

是不是感覺有些奇怪?我們再做個測試,用瀏覽器提供的幾個函數試一下:

encodeURIComponent("blank test"// "blank%20test"
encodeURI("q=blank test")        // "q=blank%20test"
new URLSearchParams("q=blank test").toString() // "q=blank+test"

代碼是不會說謊的,其實上面的結果都是正確的,encode 結果不一樣,是因為 URI 規範W3C 規範衝突了,才會搞出這種讓人疑惑的烏龍事件。

2.衝突的協議

我們首先看看 URI 中的保留字,這些保留字不參與編碼。保留字元一共有兩大類:

  • gen-delims:: / ? # [ ] @
  • sub-delims:! $ & ' ( ) * + , ; =

URI 的編碼規則也很簡單,先把非限定範圍的字元轉為 16 進位,然後前面加百分號。

空格這種不安全字元轉為十六進位就是 0x20,前面再加上百分號 % 就是 %20

所以這時候再看 encodeURIComponentencodeURI 的編碼結果,就是完全正確的。

既然空格轉為%20 是正確的,那轉為 + 是怎麼回事?這時候我們就要瞭解一下 HTML form 表單的歷史。

早期的網頁沒有 AJAX 的時候,提交數據都是通過 HTML 的 form 表單。form 表單的提交方法可以用 GET 也可以用 POST,大家可以在 MDN form 詞條上測試:

經過測試我們可以看出表單提交的內容中,空格都是轉為加號的,這種編碼類型就是 application/x-www-form-urlencoded,在 WHATWG 規範里是這樣定義的:

到這裡基本上就破案了,URLSearchParams 做 encode 的時候,就按這個規範來的。我找到了 URLSearchParamsPolyfill 代碼,裡面就做了 %20+ 的映射:

replace = {
    '!''%21',
    "'"'%27',
    '(''%28',
    ')''%29',
    '~''%7E',
    '%20''+'// <= 就是這個
    '%00''\x00'
}

規範里對這個編碼類型還有解釋說明:

The application/x-www-form-urlencoded format is in many ways an aberrant monstrosity, the result of many years of implementation accidents and compromises leading to a set of requirements necessary for interoperability, but in no way representing good design practices. In particular, readers are cautioned to pay close attention to the twisted details involving repeated (and in some cases nested) conversions between character encodings and byte sequences. Unfortunately the format is in widespread use due to the prevalence of HTML forms.

這種編碼方式就不是個好的設計,不幸的是隨著 HTML form 表單的普及,這種格式已經推廣開了

其實上面一大段句話就是一個意思:這玩意兒設計的就是

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 學習目標:能夠說出為什麼需要對象 能夠使用字面量創建對象 能夠使用構造函數創建對象 能夠說出new的執行過程 能夠遍歷對象 1.對象 1.1萬物皆對象,對象是一個具體的事物 在程式裡面,一個伺服器,一張網頁,一個遠程伺服器的連接也可以是對象。 Javascript中對象是一組無序的相關屬性和方法的集 ...
  • <script> //數組排序(冒泡排序) //冒泡排序是一種演算法,把一系列的數據按照一定的循序進行排列顯示(從小到大或從大到小) //把數組[5,4,3,2,1]換成[1,2,3,4,5] var arr = [5,4,3,2,1]; var temp; for(i=0;i<arr.length- ...
  • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document< ...
  • 摘要 在之前的文章中有講過保留兩位小數的實現,最近在開發在開發活動頁面時,依舊是展示參賽作品的點贊數。當點贊數過萬時,顯示三位有效數字來展示點贊數,例如:1.56萬、23.1萬等。經過探索,找到了toPrecision() 方法,在MDN中是這樣概述該方法的:toPrecision() 方法以指定的 ...
  • display: contents 是一個比較陌生的屬性,雖然屬於 display 這個基本上是最常見的 CSS 屬性,但是 contents 這個取值基本不會用到。但是它早在 2016 年就已經得到了 Firefox 的支持。 本文將深入一下這個有意思的屬性值。 基本用法 根據 W3C 對 dis ...
  • 前言 在 JavaScript 的學習過程中,我們可能或多或少地接觸過高階函數。那麼,我們自己對此是否有一個明確的定義,或者說很熟練的掌握這些用法呢 簡單來說,高階函數是一個函數,它接收函數作為參數或將函數作為輸出返回 看到這樣的概念,在你的腦海中會出現哪些函數呢 其實,像我們經常會使用到的一些數組 ...
  • Js--使用sort根據數組中對象的某一個屬性值進行排序 博客說明 文章所涉及的資料來自互聯網整理和個人總結,意在於個人學習和經驗彙總,如有什麼地方侵權,請聯繫本人刪除,謝謝! 說明 在開發的時候時常會遇到這樣的問題 思路 c = [{create_time: "Mon, 25 May 2020 0 ...
  • Js--將兩個數組合併 博客說明 文章所涉及的資料來自互聯網整理和個人總結,意在於個人學習和經驗彙總,如有什麼地方侵權,請聯繫本人刪除,謝謝! 方法一 使用concat var a = [1,2,3]; var b = [4,5,6]; var c = a.concat(b); //c=[1,2,3 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...