TRXTD/scripts/security-test.mjs
Tronax 4ec0e483aa
feat(server): add security hardening for public hosting
- 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
2026-08-16 13:16:20 +02:00

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)