security: full hardening pass — auth guards, rate limiting, headers, internal secret
- Add requireAdmin() to all unprotected admin server actions (clients/new, clients/[id], timer-actions — 17 functions total) - Protect /api/internal/* endpoints with X-Internal-Secret header (proxy.ts sends it; routes reject requests without it) - Randomize auto-generated client slugs with 4-char suffix to prevent enumeration via predictable name-based slugs - Add in-memory rate limiting to /api/client/approve (20/min) and /api/client/comment (10/min) per IP - Add security headers: X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, X-DNS-Prefetch-Control - Reduce JWT session from 30 days to 7 days with daily rotation - Remove hardcoded NEXTAUTH_URL from Dockerfile (pass via Coolify env) - Genericize client API error messages to not leak data structure - Update .env.example with all required variables including INTERNAL_SECRET Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -33,7 +33,8 @@ export const authOptions: NextAuthOptions = {
|
||||
],
|
||||
session: {
|
||||
strategy: "jwt", // stateless JWT — no DB session table (per D-03)
|
||||
maxAge: 30 * 24 * 60 * 60, // 30 days
|
||||
maxAge: 7 * 24 * 60 * 60, // 7 days (reduced from 30 for security)
|
||||
updateAge: 24 * 60 * 60, // rotate token daily on active use
|
||||
},
|
||||
pages: {
|
||||
signIn: "/admin/login", // custom login page (per D-07)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// In-memory rate limiter — suitable for single-container deployments.
|
||||
// Each bucket tracks hit count within a rolling window.
|
||||
|
||||
const buckets = new Map<string, { hits: number; resetAt: number }>();
|
||||
|
||||
export function rateLimit(key: string, limit: number, windowMs: number): boolean {
|
||||
const now = Date.now();
|
||||
const bucket = buckets.get(key);
|
||||
|
||||
if (!bucket || now >= bucket.resetAt) {
|
||||
buckets.set(key, { hits: 1, resetAt: now + windowMs });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (bucket.hits >= limit) return false;
|
||||
bucket.hits++;
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user