feat(api): add helmet security headers and rate limiting

- Add helmet middleware for secure HTTP response headers
- Add express-rate-limit with three limiters:
  - authLimiter: per-IP, 20 req/15min on /api/auth/*
  - gameLimiter: per-user, 150 req/15min (not yet wired)
  - lobbyLimiter: per-user, 20 req/15min (not yet wired)
- Set trust proxy for correct client IP behind Caddy
- Add tests for all three limiters and helmet headers
This commit is contained in:
lila 2026-04-23 11:13:11 +02:00
parent 1dfe391233
commit 9893ead689
6 changed files with 300 additions and 1 deletions

29
pnpm-lock.yaml generated
View file

@ -62,6 +62,12 @@ importers:
express:
specifier: ^5.2.1
version: 5.2.1
express-rate-limit:
specifier: ^8.4.0
version: 8.4.0(express@5.2.1)
helmet:
specifier: ^8.1.0
version: 8.1.0
ws:
specifier: ^8.20.0
version: 8.20.0
@ -2045,6 +2051,12 @@ packages:
resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
engines: {node: '>=12.0.0'}
express-rate-limit@8.4.0:
resolution: {integrity: sha512-gDK8yiqKxrGta+3WtON59arrrw6GLmadA1qoFgYXzdcch8fmKDID2XqO8itsi3f1wufXYPT51387dN6cvVBS3Q==}
engines: {node: '>= 16'}
peerDependencies:
express: '>= 4.11'
express@5.2.1:
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
engines: {node: '>= 18'}
@ -2185,6 +2197,10 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
helmet@8.1.0:
resolution: {integrity: sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==}
engines: {node: '>=18.0.0'}
hermes-estree@0.25.1:
resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==}
@ -2227,6 +2243,10 @@ packages:
ini@1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
ip-address@10.1.0:
resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==}
engines: {node: '>= 12'}
ipaddr.js@1.9.1:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
@ -4821,6 +4841,11 @@ snapshots:
expect-type@1.3.0: {}
express-rate-limit@8.4.0(express@5.2.1):
dependencies:
express: 5.2.1
ip-address: 10.1.0
express@5.2.1:
dependencies:
accepts: 2.0.0
@ -4982,6 +5007,8 @@ snapshots:
dependencies:
function-bind: 1.1.2
helmet@8.1.0: {}
hermes-estree@0.25.1: {}
hermes-parser@0.25.1:
@ -5020,6 +5047,8 @@ snapshots:
ini@1.3.8: {}
ip-address@10.1.0: {}
ipaddr.js@1.9.1: {}
is-binary-path@2.1.0: