UNPKG

webssh2-server

Version:

A Websocket to SSH2 gateway using xterm.js, socket.io, ssh2

103 lines (102 loc) 2.96 kB
// app/middleware/csrf.middleware.ts // CSRF protection middleware import { HTTP, DEFAULTS } from '../constants.js'; /** * Create CSRF protection middleware * @param config - Application configuration * @returns Express middleware handler */ export function createCSRFMiddleware(config) { return (req, res, next) => { if (!config.sso.csrfProtection) { next(); return; } if (isTrustedProxy(req, config.sso.trustedProxies)) { next(); return; } if (hasSsoHeaders(req)) { next(); return; } if (req.method === 'POST' && !isValidCsrfToken(req)) { res.status(HTTP.FORBIDDEN).send('CSRF token validation failed'); return; } next(); }; } const isTrustedProxy = (req, trustedProxies) => { if (trustedProxies.length === 0) { return false; } const candidateIp = getClientIp(req); if (candidateIp === undefined) { return false; } return trustedProxies.includes(candidateIp); }; const getClientIp = (req) => { if (typeof req.ip === 'string' && req.ip !== '') { return req.ip; } const connection = req.connection; return connection?.remoteAddress; }; const hasSsoHeaders = (req) => { const usernameHeader = req.headers[DEFAULTS.SSO_HEADERS.USERNAME]; const sessionHeader = req.headers[DEFAULTS.SSO_HEADERS.SESSION]; return usernameHeader != null || sessionHeader != null; }; const isValidCsrfToken = (req) => { const enrichedRequest = req; const sessionToken = extractSessionToken(enrichedRequest); if (sessionToken === undefined) { return false; } const requestToken = extractRequestToken(enrichedRequest); return requestToken === sessionToken; }; const extractSessionToken = (req) => { const session = req.session; if (!isObjectLike(session)) { return undefined; } return session['csrfToken']; }; const extractRequestToken = (req) => { const bodyToken = extractBodyToken(normalizeBody(req.body)); if (bodyToken !== undefined) { return bodyToken; } return toHeaderString(req.headers['x-csrf-token']); }; const normalizeBody = (body) => { if (!isPlainRecord(body)) { return undefined; } return body; }; const extractBodyToken = (body) => { if (body == null) { return undefined; } const csrfCandidate = body['_csrf']; return typeof csrfCandidate === 'string' && csrfCandidate !== '' ? csrfCandidate : undefined; }; const toHeaderString = (value) => { if (typeof value === 'string') { return value; } if (Array.isArray(value)) { return value[0]; } return undefined; }; const isObjectLike = (value) => { return typeof value === 'object' && value !== null; }; const isPlainRecord = (value) => { return isObjectLike(value) && !Array.isArray(value); };