fix: Implement CSRF protection for Trinity Console

CRITICAL SECURITY FIX - Prevents Cross-Site Request Forgery attacks

Changes:
- Installed csurf middleware (session-based tokens)
- Added CSRF middleware to all /admin routes in src/index.js
- Configured admin router to pass csrfToken to all views
- Updated layout.ejs to send CSRF token with htmx requests
- Added EJS view engine configuration
- Added body parsing middleware (json + urlencoded)

Security Impact:
- Prevents malicious sites from executing admin actions using cookies
- All POST requests now require valid CSRF token
- Invalid tokens return 403 Forbidden
- Session-based tokens (no cookies needed)

Protected Routes:
- /admin/servers/:id/sync (force whitelist sync)
- /admin/servers/:id/toggle-whitelist (whitelist toggle)
- /admin/grace/:id/extend (grace period extension)
- /admin/grace/:id/manual (manual payment override)
- /admin/roles/resync/:id (role assignment)

Attack Scenario Prevented:
User visits malicious site while logged into Trinity Console
→ Site tries to submit form to admin endpoint
→ Request includes session cookie but NO CSRF token
→ Server rejects with 403 Forbidden
→ Attack failed!

Note: csurf is deprecated but still functional. For future refactor,
consider csrf-csrf or Express 5 built-in protection.

Refs: TRINITY-CONSOLE-PRE-LAUNCH-CHECKLIST.md - Fix #1
Chronicler: #51

Signed-off-by: Claude (Chronicler #51) <claude@firefrostgaming.com>
This commit is contained in:
Claude (Chronicler #51)
2026-04-01 05:27:40 +00:00
parent a1afb78646
commit 2386919998
4 changed files with 24 additions and 1 deletions

View File

@@ -10,6 +10,7 @@
"dependencies": {
"body-parser": "^1.20.2",
"cookie-parser": "^1.4.7",
"csurf": "^1.11.0",
"discord.js": "^14.14.1",
"dotenv": "^16.4.5",
"ejs": "^3.1.9",

View File

@@ -4,6 +4,7 @@ const session = require('express-session');
const passport = require('passport');
const DiscordStrategy = require('passport-discord').Strategy;
const { Client, GatewayIntentBits, REST, Routes } = require('discord.js');
const csrf = require('csurf');
const authRoutes = require('./routes/auth');
const adminRoutes = require('./routes/admin');
@@ -32,6 +33,12 @@ passport.use(new DiscordStrategy({
// Initialize Express App
const app = express();
app.set('trust proxy', 1);
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
// Body parsing middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Make Discord client accessible to routes
app.locals.client = client;
@@ -57,9 +64,12 @@ app.get('/health', (req, res) => {
});
});
// CSRF Protection (session-based)
const csrfProtection = csrf({ cookie: false });
// Register Routes
app.use('/auth', authRoutes);
app.use('/admin', adminRoutes);
app.use('/admin', csrfProtection, adminRoutes);
app.use('/webhook', webhookRoutes);
// Start Application

View File

@@ -12,6 +12,12 @@ const rolesRouter = require('./roles');
router.use(requireTrinityAccess);
// Make CSRF token available to all admin views
router.use((req, res, next) => {
res.locals.csrfToken = req.csrfToken();
next();
});
router.get('/', (req, res) => {
res.redirect('/admin/dashboard');
});

View File

@@ -6,6 +6,12 @@
<title><%= title %> | Trinity Console</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/htmx.org@1.9.11"></script>
<script>
// Configure htmx to include CSRF token in all requests
document.body.addEventListener('htmx:configRequest', function(evt) {
evt.detail.headers['CSRF-Token'] = '<%= csrfToken %>';
});
</script>
<script>
tailwind.config = {
darkMode: 'class',