如何仿造websocket請求?

2022-08-07 09:00:27

之前兩次singnalr、 websocket實時推播相關:

tag: 瀏覽器--->nginx--> server

其中提到nginx預設不會為使用者端轉發UpgradeConnection檔頭, 因為為了讓被代理的後端伺服器知道使用者端要升級協定,故要在nginx上顯式轉發檔頭:

location /realtime/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

事情本該就就這麼簡單, 但devops總會有各種奇怪的姿勢。

小動作引起的頭腦風暴

但是運維在給nginx設定的時候,給/根路徑設定了webcoket協定升級檔頭。

按照字面理解,導致所有的使用者端轉發請求都在要求切換到websocket協定,但是除了/chat路徑, 伺服器其他http路徑並沒有做websocket協定的邏輯,其他http請求是不是都該報錯了。

是實際看,所有的請求(websocket、http)都沒有報錯,都按照指定預期返回。

刨一下

利用asp.netcore預設腳手架專案:

已知http://localhost:5000/WeatherForecast是http請求,返回一大坨json資料;
WeatherForecast新增斷言紀錄檔:

模擬ops的錯配效果,我們給這個請求新增websocket協定升級檔頭。

第一次:curl 'http://localhost:5000/WeatherForecast' -H 'Upgrade: websocket' -H 'Connection: Upgrade' --verbose,正常返回大坨json資料。

紀錄檔記錄:

 該請求是不是webcocket請求:False,headers:[Accept, */*], [Connection, Upgrade], [Host, localhost:5000], [User-Agent, curl/7.79.1], [Upgrade, websocket]

以上說明,伺服器端並不認為是websocket請求, 這也印證了ops雖然錯配,但對於常規的http請求沒造成影響。

那伺服器端到底是怎麼認定websocket請求?

伺服器端認定websocket請求的原始碼

依次判斷;

  • HttpMethod: GET
  • Sec-WebSocket-Version檔頭==13
  • Connection檔頭==Upgrade
  • Upgrade檔頭==websocket
  • 有效的Sec-WebSocket-Key檔頭

這樣我們就明白了,雖然websocket協定基於http,新增了httpConnectionUpgrade檔頭,但是瀏覽器實際會給我們帶上Sec-WebSocket-KeySec-WebSocket-Version檔頭,以向伺服器證明這是一個有效的websocket握手。

於是我們可以使用
curl 'http://localhost:5000/WeatherForecast' -H 'Upgrade: websocket' -H 'Connection: Upgrade' -H 'Sec-WebSocket-Version: 13' -H 'Sec-webSocket-Key: eeZn6lg/rOu8QbKwltqHDA==' --verbose 仿造使用者端websocket請求。

紀錄檔記錄:

該請求是不是webcocket請求:True,headers:[Accept, */*], [Connection, Upgrade], [Host, localhost:5000], [User-Agent, curl/7.79.1], [Upgrade, websocket], [Sec-WebSocket-Version, 13], [Sec-WebSocket-Key, eeZn6lg/rOu8QbKwltqHDA==]

對於這個websocket請求,伺服器端還是按照http程式碼邏輯返回200ok和JSON資料,從這個層面上看,http協定是相容websocket的。

真正要讓伺服器端按照websocket姿勢, 要使用HttpContext.WebSockets.AcceptWebSocketAsync()告知使用者端開始切換協定,並在原tcp上發起全雙工通訊。

前後對比, 困惑得解: 雖然nginx為http請求轉發了ConnectionUpgrade檔頭, 但是伺服器並不認可這是websocket升級協定,認為是攜帶了特殊檔頭的http請求,走原來的http業務處理邏輯是沒有問題的。

總結

  1. 本文記錄了nginx在轉發websocket請求時要新增的設定
  2. websocket以http協定為藍本,新增了特定的htp檔頭來要求切換協定;為了與常規http區分,瀏覽器自動增加了Sec-websocket-key等檔頭, 讓伺服器端認為這是一個有效的websocket請求。