Web應用安全性: HTTP簡介
HTTP是一個美好的東西:一個存在了20多年而沒有太多變化的協(xié)議。
正如我們在前一篇文章中看到的,瀏覽器通過HTTP協(xié)議與web應用程序交互,這是我們深入研究這個主題的主要原因。如果用戶在網(wǎng)站上輸入他們的信用卡信息,黑客就能在數(shù)據(jù)到達服務器之前攔截數(shù)據(jù),我們肯定會有麻煩。
了解HTTP是如何工作的,我們如何保護客戶端和服務器之間的通信,以及該協(xié)議提供了哪些與安全相關的特性,這是改進安全狀態(tài)的首步。
但是,在討論HTTP時,我們應該始終區(qū)分語義和技術實現(xiàn),因為它們是HTTP工作方式的兩個非常不同的方面。
兩者之間的關鍵區(qū)別可以用一個非常簡單的類比來解釋:20年前,人們像現(xiàn)在一樣關心他們的親人,盡管他們互動的方式已經(jīng)發(fā)生了巨大的變化。我們的父母可能會開著車去他們姐姐家,這樣就能趕上和家人在一起。
相反,現(xiàn)在更常見的是在 WhatsApp 上留言、打電話或使用 Facebook 群組,這在以前是不可能的。這并不是說人們或多或少地交流或關心,而是說他們交流的方式改變了。
HTTP 也不例外:協(xié)議背后的語義沒有太大的變化,而客戶端和服務器之間通信的技術實現(xiàn)已經(jīng)經(jīng)過多年的優(yōu)化。如果您查看 1996 年的 HTTP 請求,它看起來與我們在前一篇文章中看到的請求非常相似,盡管這些數(shù)據(jù)包通過網(wǎng)絡的方式非常不同。
概述
如前所述,HTTP遵循請求/響應模型,其中連接到服務器的客戶端發(fā)出請求,服務器對其進行響應。
HTTP消息(請求或響應)包含多個部分:
- 請求行
- 請求頭
- 請求體
一部分:請求行,用來說明請求類型,要訪問的資源以及所使用的HTTP版本。
- GET /players/lebron-james HTTP/1.1
GET說明請求類型為 GET,/players/lebron-james 為要訪問的資源,該行的后一部分說明使用的是 HTTP1.1 版本。
第二部分:請求頭部,緊接著請求行之后的部分,用來說明服務器要使用的附加信。
- GET /players/lebron-james HTTP/1.1
- Host: nba.com
- Accept: */*
- Coolness: 9000
例如,在此請求中,客戶端已為請求附加了3個附加標頭:Host,Accept和 Coolness。
等一下,Coolness 是什么
報頭不必使用特定的保留名稱,但通常建議依賴于 HTTP 規(guī)范標準化的名稱:越偏離標準,交換中的另一方就越不理解你。
例如,Cache-Control 是一個頭文件,用于定義響應是否是可緩存的:大多數(shù)代理和反向代理都完全按照 HTTP 規(guī)范來理解它。如果將 Cache-Control 頭重命名為 Awesome-Cache-Control,代理將不再知道如何緩存響應,因為它們不是按照你剛剛提出的規(guī)范構建的。
但有時候,在消息中包含“自定義”標題可能是有意義的,因為你可能希望添加實際上不屬于 HTTP 規(guī)范的元數(shù)據(jù):服務器可以決定在其響應中包含技術信息,以便客戶端可以同時執(zhí)行請求并獲取有關回復的服務器狀態(tài)的重要信息:
- ...
- X-Cpu-Usage: 40%
- X-Memory-Available: 1%
- ...
使用自定義標頭時,始終為它們添加一個鍵,以便它們不會與將來可能成為標準的其他標頭沖突:從歷史上看,這一直很有效,直到每個人都開始使用“非標準” X 前綴 反過來,這成為常態(tài)。 X-Forwarded-For 和 X-Forwarded-Proto標 頭是負載平衡器和代理廣泛使用和理解的自定義標頭的示例,即使它們不是 HTTP 標準的一部分。
如果你需要添加自己的自定義頭,那么現(xiàn)在通常使用一個自動生成的前綴,例如 Acme-Custom-Header 頭或 A-Custom-Header 頭。
在標題之后,一個請求可能包含一個主體,它與標題之間用空行隔開:
- POST /players/lebron-james/comments HTTP/1.1
- Host: nba.com
- Accept: */*
- Coolness: 9000
- Best Player Ever
我們的請求完成了:首行(位置和協(xié)議信息)、請求頭和請求體。注意,請求體是完全可選的,在大多數(shù)情況下,它只在我們想要向服務器發(fā)送數(shù)據(jù)時使用——這就是上面的示例使用 POST 的原因。
響應沒有太大的不同:
- HTTP/1.1 200 OK
- Content-Type: application/json
- Cache-Control: private, max-age=3600
- {"name": "Lebron James", "birthplace": "Akron, Ohio", ...}
響應發(fā)布的一個信息是它使用的協(xié)議版本以及該響應的狀態(tài)。請求頭也一樣,如果需要的話,在正文后面加一個換行符。
如前所述,該協(xié)議經(jīng)過了多次修訂,并隨著時間的推移添加了一些特性(新的頭文件、狀態(tài)代碼等),但是底層結構并沒有太大的變化(請求行、請求頭和正文)。真正改變的是客戶端和服務器如何交換這些消息——讓我們更仔細地研究一下。
HTTP vs HTTPS vs H2
HTTP 已經(jīng)經(jīng)歷了 2 個相當大的語義變化: HTTP/1.0 和 HTTP/1.1。
那,“HTTPS 和 HTTP2 在哪里?”
HTTPS 和 HTTP2 (縮寫為 H2)是更多的技術更改,因為它們引入了在互聯(lián)網(wǎng)上傳遞消息的新方法,而不會嚴重影響協(xié)議的語義。
HTTPS 是 HTTP的一種“安全”擴展,它涉及在客戶機和服務器之間建立一個公共秘密,確保我們與正確的一方進行通信,并對與公共秘密交換的消息進行加密(稍后將對此進行詳細介紹)。HTTPS 的目標是提高協(xié)議HTTP 的安全性,而 H2 的目標是為其帶來更快的速度。
H2 使用二進制而不是純文本消息,支持多路復用,使用 HPACK 算法壓縮報頭……長話短說,H2 是對HTTP/1.1 的性能提升。
網(wǎng)站所有者不愿意切換到 HTTPS,因為它涉及客戶端和服務器之間的額外往返(如上所述,需要在兩方之間建立共同的秘密),從而減慢用戶體驗:使用 H2 加密 默認情況下,他們就沒有借口了,因為多路復用和服務器推送等功能使其 性能優(yōu)于普通的 HTTP/1.1。
HTTPS
HTTPS的目標是讓客戶端和服務器通過 TLS(傳輸層安全性)安全地進行通信,TLS 是SSL(安全套接字層)的繼承者。
TLS 所針對的問題相當簡單,可以用一個簡單的比喻:你的另一半中午打電話給你,當你在一個會議上,并詢問你告訴他們你的網(wǎng)上銀行賬戶的密碼,因為他們需要執(zhí)行一個銀行轉賬,以確保你兒子的教育費用按時支付。重要的是你現(xiàn)在就告訴他們,否則第二天早上你的孩子可能會被學校拒之門外。
你們現(xiàn)在面臨著兩個挑戰(zhàn):
- 身份驗證: 確保你真的在和你的另一半說話,因為有可能別人會假裝他們
- 加密: 在同事無法理解和記錄下密碼的情況下進行通信
這正是 HTTPS 試圖解決的問題。
為了驗證你正在與誰交談,HTTPS 使用公鑰證書,這只是聲明特定服務器背后身份的證書:當你通過 HTTPS 連接到 IP 地址時,該地址背后的服務器將向你提供其證書,以驗證其身份?;氐轿覀兊念惐?,這可能只是你讓你的另一半拼寫他們的社會保險號。一旦驗證了數(shù)字的正確性,你就獲得了額外的信任級別。
但是,這并不能阻止黑客學習受害者的社會安全號碼,偷走你伴侶的智能手機并給你打電話。 我們如何驗證來電者的身份?
你不是直接讓你的另一半拼他們的社會保險號,而是打電話給你的媽媽(她正好住在你隔壁),讓她去你的公寓,確保你的另一半拼的是他們的社會保險號。這增加了額外的信任級別,因為你不認為你的母親是一個威脅,并依賴她來驗證調用者的身份。
在 HTTPS 術語中,你的媽媽稱為 CA,證書頒發(fā)機構 (Certificate Authority)的簡稱:CA 的工作是驗證特定服務器后面的身份,并頒發(fā)具有自己的數(shù)字簽名的證書:這意味著,當我連接到特定域時,我不會出示由域所有者生成的證書(稱為自簽名證書),而是由 CA 頒發(fā)。
權威機構的職責是確保他們驗證域名后面的身份并相應地頒發(fā)證書:當你“訂購”證書時(通常稱為 SSL 證書,即使現(xiàn)在使用 TLS 代替 ), 當局可能會給人打電話或要求你更改 DNS 設置,以驗證你是否可以控制相關域。 驗證過程完成后,它將頒發(fā)證書,然后你可以在 Web 服務器上安裝該證書。
像瀏覽器這樣的客戶端將連接到您的服務器并獲得此證書,以便他們可以驗證它看起來是真實的:瀏覽器與CA有某種“關系”,因為它們跟蹤可信CA的列表。 為了驗證證書是否真的值得信賴。 如果證書未由受信任的機構簽名,則瀏覽器將向用戶顯示一條信息量大的警告:
確保你和你的另一半之間的通信安全已經(jīng)完成了一半:現(xiàn)在我們已經(jīng)解決了身份驗證(驗證調用者的身份),我們需要確保我們可以安全地通信,而不會在此過程中被其他人竊聽。正如我提到的,你正在開會,需要拼寫你的網(wǎng)上銀行密碼。你需要找到一種方法來加密你的交流,這樣只有你和你的伴侶才能理解你的談話。
您可以通過在雙方之間建立共享密鑰來實現(xiàn)此目的,并通過該密鑰加密消息:例如,你可以根據(jù)婚禮日期決定使用 Caesar cipher 的變體。
如果雙方都有一段穩(wěn)定的關系,就像你和你的靈魂伴侶一樣,這將會很有效,因為他們可以在別人不知道的共同記憶的基礎上創(chuàng)造一個密鑰。但是,瀏覽器和服務器不能使用相同的機制,因為它們事先不了解彼此。
取而代之的是 Diffie-Hellman 密鑰交換協(xié)議的變體,它確保沒有預先知道的各方建立共享的密鑰,而其他人無法“嗅探”它。這需要用到一點數(shù)學知識,這是留給讀者的一個練習。
一旦密鑰建立起來,客戶端和服務器就可以進行通信,而不必擔心有人會截獲它們的消息。即使黑客這樣做,他們也沒有解密消息所需的公共密鑰。
HTTPS無處不在
還在爭論你是否應該在你的網(wǎng)站上支持HTTPS? 我沒有好消息:瀏覽器已經(jīng)開始推動用戶遠離不支持HTTPS 的網(wǎng)站,以“強迫”網(wǎng)絡開發(fā)者提供完全加密的瀏覽體驗。
在 “HTTPS無處不在” 的口號背后,瀏覽器開始反對未加密的連接——谷歌宣布從 Chrome 68(2018年7月) 開始將把HTTP網(wǎng)站標記為“不安全”:
對于不使用HTTPS的網(wǎng)站來說,更令人擔憂的是,一旦用戶在網(wǎng)頁上輸入任何內容,“不安全”標簽就會變成紅色——這一舉動應該會鼓勵用戶在與不支持HTTPS的網(wǎng)站交換數(shù)據(jù)之前三思而后行。
將此與在HTTPS上運行并配備有效證書的網(wǎng)站的外觀進行比較:
從理論上講,網(wǎng)站不一定是安全的,但在實踐中,這會嚇跑用戶 - 這是理所當然的。 當 H2 還沒普遍時,堅持使用未加密的HTTP通信是有意義的,如今幾乎沒有理由這樣做。
GET 和 POST
正如我們前面看到的,HTTP請求以一個特殊的請求行開始:
首先,客戶端告訴服務器它正在使用什么動詞來執(zhí)行請求:常見的 HTTP 動詞包括 GET,POST,PUT 和 DELETE,但列表可以繼續(xù)使用不常見(但仍然是標準的)動詞,如 TRACE, OPTIONS,或 HEAD。
理論上,沒有一種方法比其他方法更安全;實際上,事情并沒有那么簡單。
GET 請求通常不帶主體,因此參數(shù)包含在 URL 中(如 www.example.com/articles?article_id=1),而 POST 請求通常用于發(fā)送(“post”)包含在內的數(shù)據(jù)。
另一個區(qū)別在于這些動詞帶有的副作用:GET 是一個冪等動詞,意思是無論你要發(fā)送多少個請求,你都不會改變網(wǎng)絡服務器的狀態(tài)。 相反,POST 不是冪等的:對于你發(fā)送的每個請求,你可能正在更改服務器的狀態(tài)(例如,考慮發(fā)布新的付款 - 現(xiàn)在您可能理解為什么站點要求你在執(zhí)行時不刷新頁面 交易)。
冪等性:指一次和多次請求某一個資源應該具有同樣的副作用,也就是一次訪問與多次訪問,對這個資源帶來的變化是相同的。
為了說明這些方法之間的一個重要區(qū)別,我們需要看一看 web 服務器的日志,這些日志你可能已經(jīng)很熟悉了:
- 192.168.99.1 - [192.168.99.1] - - [29/Jul/2018:00:39:47 +0000] "GET /?token=1234 HTTP/1.1" 200 525 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" 404 0.002 [example-local] 172.17.0.8:9090 525 0.002 200
- 192.168.99.1 - [192.168.99.1] - - [29/Jul/2018:00:40:47 +0000] "GET / HTTP/1.1" 200 525 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" 393 0.004 [example-local] 172.17.0.8:9090 525 0.004 200
- 192.168.99.1 - [192.168.99.1] - - [29/Jul/2018:00:41:34 +0000] "PUT /users HTTP/1.1" 201 23 "http://example.local/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" 4878 0.016 [example-local] 172.17.0.8:9090 23 0.016 201
如你所見,web服務器記錄請求路徑:這意味著,如果你在 URL 中包含敏感數(shù)據(jù),那么它將被 web 服務器泄露并保存在你的日志中的某個位置—你的密鑰將以明文的形式出現(xiàn),這是我們絕對需要避免的。假設黑客能夠訪問你的一個舊日志文件,該文件可能包含信用卡信息、私有服務的訪問令牌等等:這將是一場徹底的災難。
Web 服務器不記 錄HTTP標頭或主體,因為要保存的數(shù)據(jù)太大 - 這就是為什么通過請求主體而不是URL發(fā)送信息通常更安全。 從這里我們可以得出 POST(和類似的,非冪等方法)比 GET 更安全,即使更多的是使用特定動詞時數(shù)據(jù)的發(fā)送方式而不是特定動詞本身比其他動詞更安全:如果你 將敏感信息包含在 GET 請求的主體中,然后你不會遇到比使用 POST 時更多的問題,即使這種方法被認為是不尋常的。
我們信任 HTTP 報頭
在本文中,我們研究了HTTP,它的演變以及它的安全擴展如何集成身份驗證和加密,以使客戶端和服務器通過安全通道進行通信:這不是所有 HTTP 在安全性方面提供的。
正如我們將在下一篇文章中看到的,HTTP安全頭文件提供了一種改進應用程序安全狀態(tài)的方法,下一篇文章將致力于理解如何利用它們。