- Add CSP, frame and referrer headers for served static files - Enforce WebSocket origin check to prevent cross-site hijacking - Trust X-Forwarded-For only when the peer is from a private proxy network - Limit concurrent connections (500 total / 20 per IP) and rooms (300) - Add ALLOWED_ORIGINS env var for additional WebSocket origins - Document reverse proxy setup (NPM/NPMplus) in README - Add scripts/security-test.mjs to verify origin and limit behavior
64 lines
2.1 KiB
JavaScript
64 lines
2.1 KiB
JavaScript
import { request } from 'node:http'
|
|
|
|
const PORT = 3101
|
|
|
|
function wsUpgrade(origin) {
|
|
return new Promise((resolve) => {
|
|
const req = request({
|
|
host: 'localhost',
|
|
port: PORT,
|
|
path: '/',
|
|
headers: {
|
|
Connection: 'Upgrade',
|
|
Upgrade: 'websocket',
|
|
'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
|
|
'Sec-WebSocket-Version': '13',
|
|
...(origin ? { Origin: origin } : {}),
|
|
},
|
|
})
|
|
req.on('response', (res) => resolve({ status: res.statusCode }))
|
|
req.on('error', () => resolve({ status: 'destroyed' }))
|
|
req.end()
|
|
})
|
|
}
|
|
|
|
const { WebSocket } = await import('ws')
|
|
|
|
function wsOpen(origin, sendCreate) {
|
|
return new Promise((resolve) => {
|
|
const ws = new WebSocket(`ws://localhost:${PORT}`, origin ? { origin } : undefined)
|
|
const done = (ok, info) => {
|
|
try { ws.close() } catch {}
|
|
resolve({ ok, info })
|
|
}
|
|
ws.on('open', () => {
|
|
if (sendCreate) ws.send(JSON.stringify({ t: 'create', mode: 'coop', name: 'Test' }))
|
|
else done(true, 'connected')
|
|
})
|
|
ws.on('message', (d) => {
|
|
const m = JSON.parse(d.toString())
|
|
if (m.t === 'room') done(true, 'room ' + m.code)
|
|
if (m.t === 'error') done(false, m.msg)
|
|
})
|
|
ws.on('error', () => done(false, 'error'))
|
|
setTimeout(() => resolve({ ok: false, info: 'timeout' }), 3000)
|
|
})
|
|
}
|
|
|
|
// 1) böser Origin → 403
|
|
const evil = await wsUpgrade('https://boese-seite.example.com')
|
|
console.log('1) Origin-Check böser Origin :', evil.status === 403 ? 'OK (403)' : `FEHLER (${evil.status})`)
|
|
|
|
// 2) same-origin → erlaubt
|
|
const same = await wsOpen('http://localhost:' + PORT, false)
|
|
console.log('2) Same-Origin :', same.ok ? 'OK' : 'FEHLER ' + same.info)
|
|
|
|
// 3) dev-origin (localhost:5173) → erlaubt
|
|
const dev = await wsOpen('http://localhost:5173', false)
|
|
console.log('3) Dev-Origin (:5173) :', dev.ok ? 'OK' : 'FEHLER ' + dev.info)
|
|
|
|
// 4) ohne Origin + Raum erstellen
|
|
const plain = await wsOpen(undefined, true)
|
|
console.log('4) Non-Browser + Raum :', plain.ok ? 'OK (' + plain.info + ')' : 'FEHLER ' + plain.info)
|
|
|
|
process.exit(0)
|