如果不用HTTPS,HTTP協議如何安全的傳輸密碼資訊?
HTTP協議是純文字協議,沒有任何加密措施。通過HTTP協議傳輸的資料都可以在網路上被完全監聽。如果使用者登陸時將使用者名稱和密碼直接明文通過HTTP協議傳輸過去了,那麼密碼可能會被黑客竊取。 一種方法是使用非對稱加密。GET登陸頁面時,將公鑰以Javascript變數的形式暴露給瀏覽器。然後用公鑰對使用者的密碼加密後,再將密碼密文、使用者名稱和公鑰一起傳送給伺服器。伺服器會提前儲存公鑰和私鑰的對映資訊,通過客戶端發過來的公鑰就可以查出對應的私鑰,然後對密碼密文進行解密就可以還原出密碼的明文。 為了加強公鑰私鑰的安全性,伺服器應該動態生成公鑰私鑰對,並且使用後立即銷燬。但是動態生成又是非常耗費計算資源的,所以一般伺服器會選擇Pool方法提供有限數量的公鑰私鑰對池,然後每隔一段時間重新整理一次Pool。
檔案路徑攻擊
很多作業系統都會使用..符號表示上層目錄。如果黑客在URL的路徑裡面使用..符號引用上層目錄,而伺服器沒有做好防範的話就有可能導致黑客可以直接訪問許可權之外的檔案。比如使用多級..符號就可以引用到根目錄,進一步就可以訪問任意檔案。 所以很多伺服器都禁止在URL路徑裡出現..符號以避免被攻擊。 檔案路徑攻擊也是很多黑客非常喜愛使用的攻擊方法之一。如果你的伺服器有一定的訪問量,開啟你的nginx日誌,你就會偶爾發現有一些奇怪的URL裡面有一堆..符號,這種URL的出現就表示網路上的黑客正在嘗試攻擊你的伺服器。
DNS欺騙
HTTP協議嚴重依賴於DNS域名解析。任意一個域名類網址的訪問都需要經過域名解析的過程得到目標服務的IP地址才能成功繼續下去。 如果掌管DNS服務的運營商作惡將域名解析到不正確的IP,指向一個釣魚的網頁服務。使用者如果沒有覺察,就可能會將自己的敏感資訊提交給冒牌的伺服器。
謹慎使用外部的HTTP代理
HTTP代理作為客戶端到伺服器之間的中間路由節點,它起到傳話人和翻譯官的角色。 如果這個翻譯官不靠譜的話,客戶端是會拿到錯誤的返回資料的。它同DNS欺騙一樣,是可以對客戶端進行釣魚攻擊的。 如果這個翻譯官口風不嚴的話,它可能會將它聽到的敏感資訊洩露給別人。
413 Request Entity Too Large
客戶端上傳圖片太大超過伺服器限制時,伺服器返回413錯誤。
414 Request-URI Too Long
客戶端訪問的URI太長,超出了伺服器允許限制,伺服器返回414錯誤。
202 Accepted
常用於非同步請求。客戶端傳送請求到伺服器,伺服器立即返回一個202 Accepted表示已經成功接收到客戶端的請求。 後面怎麼處理由伺服器自己決定,一般伺服器會給客戶端預留一個可以查詢處理狀態的介面,客戶端可以選擇輪訓該介面來知道請求的處理進度和結果。
POST提交資料的方式
application/x-www-form-urlencoded
提交資料表單時經常使用,Body內部存放的是轉碼後的鍵值對。
POST http://xyz.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
a=1&b=2&c=3&c=4
複製程式碼
application/json
提交結構化表單時使用,Body內部存放的是JSON字串。ElasticSearch的查詢協議使用的是這種方式。
POST http://xyz.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{"a": 1, "b": 2, "c": [3, 4]}
複製程式碼
multipart/form-data
上傳檔案時經常使用。這種格式比較複雜,它是為了支援多檔案上傳混合表單資料而設計的一種特殊的格式。
<form action="http://example.com/upload" method="post" enctype="multipart/form-data">
<p><input type="text" name="key1" value="value1">
<p><input type="text" name="key2" value="value2">
<p><input type="file" name="file1">
<p><input type="file" name="file2">
<p><button type="submit">Submit</button>
</form>
複製程式碼
使用者填充了表單設定了待上傳的檔案,點選Submit,傳輸資料大致如下
POST /upload HTTP/1.1
Content-Length:xxxxx
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryKOThiwE6HubGib7j
Host:example.com
------WebKitFormBoundaryKOThiwE6HubGib7j
Content-Disposition: form-data; name="key1"
value1
------WebKitFormBoundaryKOThiwE6HubGib7j
Content-Disposition: form-data; name="key2"
value2
------WebKitFormBoundaryKOThiwE6HubGib7j
Content-Disposition: form-data; name="file1"; filename="file1name.png"
Content-Type: image/png
file1 content here
------WebKitFormBoundaryKOThiwE6HubGib7j
Content-Disposition: form-data; name="file2"; filename="file2name.jpeg"
Content-Type: image/jpeg
file2 content here
------WebKitFormBoundaryKOThiwE6HubGib7j--
複製程式碼
Cookie
瀏覽器請求的Cookie中往往會攜帶敏感資訊。伺服器一般會將當前使用者的會話ID存在cookie裡,會話的具體內容存在伺服器端,會話的內容很敏感。
瀏覽器請求時會攜帶Cookie資訊,伺服器根據Cookie資訊中的會話ID找到對應的會話內容。會話內容裡可能儲存了使用者的許可權資訊,拿到這部分許可權資訊後就可能隨意控制修改使用者的資料。
因為HTTP協議的不安全性,請求資料包很容易被竊聽,Cookie中的會話資訊很容易被盜。解決方案之一就是在會話中記錄使用者的終端資訊和IP地址資訊,如果這些資訊突然發生改變,需要強制使用者重新認證。
不過高階的黑客是可以偽造出和使用者真實請求一摸一樣的資料包的。最徹底的解決方案還是採用HTTPS協議。
普通的Cookie資訊可以通過Javascript指令碼獲取到。如果黑客通過某種方式在網頁中植入不安全的指令碼,將使用者的Cookie拿到然後傳送到遠端的第三方伺服器中,那麼Cookie中的資訊就被洩露了。
Cookie的兩個重要屬性
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
複製程式碼
被標記為Secure的Cookie資訊在HTTP請求中不會被傳送,它只會在HTTPS請求中傳送,避免資料被洩露。
被標記為HttpOnly的Cookie資訊是無法通過Javascript API獲取到的,它只會在請求中傳送。這樣可以避免黑客通過網頁尾本方式竊取Cookie中的敏感資訊。
Cookie(甜點)如此好吃,黑客們總想通過Cookie做各種文章。
CSRF(Cross-Site Request Forgery)
CSRF跨站請求偽造有很多別名,比如One-Click Attack(一鍵攻擊),比如Session Riding(搭便車攻擊)
假設在在一個社群部落格網站中,刪除個人的文章只需要一個URL就可以,Cookie中的會話許可權資訊會自動附加到請求上。
# 123456為文章的ID
http://example.com/blog/123456/delete
複製程式碼
那麼當別人偽造了一個上面的連結地址誘惑你去點選,比如通過站內信件、私聊、部落格評論、圖片連結或者在別的什麼網站上隨機制造的一個連結。你不經意點了一下,就丟了你的文章。所以它被稱為一鍵攻擊。因為這是借用了你當前登陸的會話資訊來搞事,所以也被稱為搭便車攻擊。
如果在一個金融系統中,轉賬要是也可以通過一個簡單的URL進行的話,那這種危險就非同小可。
這就要求修改性的操作務必不得使用簡單的GET請求進行處理。但是即使這種情況下你改成了POST請求,黑客依然有辦法偽造請求,那就是通過iframe。
黑客在別的什麼網站上偽造了一個POST表單,誘惑你去submit。如果只是普通的內嵌進HTML網頁的表單,使用者提交時會出現跨域問題。因為當前網站的域名和表單提交的目標域名不一致。但是如果通過iframe來內嵌表單,則可以繞過跨域的問題,而使用者卻完全沒有任何覺察。
為了防範CSRF攻擊,聰明的網站的POST表單裡都會帶上CSRF_TOKEN這個隱藏欄位。CSRF_TOKEN是根據使用者的會話資訊生成的。當表單提交時,會將token和使用者的會話資訊做比對。如果匹配就是有效的提交請求。
<form method="POST" action="/blog/delete">
<label for="blog_id">部落格ID</label>
<input type="text" name="blog_id" value="12345">
<input type="hidden" name="csrf_token" value="xxxxxxxxxxxx">
</form>
複製程式碼
黑客必須拿到CSRF_TOKEN才可以借用使用者的會話資訊實施CSRF攻擊,但是CSRF_TOKEN又必須由使用者的會話資訊才可以生成。黑客沒有使用者的會話資訊,從而無法實施CSRF攻擊。
XSS(Cross Site Scripting)
如果黑客可以在你的網頁中植入任意Javascript指令碼,那他就可以隨意魚肉你的賬戶。通過Javascript可以獲取Cookie的資訊,可以借用你的會話去呼叫一些隱祕的API,而這一些行為都是在偷偷的進行,你根本完全不知道。
<div>
# 使用者內容Start
<script>send_to_hacker(document.cookie)</script>
# 使用者內容END
</div>
複製程式碼
這類攻擊在一些UGC網站中非常常見,常見的部落格類網站就是UGC網站,使用者可以通過編輯內容來生成網頁。
黑客也是使用者。他可以編輯一段Javascript指令碼作為內容提交上去。如果伺服器沒有做好防範,這段指令碼就會在生成的網頁中執行起來。當其它使用者在登陸的狀態下來瀏覽這個網頁的時候,就悲劇了。
防範XSS一般是通過對輸出的內容進行內容替換做到的。在HTML頁面中不同的位置會有不同的內容替換規則。 比較常見的是使用HTML entity編碼將HTML標籤之間的內容中的一些特殊的字元進行轉碼。
<div>
# safe now
<script>send_to_hacker(document.cookie)</script>
</div>
複製程式碼
還有些UGC內容在HTML標籤的屬性中、Javascript的變數中、URL、css程式碼中,他們轉碼的規則並不一樣,具體方法可以去Google相關文件。
跨域
跨域是個很頭痛的問題。
當你有多個後端服務,但是隻有一個前端的時候,你想做前後端分離,就會遇到跨域問題。你發現你的前端js呼叫後端服務時控制檯告訴你不ok。然後只好把這些服務都掛在了同一個nginx域名下面,通過url字首區分。
這時候你會想,跨域太TM討厭了。既然跨域這麼討厭,那為什麼瀏覽器非要限制跨域呢?
還是安全原因。
讓我們回到上文的搭便車攻擊(Session Riding),也就是騎著別人的會話來搞事情。
假設現在你的瀏覽器開了一個站點A,登陸了進去,於是cookie便記錄了會話id。 然後你又不小心開了另一個站點B,這個站點頁面一開啟就開始執行一些惡意程式碼。這些程式碼的邏輯是呼叫站點A的API來獲取站點A的資料,因為可以騎著(Ride)站點A的會話cookie。而這些資料正好是使用者私密性的。於是使用者在站點A上的私有資訊就被站點B上的程式碼竊走了。這就是跨域的風險。
但是有時候我們又希望共享資料給不同的站點,該怎麼辦呢?
答案是JSONP & CORS
JSONP(JSON Padding)
JSONP通過HTML的script標記實現了跨域共享資料的方式。JSONP通過在網頁裡定義一個回撥方法,然後在頁面上插入一個動態script標籤,指向目標呼叫地址。伺服器會返回一段javascript程式碼,一般是some_callback(data)
這種形式的回撥。該段程式碼會在瀏覽器裡自動執行,於是網頁就得到了跨域伺服器返回的資料。
<script>
function some_callback(data) {
console.log(data)
}
</script>
<script src="http://example.com/someapi?callback=some_callback"></script>
複製程式碼
因為JSONP是不攜帶cookie資訊的,所以能有效避免搭便車攻擊。JSONP是否可以獲取到資料還需要伺服器對這種呼叫提供顯示支援,伺服器必須將資料以javascript程式碼的形式返回才可以傳遞給瀏覽器。
CORS(Cross-Origin Resource Sharing)
JSONP的不足在於它只能傳送GET請求,並且不能攜帶cookie。而CORS則可以傳送任意型別的請求,可以選擇性攜帶cookie。
CORS是通過Ajax傳送的跨域請求技術。CORS的請求分為兩種,一種是簡單請求,一種是複雜請求。簡單請求就是頭部很少很簡單的GET/HEAD/POST請求。複雜請求就是非簡單請求。
瀏覽器發現Ajax的請求是跨域的,就會在請求頭新增一個Origin引數,指明當前請求的發起站點來源。伺服器根據Origin引數來決定是否授權。
如果是簡單請求,Ajax直接請求伺服器。伺服器會當成普通的請求直接返回內容,不同的是還會在響應頭部新增幾個重要的頭部,其中最重要的頭部是Access-Control-Allow-Origin: http://example.com
。
瀏覽器如果在響應中沒有讀到這個頭部,就會通知Ajax請求失敗。雖然伺服器返回了資料,瀏覽器也不讓指令碼讀到資料,這就保證了跨域的安全。伺服器就是通過請求的Origin引數來決定要不要響應Access-Control-Allow-Origin頭部來決定是否允許指定網站的跨域請求。
如果是複雜請求,要走一個預檢的流程。預檢就是瀏覽器先向伺服器傳送一個Method為Options的請求,如果伺服器允許跨域請求,瀏覽器再發起這個Ajax請求。所以CORS的複雜請求會比簡單請求額外耗費一個TTL的時間。
CORS的細節請參見大神阮一峰的博文《跨域資源共享CORS詳解》
閱讀相關文章,關注公眾號【碼洞】