WebSocket 是一種能在單一 TCP 連線上提供全雙工通訊的協定。它讓客戶端和伺服器之間能進行持續性的雙向通訊,特別適合需要即時更新的應用程式,例如線上遊戲、聊天軟體和即時資料展示。
WebSocket 的主要特點包括:
規範:
全雙工通訊(Full-duplex Communication)是一種通訊方式,
允許資料在兩個方向上同時傳輸。
想像一條雙向車道,車輛可以同時在兩個方向上行駛,
不需要等待對向車道淨空。
輪詢是一種客戶端定期向伺服器請求更新的技術。
一個學生(客戶端)不斷詢問老師(伺服器)「有新課程(訊息)嗎?」
即使大多數時候答案是「沒有」。
相較於輪詢,全雙工通訊(如 WebSocket)提供了更有效率和即時的通訊方式。輪詢可能會導致延遲和不必要的資源消耗,而全雙工通訊允許即時、雙向的資料流動,特別適合需要即時更新的應用場景。
WebSocket 如果使用不當,可能會出現以下安全漏洞:
為了避免這些漏洞,開發者應該實施嚴格的身分驗證、使用安全的 wss:// 協定、限制連線數量、仔細驗證所有輸入資料,並確保整體系統的安全性。定期的安全稽核和滲透測試也是確保 WebSocket 應用程式安全的重要措施。
商業邏輯漏洞是指應用程式的設計或實現中存在的缺陷,
這些缺陷允許用戶以意料之外的方式操作系統,
可能導致未授權的行為或對系統造成損害。
這些漏洞通常與應用程式的特定業務流程或功能相關。
商業邏輯漏洞可能會造成系統安全性的重大問題,以下是幾種常見的情況:
這些商業邏輯漏洞往往不像技術漏洞那樣容易被自動化工具發現,
需要開發者和測試人員仔細審查系統流程,
確保每個步驟都有適當的檢查和限制。
https://github.com/fei3363/ithelp_web_security_2024/commit/10da3fd3a8d56ee4a56a389601e0ad3f680a6ddb
/web/controllers/inventoryController.js
const { broadcastInventoryUpdate } = require('../websocket');
let inventory = {
'item1': 100,
'item2': 200,
'item3': 300
};
const inventoryController = {
getALLInventory(req, res) {
res.json(inventory);
},
updateInventory(req, res) {
const { item, quantity } = req.body;
if (item && quantity !== undefined) {
inventory[item] = quantity;
broadcastInventoryUpdate({ item, quantity });
res.json({ message: '庫存已更新', item, quantity });
} else {
res.status(400).json({ error: '無效的請求' });
}
},
};
module.exports = inventoryController;
broadcastInventoryUpdate
函數來及時廣播庫存更新inventory
物件可能導致意外的副作用/web/package.json
{
"dependencies": {
// ...其他相依特建
"ws": "^8.6.0"
}
}
/web/routes/inventoryRoutes.js
const express = require('express');
const router = express.Router();
const inventoryController = require('../controllers/inventoryController');
router.get('/', inventoryController.getALLInventory);
router.post('/update', inventoryController.updateInventory);
module.exports = router;
/web/server.js
const WebSocket = require('ws');
const http = require('http');
const { initWebSocket } = require('./websocket');
const inventoryRoutes = require('./routes/inventoryRoutes');
// ... 其他程式碼 ...
app.use('/api/inventory', inventoryRoutes);
const server = http.createServer(app);
initWebSocket(server);
server.listen(port, () => {
console.log(`HTTP and WebSocket server running at http://localhost:${port}`);
});
http.createServer
建立可同時支持 HTTP 和 WebSocket 的伺服器initWebSocket
初始化 WebSocket 伺服器/web/websocket.js
const WebSocket = require('ws');
let wss;
const clients = new Set();
function initWebSocket(server) {
wss = new WebSocket.Server({ server });
wss.on('connection', (ws) => {
clients.add(ws);
console.log('新的 WebSocket 連接');
ws.on('close', () => {
clients.delete(ws);
console.log('WebSocket 連接關閉');
});
});
}
function broadcastInventoryUpdate(update) {
const message = JSON.stringify(update);
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
}
module.exports = { initWebSocket, broadcastInventoryUpdate };
Set
來管理 WebSocket 連接broadcastInventoryUpdate
函數來向所有連接的客戶端發送更新broadcastInventoryUpdate
函數沒有錯誤處理,可能因單個客戶端錯誤而中斷整個廣播過程wss
變數可能導致測試困難和潛在的並行問題curl -O -L https://github.com/vi/websocat/releases/download/v1.13.0/websocat.x86_64-unknown-linux-musl
ls -al websocat.x86_64-unknown-linux-musl
chmod +x websocat.x86_64-unknown-linux-musl
./websocat.x86_64-unknown-linux-musl ws://nodelab.feifei.tw
{"item":"item1","quantity":50}
{"item":"item1","quantity":0}
{"item":"<h1>item1</h1>","quantity":0}
{"item":"item2","quantity":-1000}
curl http://nodelab.feifei.tw/api/inventory && echo
{"item1":100,"item2":200,"item3":300}
curl -X POST http://nodelab.feifei.tw/api/inventory/update -H "Content-Type: application/json" -d '{"item": "item1", "quantity": 50}' && echo
{"message":"庫存已更新","item":"item1","quantity":50}
curl -X POST http://nodelab.feifei.tw/api/inventory/update -H "Content-Type: application/json" -d '{"item": "item1", "quantity": 0}' && echo
指令目的:測試將庫存設定為零的情況,這在某些系統中可能被視為無效操作。
指令結果:
{"message":"庫存已更新","item":"item1","quantity":0}
指令截圖:
其他
指令:
curl -X POST http://nodelab.feifei.tw/api/inventory/update -H "Content-Type: application/json" -d '{"item": "<h1>item1</h1>", "quantity": 0}' && echo
{"message":"庫存已更新","item":"<h1>item1</h1>","quantity":0}
curl -X POST http://nodelab.feifei.tw/api/inventory/update -H "Content-Type: application/json" -d '{"item": "item2", "quantity": -1000}' && echo
{"message":"庫存已更新","item":"item2","quantity":-1000}
目前庫存系統實作中的多個安全漏洞和邏輯錯誤,涉及 WebSocket 和商業邏輯兩個層面。
由於 WebSocket 的即時特性,這些問題對所有連線的使用者都是可見的:
在給定的 WebSocket 實作中,以下哪項不是一個安全隱患?
A. 缺乏身分驗證機制
B. 使用 Set 來管理 WebSocket 連線
C. 允許任何客戶端連線並接收所有更新
D. 沒有限制單一 IP 的連線數量
答案:B
解析:使用 Set 來管理 WebSocket 連線是一個好的做法,它能有效地管理連線並避免重複。其他選項都是安全隱患:缺乏身分驗證可能導致未經授權的存取,允許所有客戶端接收所有更新可能導致敏感資訊外洩,而不限制連線數量可能被用於阻斷服務(DoS)攻擊。
在更新庫存的功能中,哪一項操作可能導致最嚴重的安全問題?
A. 將商品庫存設定為 0
B. 更新不存在的商品
C. 將商品名稱設定為 "<script>alert('XSS')</script>"
D. 將商品庫存設定為負數
答案:C
解析:雖然所有這些操作都可能導致問題,但將商品名稱設定為包含 JavaScript 程式碼的 HTML 標籤是最危險的,因為它可能導致跨網站指令碼攻擊(XSS)。這種攻擊可能影響所有查看庫存資訊的使用者,造成廣泛的安全威脅。其他選項主要是邏輯或資料完整性問題,雖然也需要處理,但不會直接導致程式碼執行的安全漏洞。
在目前的實作中,為什麼允許將庫存設定為負數是一個問題?
A. 這會導致系統當機
B. 這違反了基本的業務邏輯
C. 這會使 WebSocket 連線中斷
D. 這會自動觸發訂單取消
答案:B
解析:允許負數庫存違反了基本的業務邏輯。在現實世界中,庫存不可能為負數。這種情況可能導致庫存管理混亂、錯誤的銷售預測,以及可能的超賣問題。雖然這不會直接導致系統當機或 WebSocket 連線問題,但它反映了系統在輸入驗證和業務規則執行方面的不足。
在 WebSocket 實作中,為什麼對所有連線的客戶端廣播所有更新可能是個問題?
A. 這會增加伺服器的負載
B. 這可能洩漏敏感的業務資訊
C. 這會使 WebSocket 連線變慢
D. 這會導致客戶端記憶體溢位
答案:B
解析:雖然廣播所有更新確實可能增加伺服器負載,但最主要的問題是可能洩漏敏感的業務資訊。在一個正式環境中,不同的使用者或客戶端應該只能存取他們有權限看到的資訊。廣播所有更新意味著每個連線的客戶端都能看到系統中的所有庫存變化,這可能包括競爭對手或未經授權的使用者不應該看到的資訊。
以下哪項不是解決目前系統中的競爭條件(Race Condition)問題的有效方法?
A. 使用資料庫交易
B. 實作 Optimistic Locking
C. 增加伺服器的處理能力
D. 使用 Atomic Operation
答案:C
解析:增加伺服器的處理能力不能有效解決競爭條件問題。競爭條件是由於多個程式同時存取共享資源而導致的邏輯問題,單純增加處理能力並不能解決這個根本問題。相反,使用資料庫交易、實作 Optimistic Locking 或使用 Atomic Operation 都是處理競爭條件的有效方法。這些方法可以確保在高併發情況下,資料的一致性和完整性得到維護。