express-request-id icon indicating copy to clipboard operation
express-request-id copied to clipboard

Consider Cuid2 support?

Open ericelliott opened this issue 2 years ago • 1 comments

Cuid2 is a secure id generator that may be better than UUID for this use case.

Cuid2 is:

  • Secure: It's not feasible to guess the next id, existing valid ids, or learn anything about the referenced data from the id. Cuid2 uses multiple, independent entropy sources and hashes them with a security-audited, NIST-standard cryptographically secure hashing algorithm (Sha3).
  • Collision resistant: It's extremely unlikely to generate the same id twice (by default, you'd need to generate roughly 4,000,000,000,000,000,000 ids (sqrt(36^(24-1) * 26) = 4.0268498e+18) to reach 50% chance of collision.
  • Horizontally scalable: Generate ids on multiple machines without coordination.
  • Offline-compatible: Generate ids without a network connection.
  • URL and name-friendly: No special characters.
  • Fast and convenient: No async operations. Won't introduce user-noticeable delays. Less than 5k, gzipped.
  • But not too fast: If you can hash too quickly you can launch parallel attacks to find duplicates or break entropy-hiding. For unique ids, the fastest runner loses the security race.

UUID V4 relies purely on pseudorandom entropy (even the "cryptographically secure" version - see Cuid2 docs), and has historically had collision problems, which could lead to duplicate request ids.

ericelliott avatar Jan 24 '23 22:01 ericelliott

// src/libs/express-unique-id.ts
import { Request, Response, NextFunction, RequestHandler } from 'express';
import { createId } from '@paralleldrive/cuid2';

export interface ExpressRequestIdOptions {
  setHeader?: boolean;
  headerName?: string;
  generator?: () => string;
  validator?: (id: string) => boolean;
}

export function requestID(options: ExpressRequestIdOptions = {}): RequestHandler {
  const { generator = createId, headerName = 'X-Request-Id', setHeader = true, validator = () => true } = options;

  return function (req: Request, res: Response, next: NextFunction) {
    let requestId = req.get(headerName);

    // Use the validator to check if the existing ID is acceptable
    if (!requestId || !validator(requestId)) {
      requestId = generator();
    }

    Object.defineProperty(req, 'id', {
      value: requestId,
      writable: false,
    });

    if (setHeader) {
      res.setHeader(headerName, requestId);
    }

    next();
  };
}
import { requestID } from '@/libs/express-unique-id';
app.use(requestID());

also includes a validator for https://github.com/floatdrop/express-request-id/issues/39

masterkain avatar May 12 '24 09:05 masterkain