mirror of
https://github.com/Ride-The-Lightning/RTL.git
synced 2026-08-13 12:33:07 +02:00
csurf has been deprecated since 2022 and pins an old cookie release with a known advisory; npm's only fix is a downgrade (issue #1634, item 2). csrf-csrf v4 implements the same double-submit-cookie pattern with an HMAC-signed, session-bound token keyed on the existing boot secret (common.secret_key). The frontend contract is unchanged: the token still arrives via the XSRF-TOKEN cookie/header and is echoed as x-xsrf-token (all token sources csurf accepted are still read), the signed cookie keeps the _csrf name (now httpOnly, secure:false to match the session cookie on plain-HTTP deployments), doubleCsrfProtection attaches req.csrfToken so app.ts keeps working, and the error code is EBADCSRFTOKEN - already handled in app.ts. The websocket upgrade check in authCheck.ts now routes through the shared middleware; upgrade requests are GETs, so its pass-through semantics are unchanged. One fix this surfaced: app.ts called req.csrfToken() twice (cookie and header). Under csurf every token validated against a stable secret; under csrf-csrf each first-visit call mints a new token, desyncing the XSRF-TOKEN cookie from the _csrf cookie it must equal. The token is now generated once per request. Tokens are session-bound, so a token stolen from one session no longer validates in another - a check csurf's cookie mode did not perform. Production npm audit drops from 6 low findings to 4, all in the crypto-browserify/elliptic chain tracked in #1634. Verified against the docker regtest fixture: both API suites (43 checks across LND, CLN and Eclair) plus a dedicated CSRF battery - valid-token auth, missing token 403, garbage token 403, cross-session replay 403, token stability across requests, the XSRF-TOKEN response header for Quickpay, and the websocket handshake. Lint and build are clean.
63 lines
2.5 KiB
TypeScript
63 lines
2.5 KiB
TypeScript
import jwt from 'jsonwebtoken';
|
|
import CSRF from './csrf.js';
|
|
import { Common, CommonService } from './common.js';
|
|
import { Logger, LoggerService } from './logger.js';
|
|
|
|
const common: CommonService = Common;
|
|
const logger: LoggerService = Logger;
|
|
const csurfProtection = CSRF.csrfProtection;
|
|
|
|
export const isAuthenticated = (req, res, next) => {
|
|
try {
|
|
const token = req.headers.authorization.split(' ')[1];
|
|
jwt.verify(token, common.secret_key);
|
|
next();
|
|
} catch (error) {
|
|
const errMsg = 'Authentication Failed! Please Login First!';
|
|
const err = common.handleError({ statusCode: 401, message: 'Authentication Error', error: errMsg }, 'AuthCheck', errMsg, req.session.selectedNode);
|
|
return res.status(err.statusCode).json({ message: err.message, error: err.error });
|
|
}
|
|
};
|
|
|
|
export const verifyWSUser = (info, next) => {
|
|
const headers = JSON.parse(JSON.stringify(info.req.headers));
|
|
const protocols = !info.req.headers['sec-websocket-protocol'] ? [] : info.req.headers['sec-websocket-protocol'].split(',')?.map((s) => s.trim());
|
|
const jwToken = (protocols && protocols.length > 0) ? protocols[0] : '';
|
|
if (!jwToken || jwToken === '') {
|
|
next(false, 401, 'Authentication Failed! Please Login First!');
|
|
} else {
|
|
jwt.verify(jwToken, common.secret_key, (verificationErr) => {
|
|
if (verificationErr) {
|
|
next(false, 401, 'Authentication Failed! Please Login First!');
|
|
} else {
|
|
try {
|
|
let updatedReq = null;
|
|
try {
|
|
updatedReq = JSON.parse(JSON.stringify(info.req));
|
|
} catch (err) {
|
|
updatedReq = info.req;
|
|
}
|
|
let cookies = null;
|
|
try {
|
|
cookies = '{"' + headers.cookie?.replace(/ /g, '')?.replace(/;/g, '","').trim()?.replace(/[=]/g, '":"') + '"}';
|
|
updatedReq['cookies'] = JSON.parse(cookies);
|
|
} catch (err) {
|
|
cookies = {};
|
|
updatedReq['cookies'] = JSON.parse(cookies);
|
|
logger.log({ selectedNode: common.selectedNode, level: 'WARN', fileName: 'AuthCheck', msg: '403 Unable to read CSRF token cookie', data: err });
|
|
}
|
|
csurfProtection(updatedReq, null, (err) => {
|
|
if (err) {
|
|
next(false, 403, 'Invalid CSRF token!');
|
|
} else {
|
|
next(true);
|
|
}
|
|
});
|
|
} catch (err) {
|
|
logger.log({ selectedNode: common.selectedNode, level: 'WARN', fileName: 'AuthCheck', msg: '403 Unable to verify CSRF token', data: err });
|
|
next(true);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
};
|