Overview
The backend has a global rate limiter but no per-route limits, no HMAC request signing for webhook endpoints, and no HTTP security headers beyond what NestJS provides by default. This issue completes the backend security hardening. Production deployments will depend on this work.
Technical Details
1. Per-Route Rate Limits
Extend the existing @Throttle decorators to add specific limits per endpoint category:
- Auth endpoints (
/auth/login, /auth/register, /auth/refresh): 5 requests per minute per IP (already partially done — verify and tighten)
- Password reset (
/auth/forgot-password, /auth/reset-password): 3 per 15 minutes per email
- Document upload (
/documents, /carriers/documents): 10 per hour per user
- Bid submission (
/shipments/:id/bids): 20 per hour per user (carriers can't spam bids)
- Message sending (
/conversations/:id/messages): 30 per minute per user (already in [BE-13] — verify)
- CSV/PDF export: 5 per hour (already in [BE-20] — verify)
/stellar/escrow/*: 10 per hour per user
/admin/*: 60 per minute per admin user (higher limit for admin tooling)
Create a throttle.constants.ts file listing all rate limit values for easy adjustment.
2. Helmet Security Headers
Install @fastify/helmet or use NestJS Helmet middleware (if using Express):
// In main.ts comments say NOT to touch — add a note to open a follow-up if helmet needs main.ts changes.
// Instead, add a custom middleware in app.module.ts providers using NestMiddleware
Actually, since main.ts is off-limits per the project rules, implement Helmet as a NestMiddleware and apply it in AppModule.configure():
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(helmet()).forRoutes('*');
}
}
Headers to configure:
Content-Security-Policy: restrict to self + cdn sources
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Strict-Transport-Security (HSTS)
- Disable
X-Powered-By: Express
3. Webhook Signature Validation (for all external webhooks)
Ensure all webhook endpoints validate signatures:
- Stripe webhook (
POST /payments/webhook): validate stripe-signature header using stripe.webhooks.constructEvent() — already planned in [BE-23], verify this is implemented correctly
- Stellar events (if applicable): validate via the platform secret
4. SQL Injection & Input Hardening Audit
Review all TypeORM QueryBuilder calls in:
GET /shipments (filter queries)
GET /admin/users
GET /carriers
GET /notifications
Ensure all user-supplied strings are passed as TypeORM parameters (never string-interpolated into query strings). Fix any instances where .where(field = ${variable}) is used instead of .where('field = :val', { val: variable }).
5. Environment Variable Validation (Joi Schema)
Add Joi validation for all new env vars added in issues BE-10 through BE-29:
- Extend the existing
validation.schema.ts (or create it if not present)
- All Twilio, Stripe, Stellar, Web Push, and SMTP env vars should be required in production (
Joi.when('NODE_ENV', { is: 'production', then: Joi.required() }))
Acceptance Criteria
Overview
The backend has a global rate limiter but no per-route limits, no HMAC request signing for webhook endpoints, and no HTTP security headers beyond what NestJS provides by default. This issue completes the backend security hardening. Production deployments will depend on this work.
Technical Details
1. Per-Route Rate Limits
Extend the existing
@Throttledecorators to add specific limits per endpoint category:/auth/login,/auth/register,/auth/refresh): 5 requests per minute per IP (already partially done — verify and tighten)/auth/forgot-password,/auth/reset-password): 3 per 15 minutes per email/documents,/carriers/documents): 10 per hour per user/shipments/:id/bids): 20 per hour per user (carriers can't spam bids)/conversations/:id/messages): 30 per minute per user (already in [BE-13] — verify)/stellar/escrow/*: 10 per hour per user/admin/*: 60 per minute per admin user (higher limit for admin tooling)Create a
throttle.constants.tsfile listing all rate limit values for easy adjustment.2. Helmet Security Headers
Install
@fastify/helmetor use NestJS Helmet middleware (if using Express):Actually, since
main.tsis off-limits per the project rules, implement Helmet as aNestMiddlewareand apply it inAppModule.configure():Headers to configure:
Content-Security-Policy: restrict to self + cdn sourcesX-Frame-Options: DENYX-Content-Type-Options: nosniffStrict-Transport-Security(HSTS)X-Powered-By: Express3. Webhook Signature Validation (for all external webhooks)
Ensure all webhook endpoints validate signatures:
POST /payments/webhook): validatestripe-signatureheader usingstripe.webhooks.constructEvent()— already planned in [BE-23], verify this is implemented correctly4. SQL Injection & Input Hardening Audit
Review all TypeORM QueryBuilder calls in:
GET /shipments(filter queries)GET /admin/usersGET /carriersGET /notificationsEnsure all user-supplied strings are passed as TypeORM parameters (never string-interpolated into query strings). Fix any instances where
.where(field = ${variable})is used instead of.where('field = :val', { val: variable }).5. Environment Variable Validation (Joi Schema)
Add Joi validation for all new env vars added in issues BE-10 through BE-29:
validation.schema.ts(or create it if not present)Joi.when('NODE_ENV', { is: 'production', then: Joi.required() }))Acceptance Criteria
/auth/loginreturns429after 5 requests per minute from the same IP/auth/forgot-passwordreturns429after 3 requests per 15 minutes for the same email429after 10 uploads per hourcurl -I)X-Powered-Byheader is removed from all responses400for requests with an invalid signature