摘要:使用約定好的計算握手消息,并使用生產的隨機數對消息進行加密,最后將之前生成的所有消息發送給網站。之后所有的通信數據將由之前瀏覽器生成的隨機密碼并利用對稱加密算法進行加密。支持四個異步事件。
由于HTTP沒有加密機制,其傳輸的內容很容易泄漏,并且HTTP協議沒法確認通信方,也無法保證接收到的報文在傳輸過程中是否被篡改,因此HTTPS是在HTTP協議的基礎上提供了加密、認證和完整性保護的功能。HTTPS并非是應用層的一種新協議,只是HTTP通信接口部分用SSL和TLS協議代替而已,通常HTTP直接和傳輸層的TCP協議通信,當使用了SSL后,HTTP先和SSL協議通信,SSL再和TCP協議通信。
加密方法近代的加密方法中加密算法是公開的,而密鑰卻是保密的,加密和解密都需要用到密鑰,沒有密鑰就沒法對加密內容進行解密,通過這種方式得以保持加密方法的安全性。加密和解密同用一個密鑰的方式陳為共享密鑰加密,也被稱為對稱密鑰加密。以共享密鑰方式的加密必須將密鑰也發給對方,在網絡上發送密鑰很容易被攻擊者獲取。并且服務器如果對所有客戶端都使用同樣的共享密鑰,無異于沒有加密,所以HTTPS采用生成的隨機數來作為共享加密算法的密鑰。
公開密鑰加密使用一對非對稱的密鑰,一把叫做私有密鑰,一把叫做公有密鑰,發送密文的一方使用對方的公開密鑰進行加密處理,對方收到被加密的信息后,再使用自己的私有密鑰進行解密,這樣就不用擔心密鑰被攻擊者獲取。
HTTPS采用共享密鑰加密和公開密鑰加密兩者并用的混合加密機制,如果僅僅保證密鑰的安全性,使用公開密鑰加密的方式就可以實現了,但是公開密鑰加密方式比共享密鑰加密,其處理速度要慢。所以HTTPS在交換密鑰環節采用公開密鑰加密方式,之后建立通信交換報文階段采用共享密鑰加密方式。
公開密鑰加密方式也不能保證公開密鑰本身的真實性,比如,在與某臺服務器建立連接時,無法保證接收到的公開密鑰就是需要連接的那個服務器的密鑰,這個時候可以采用數字證書認證機構和其相關機關頒發的公開密鑰證書。
HTTPS的握手過程首先,客戶端會發送一個https的請求,把自身支持的一系列密鑰算法組件(Cipher Suite)發送給服務器。
服務器接收到客戶端所有的Cipher后與自身支持的對比,如果不支持則連接斷開,反之則會從中選擇一種加密算法和HASH算法以證書的形式返回給客戶端,證書中包含了加密公鑰,頒證機構,網站地址,失效日期等等。
客戶端收到服務器端的響應后會做以下幾件事:
1:驗證證書的合法性,頒發證書的機構是否合法與是否過期,證書中包含的網站地址是否與正在訪問的地址一致等,證書驗證通過后,在瀏覽器的地址欄會加上一把小鎖。
2:證書驗證通過后,生成隨機密碼,用證書中的公鑰加密。
3:使用約定好的HASH計算握手消息,并使用生產的隨機數對消息進行加密,最后將之前生成的所有消息發送給網站。
服務器接收到客戶端傳來的密文,用自己的私鑰來解密取出隨機數密碼,然后用隨機數密碼解密瀏覽器發送過來的握手消息,并驗證HASH是否是與瀏覽器發來的一致,然后使用密碼加密一段握手消息,發送給瀏覽器。
客戶端用隨機數解密并計算出握手消息的HASH,如果與服務器端發來的HASH一致,此時握手過程結束。
之后所有的通信數據將由之前瀏覽器生成的隨機密碼并利用對稱加密算法進行加密。因為這串密碼只有客戶端和服務器知道,所有即使中間請求被攔截也是沒法解密數據的,以此保證了通信的安全。
其中非對稱加密算法用于在握手消息過程中加密生成的隨機數密碼,對稱加密算法用于對真正傳輸的數據進行加密,而HASH算法用于驗證數據的完整性,TLS握手過程中如果有任何錯誤,都會使加密連接斷開,從而阻止了隱私信息的傳輸,由于HTTPS非常的安全,攻擊者無法從中找到下手的地方,于是更多的是采用了假證書的手法來欺騙客戶端,從而獲取明文的信息。
瀏覽器在對證書進行驗證時,以下幾種情況會導致驗證失敗:
SSL證書不是由受信任的CA機構頒發的
證書過期
訪問的網站域名與證書綁定的域名不一致
對HTTPS最常見的攻擊手段就是SSL證書欺騙或者叫SSL劫持,是一種典型的中間人攻擊,不過SSL劫持并非只是用于攻擊目的,在一些特殊情況下利用SSL劫持可以更順暢的訪問網絡。SSL劫持需要將攻擊者接入到瀏覽器與目標網站之間,在傳輸數據的過程中,替換目標網站發給瀏覽器的證書,之后解密傳輸的數據,中間人攻擊最好的環境是在局域網中,因為局域網中所有的計算機需要通過網關來接入互聯網,因此攻擊者只需要實施一次中間人攻擊就可以順利的截獲所有計算機與網關之間傳輸的數據。一般SSL劫持,瀏覽器會給出證書錯誤的提示,如果繼續訪問,所有加密的數據都可以被攻擊者解密。
SSLStrip攻擊也需要將攻擊者設置為中間人,之后將HTTPS訪問替換為HTTP返回給瀏覽器,由于HTTP協議傳輸的數據都是未加密的,從而截獲用戶訪問的數據。對于登錄賬號密碼等關鍵信息,可以在發送之前用javaScript進行一次加密處理,這種方法對SSLScrip和SSL劫持都是有效的。
HTTPS也存在一些問題,那就是處理速度會變慢,一是由于存在HTTPS握手環節,導致通信變慢,二是由于存在傳輸信息加密處理,消耗CPU及內存資源,這對于高并發的服務器而言,更是一種性能瓶頸,所以服務器只是對含有敏感信息的數據進行加密。
WebSoket原理HTTP協議的通信都是瀏覽器發出一個請求,服務器接收請求后進行處理并返回結果,瀏覽器再將接收到的信息進行渲染,這種機制對于實時性要求高的應用場景顯得捉襟見肘,傳統請求-響應模式的web開發通常采用輪詢方案,就是瀏覽器以一定時間間隔頻繁的向服務器請求數據,來保持客戶端數據的實時更新,但是服務器數據可能并沒有更新,會帶來很多無謂的請求,浪費帶寬,效率低下。
webSocket是HTML5下的一種新的協議,基于TCP傳輸協議,本身屬于應用層協議,并且復用了HTTP握手通道。它實現了瀏覽器與服務器的全雙工通信,能更好的節省服務器資源和帶寬并達到實時通訊的目的,webSocket與HTTP長連接的區別:HTTP長連接在建立TCP連接后,在每個請求中任需要多帶帶發送請求頭,數據傳輸的效率低,并且服務器不能主動給瀏覽器推送數據。
WebSocket建立連接webSocket借用HTTP的協議來完成握手,首先通過HTTP發起請求報文,報文如下:
GET / HTTP/1,1 Host: localhost:8080 Origin: http://127.0.0.1:3000 Connection: Upgrade Upgrade: websocket Sec-WebSocket-key:w4v7O6xFTi36lq3RNcgctw== Sec-WebSocket-Protocol:chat, superchat Sec-WebSocket-Version:13
在上面的HTTP請求頭中,Connection和Upgrade字段表示請求服務器升級協議為webSocket,其中Sec-WebSocket-key的值是隨機生成的Base64編碼的字符串,Sec-WebSocket-Protocol和Sec-WebSocket-Version字段指定子協議和版本。服務器響應頭如下:
HTTP/1.1 101 Switching Protocols Connection:Upgrade Upgrade: websocket Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=
狀態碼101表示協議切換,到此完成協議升級,后續的數據交互都按照新的協議來。Sec-WebSocket-Accept是根據請求字段Sec-WebSocket-key計算出來的,其計算過程為:
將Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11字符串進行拼接,形成新的字符串w4v7O6xFTi36lq3RNcgctw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11;
通過sha1算法計算出結果,并轉換成base64字符串。
瀏覽器會校驗Sec-WebSocket-Accept的值,如果成功,webSocket的握手成功將開始接下來的數據傳輸。
webSocket傳輸數據的基本單位是幀,一般webScoket傳輸的一條消息會被切割成多個幀,數據幀有一個標志位FIN,FIN=1表示這是消息的最后一幀,接收端接收到幀數據后,如果該幀的FIN標識為1,就會將已經接收到的數據幀組裝成一個完整的消息。
WebSocket實戰 HTML5中的WebSoketvar Socket = new WebSocket(url,[protocol])
URL字符串必須以“ws”或“wss”(加密通信時)文字作為開頭,protocol是Web應用能夠使用的協議,可以是字符串,也可以是數組,如果一下代碼中proto1和proto2是定義明確、可能已注冊且標準化的協議名稱,它們能夠同時為客戶端和服務器所理解,服務器會從列表中選擇首選協議。webSocket支持onopen、onmessage、onclose、onerror四個異步事件。
if(window.WebScoket){ var url = "ws://localhost:8080/test" var socket = new WebSocket(url,["proto1","proto2"]) socket.onopen = function(){ socket.send("message") } socket.onmessage = function(e){ console.log(e.data) } socket.onclose = function(){ } socket.onerror = function(){ } var arr = new Uint8Array([8,3,4,5,7,0,9]) socket.send(arr.buffer) socket.binaryType = "arrayBuffer" // 設置接收ArrayBuffer對象 socket.binaryType = "blob" // 設置接收blob對象 socket.onmessage = function(e){ console.log(e.data) } }
webSocket.bufferAmount屬性表示已在WebSocket上發送但尚未寫入網絡的字節數,一般用于調節發送速率。WebSocket API支持以二進制發送Blob和ArrayBuffer實例。當然也可以設置接收Blob和ArrayBuffer對象。
Nodejs中的webSocketwebSocket的數據幀格式為以下部分:
FIN:1位,表示是否結束, 1:結束
RSV[1-3]:用于協商擴展
opcode: 4位,0,1,2屬于數據幀,8,9,10屬于控制幀
mask:掩碼,0表示不使用亞掩碼,一般服務器回消息不會使用掩碼,接收消息需要掩碼
payloadLen 0-125 直接表示 數據長度
126,后面16位對應數據長度 127,后面64位對應數據長度
masking-key:如果mask為1,后面32位作為masking-key,mask為0,則缺省
payload Data: 負載的數據
其連接的代碼為:
const server = http.createServer((req,res) => { res.writeHead(200,{"Content-Type":"text/plain"}) res.send("hello world") }) server.on("upgrade",(req,socket,upgradeHead) => { var key = req.headers["sec-websocket-key"] var accept = crypto.createHash("sha1").update(key + GUID).digest("base64") var headers = [ "HTTP/1.1 101 Switching protocols", "Upgrade: websocket", "Connection: Upgrade", "Sec-WebSocket-Accept: " + accept, " " ] socket.setNoDelay(true) socket.write(headers.join(" ")) var websocket = new WebSocket() websocket.setSocket(socket) }) // websocket對象 function WebSocket(){ this.socket = null this.buffer = null } WebSocket.prototype.setSocket = function(socket){ this.socket = socket var data = [] this.socket.on("data",(chunk) => { data.push(chunk) }) this.socket.on("end",() => { this.buffer = Buffer.concat(data) var frame = this.decodeMessage(this.buffer) console.log(frame) console.log(frame.payloadData.toString()) }) } WebSocket.prototype.decodeMessage = (buffer) => { let start = 0 let frame = { isFinal:(buffer[start] & 0x80) === 0x80, opcode:buffer[start++] & 0xF, masked:(buffer[start] & 0x80) === 0x80, payloadLen:buffer[start++] & 0x7F, maskingKey:"", payloadData:null } if(frame.payloadLen === 126){ frame.payloadLen = (buffer[start++] << 8) + buffer[start++] }else if(frame.payloadLen === 127){ frame.payloadLen = 0 for(let i = 7; i >= 0; i--){ frame.payloadLen = (buffer[start++] << (8 * i)) } } if(frame.payloadLen){ if(frame.masked){ const maskingKey = [ buffer[start++], buffer[start++], buffer[start++], buffer[start++] ] frame.maskingKey = maskingKey frame.payloadData = buffer.slice(start,start + frame.payloadLen) .map((byte,idx) => byte ^ maskingKey[idx % 4]) }else{ frame.payloadData = buffer.slice(start,start + frame.payloadLen) } } return frame } module.exports = WebSocket
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/61935.html
摘要:下面我們從前端基礎和底層原理開始講起。對于和這三個對應于矢量圖位圖和圖的渲染來說,給前端開發帶來了重武器,很多小游戲也因此蓬勃發展。這篇文章受眾之大,后來被人重新整理并發布為,其中還包括中文版。 showImg(https://segmentfault.com/img/bVbjM5r?w=1142&h=640); 想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等著你! 這...
摘要:巧前端基礎進階全方位解讀前端掘金我們在學習的過程中,由于對一些概念理解得不是很清楚,但是又想要通過一些方式把它記下來,于是就很容易草率的給這些概念定下一些方便自己記憶的有偏差的結論。 計算機程序的思維邏輯 (83) - 并發總結 - 掘金從65節到82節,我們用了18篇文章討論并發,本節進行簡要總結。 多線程開發有兩個核心問題,一個是競爭,另一個是協作。競爭會出現線程安全問題,所以,本...
摘要:巧前端基礎進階全方位解讀前端掘金我們在學習的過程中,由于對一些概念理解得不是很清楚,但是又想要通過一些方式把它記下來,于是就很容易草率的給這些概念定下一些方便自己記憶的有偏差的結論。 計算機程序的思維邏輯 (83) - 并發總結 - 掘金從65節到82節,我們用了18篇文章討論并發,本節進行簡要總結。 多線程開發有兩個核心問題,一個是競爭,另一個是協作。競爭會出現線程安全問題,所以,本...
閱讀 778·2023-04-25 20:47
閱讀 2542·2019-08-30 15:53
閱讀 951·2019-08-26 14:05
閱讀 899·2019-08-26 11:59
閱讀 1685·2019-08-26 11:43
閱讀 1684·2019-08-26 10:57
閱讀 1363·2019-08-23 18:23
閱讀 2668·2019-08-23 12:57