fix(api): throttle before auth, configurable proxy trust, no-store and a strict CSP
- ThrottlerGuard runs first, so requests with wrong tokens or API keys count against the rate limit before any database lookup. - TRUST_PROXY (hop count or Express list) replaces the hard-coded 1. - Every API answer except the public lot pages is Cache-Control: no-store without ETag; helmet sends default-src 'none' / frame-ancestors 'none' (Swagger keeps the page policy when enabled) and same-site CORP. - Request log lines carry the path only - no query strings (the SSO callback's code and state no longer reach the logs). - In production the server refuses to start without an https FRONTEND_ORIGIN or with COOKIE_SECURE=false. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
2de93bc087
commit
9ae1d96a32
7 files changed
+134
-4
No files matched your search
@@ -0,0 +1,22 @@
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
|
||||
// The public origin pages of honey lots are the only answers meant for
|
||||
// everyone; they may be cached briefly.
|
||||
const CACHEABLE_PREFIX = '/api/v1/public/';
|
||||
|
||||
/**
|
||||
* Personal data, tokens and exports must not end up in a browser's
|
||||
* back/forward cache or a shared proxy. Every API answer is no-store unless
|
||||
* it is one of the public, anonymous ones.
|
||||
*/
|
||||
export function noStoreMiddleware(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
): void {
|
||||
if (!req.path.startsWith(CACHEABLE_PREFIX)) {
|
||||
res.setHeader('Cache-Control', 'no-store');
|
||||
res.setHeader('Pragma', 'no-cache');
|
||||
}
|
||||
next();
|
||||
}
|
||||
@@ -18,7 +18,9 @@ export function requestLoggerMiddleware(
|
||||
const start = Date.now();
|
||||
res.on('finish', () => {
|
||||
const duration = Date.now() - start;
|
||||
const line = `${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`;
|
||||
// Path only: query strings can carry secrets (the SSO callback's
|
||||
// authorization code and state, search terms with personal data).
|
||||
const line = `${req.method} ${req.baseUrl}${req.path} ${res.statusCode} ${duration}ms`;
|
||||
if (res.statusCode >= 500) {
|
||||
logger.error(line);
|
||||
} else if (res.statusCode >= 400) {
|
||||
|
||||
Reference in new issue
Block a user