mirror of
https://github.com/Ride-The-Lightning/RTL.git
synced 2026-08-13 12:33:07 +02:00
Replace deprecated csurf with csrf-csrf
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.
This commit is contained in:
parent
6e61d1b759
commit
e617fbb561
9 changed files with 114 additions and 110 deletions
|
|
@ -59,8 +59,12 @@ export class ExpressApplication {
|
|||
this.app.use(this.common.baseHref + '/api/ecl', eclRoutes);
|
||||
this.app.use(this.common.baseHref, express.static(join(this.directoryName, '../..', 'frontend')));
|
||||
this.app.use((req: any, res, next) => {
|
||||
res.cookie('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : (req.cookies && req.cookies._csrf) ? req.cookies._csrf : ''); // RTL Angular Frontend
|
||||
res.setHeader('XSRF-TOKEN', req.csrfToken ? req.csrfToken() : (req.cookies && req.cookies._csrf) ? req.cookies._csrf : ''); // RTL Quickpay JQuery
|
||||
// Generate the token once per request: with csrf-csrf every call mints a
|
||||
// new token on a first visit, so calling twice would desync the cookie
|
||||
// from the header and the _csrf cookie it must match.
|
||||
const csrfToken = req.csrfToken ? req.csrfToken() : (req.cookies && req.cookies._csrf) ? req.cookies._csrf : '';
|
||||
res.cookie('XSRF-TOKEN', csrfToken); // RTL Angular Frontend
|
||||
res.setHeader('XSRF-TOKEN', csrfToken); // RTL Quickpay JQuery
|
||||
res.sendFile(join(this.directoryName, '../..', 'frontend', 'index.html'));
|
||||
});
|
||||
this.app.use((err, req, res, next) => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import jwt from 'jsonwebtoken';
|
||||
import csurf from 'csurf/index.js';
|
||||
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 = csurf({ cookie: true });
|
||||
const csurfProtection = CSRF.csrfProtection;
|
||||
|
||||
export const isAuthenticated = (req, res, next) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,31 @@
|
|||
import csurf from 'csurf/index.js';
|
||||
import { doubleCsrf } from 'csrf-csrf';
|
||||
import { Application } from 'express';
|
||||
import { Logger, LoggerService } from './logger.js';
|
||||
import { Common, CommonService } from './common.js';
|
||||
|
||||
class CSRF {
|
||||
|
||||
public csrfProtection = csurf({ cookie: true });
|
||||
public logger: LoggerService = Logger;
|
||||
public common: CommonService = Common;
|
||||
|
||||
// Signed double-submit-cookie protection (replaces the deprecated csurf).
|
||||
// The signed token lives in the httpOnly '_csrf' cookie; the client echoes
|
||||
// the same token (read from the XSRF-TOKEN cookie set in app.ts) in a
|
||||
// header. The cookie is not secure-only because RTL commonly serves plain
|
||||
// HTTP (matching the session cookie); token sources match what csurf
|
||||
// accepted. The error code EBADCSRFTOKEN is handled in app.ts.
|
||||
private doubleCsrfUtilities = doubleCsrf({
|
||||
getSecret: () => this.common.secret_key,
|
||||
getSessionIdentifier: (req: any) => (req.session ? req.session.id : ''),
|
||||
cookieName: '_csrf',
|
||||
cookieOptions: { sameSite: 'strict', path: '/', secure: false, httpOnly: true },
|
||||
getCsrfTokenFromRequest: (req: any) => (req.body && req.body._csrf) || (req.query && req.query._csrf) ||
|
||||
req.headers['csrf-token'] || req.headers['xsrf-token'] ||
|
||||
req.headers['x-csrf-token'] || req.headers['x-xsrf-token']
|
||||
});
|
||||
|
||||
public csrfProtection = this.doubleCsrfUtilities.doubleCsrfProtection;
|
||||
|
||||
public mount(app: Application): Application {
|
||||
this.logger.log({ selectedNode: this.common.selectedNode, level: 'INFO', fileName: 'CSRF', msg: 'Setting up CSRF..' });
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue