feat(security): implement comprehensive security hardening

- Add CSRF protection with cookie-based tokens
  - Add cookie-parser and csurf middleware
  - Create GET /api/csrf-token endpoint
  - Frontend automatically includes CSRF token in POST/PUT/DELETE requests
  - Add retry logic for expired CSRF tokens

- Implement account lockout mechanism
  - Add database fields: failedLoginAttempts, lockedUntil
  - Track failed login attempts and lock accounts after max attempts (configurable)
  - Auto-unlock after lockout duration expires
  - Return helpful error messages with remaining time

- Add comprehensive security environment variables
  - Rate limiting configuration (API, auth, email endpoints)
  - CSRF protection toggle
  - Password policy requirements
  - Account lockout settings
  - Logging levels

- Add comprehensive test coverage
  - 6 new tests for account lockout functionality
  - 11 new tests for CSRF protection
  - All tests handle enabled/disabled states gracefully

- Update documentation
  - Add Phase 3 security hardening to SESSION_CONTEXT.md
  - Document new database fields and migration
  - Update progress to 85%

Files changed:
- Backend: app.js, auth controller, security config, new migration
- Frontend: api.js with CSRF token handling
- Tests: auth.test.js (extended), csrf.test.js (new)
- Config: .env examples with security variables
- Docs: SESSION_CONTEXT.md updated
This commit is contained in:
Radosław Gierwiało
2025-11-19 20:16:05 +01:00
parent cbc970f60b
commit 44df50362a
10 changed files with 687 additions and 12 deletions

View File

@@ -1,6 +1,8 @@
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const cookieParser = require('cookie-parser');
const csrf = require('csurf');
const securityConfig = require('./config/security');
const { apiLimiter } = require('./middleware/rateLimiter');
@@ -53,6 +55,40 @@ app.use(cors({
app.use(express.json({ limit: securityConfig.bodyLimit }));
app.use(express.urlencoded({ extended: true, limit: securityConfig.bodyLimit }));
// Cookie parser (required for CSRF protection)
app.use(cookieParser());
// CSRF Protection (Phase 3 - Security Hardening)
if (securityConfig.csrf.enabled) {
const csrfProtection = csrf({
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production', // HTTPS only in production
sameSite: 'strict',
}
});
// Apply CSRF protection to all routes
app.use(csrfProtection);
// CSRF token endpoint - provides token to clients
app.get('/api/csrf-token', (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// CSRF error handler
app.use((err, req, res, next) => {
if (err.code === 'EBADCSRFTOKEN') {
return res.status(403).json({
success: false,
error: 'Invalid CSRF token',
message: 'Form submission failed. Please refresh the page and try again.',
});
}
next(err);
});
}
// Request logging middleware
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);