- Create Economist SubmissionTracking package correctly: * mainArticle = full blog post content * coverLetter = 216-word SIR— letter * Links to blog post via blogPostId - Archive 'Letter to The Economist' from blog posts (it's the cover letter) - Fix date display on article cards (use published_at) - Target publication already displaying via blue badge Database changes: - Make blogPostId optional in SubmissionTracking model - Economist package ID: 68fa85ae49d4900e7f2ecd83 - Le Monde package ID: 68fa2abd2e6acd5691932150 Next: Enhanced modal with tabs, validation, export 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
427 lines
11 KiB
Markdown
427 lines
11 KiB
Markdown
# Security Quick Wins Implementation Guide
|
|
**Get 80% of security value with 20% of effort**
|
|
|
|
**Status:** Ready to Deploy
|
|
**Time to Implement:** 1-2 hours
|
|
**Value:** HIGH - Immediate protection against common attacks
|
|
|
|
---
|
|
|
|
## What You're Getting
|
|
|
|
✅ **Security Headers** - Prevents XSS, clickjacking, MIME sniffing
|
|
✅ **Input Validation** - Sanitizes HTML, enforces length limits
|
|
✅ **Rate Limiting** - Prevents brute force, DoS, spam
|
|
✅ **CSRF Protection** - Prevents cross-site request forgery
|
|
✅ **Security Logging** - Audit trail for all security events
|
|
✅ **Response Sanitization** - Hides stack traces and sensitive data
|
|
|
|
**What This Protects Against:**
|
|
- Cross-Site Scripting (XSS) attacks
|
|
- Cross-Site Request Forgery (CSRF)
|
|
- Clickjacking
|
|
- MIME type confusion attacks
|
|
- Brute force authentication
|
|
- Form spam
|
|
- DoS attacks
|
|
- Information disclosure
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
### 1. Install Dependencies
|
|
```bash
|
|
cd /home/theflow/projects/tractatus
|
|
|
|
# Install required npm packages
|
|
npm install express-rate-limit validator csurf cookie-parser
|
|
```
|
|
|
|
### 2. Create Log Directory
|
|
```bash
|
|
# Create security log directory
|
|
sudo mkdir -p /var/log/tractatus
|
|
sudo chown -R $USER:$USER /var/log/tractatus
|
|
sudo chmod 750 /var/log/tractatus
|
|
```
|
|
|
|
---
|
|
|
|
## Step 1: Update src/server.js
|
|
|
|
Add the following to your `src/server.js` file. **Insert in the order shown:**
|
|
|
|
```javascript
|
|
const express = require('express');
|
|
const cookieParser = require('cookie-parser');
|
|
const csrf = require('csurf');
|
|
|
|
// Import security middleware
|
|
const { securityHeadersMiddleware } = require('./middleware/security-headers.middleware');
|
|
const { publicRateLimiter } = require('./middleware/rate-limit.middleware');
|
|
const { sanitizeErrorResponse, sanitizeResponseData } = require('./middleware/response-sanitization.middleware');
|
|
|
|
const app = express();
|
|
|
|
// ============================================================
|
|
// SECURITY MIDDLEWARE (Apply BEFORE routes)
|
|
// ============================================================
|
|
|
|
// 1. Security Headers (HIGH VALUE - 5 minutes to add)
|
|
app.use(securityHeadersMiddleware);
|
|
|
|
// 2. Cookie Parser (required for CSRF)
|
|
app.use(cookieParser());
|
|
|
|
// 3. Body Parsers
|
|
app.use(express.json({ limit: '1mb' })); // Payload size limit
|
|
app.use(express.urlencoded({ extended: true, limit: '1mb' }));
|
|
|
|
// 4. Response Data Sanitization
|
|
app.use(sanitizeResponseData);
|
|
|
|
// 5. Public Rate Limiting (100 req/15min per IP)
|
|
app.use(publicRateLimiter);
|
|
|
|
// 6. CSRF Protection (POST/PUT/DELETE/PATCH only)
|
|
const csrfProtection = csrf({ cookie: true });
|
|
app.use((req, res, next) => {
|
|
if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) {
|
|
return csrfProtection(req, res, next);
|
|
}
|
|
next();
|
|
});
|
|
|
|
// ============================================================
|
|
// YOUR EXISTING ROUTES
|
|
// ============================================================
|
|
|
|
// CSRF token endpoint (for forms)
|
|
app.get('/api/csrf-token', (req, res) => {
|
|
res.json({ csrfToken: req.csrfToken() });
|
|
});
|
|
|
|
// ... your existing routes here ...
|
|
|
|
// ============================================================
|
|
// ERROR HANDLING (Apply AFTER routes)
|
|
// ============================================================
|
|
|
|
// CSRF Error Handler
|
|
app.use((err, req, res, next) => {
|
|
if (err.code === 'EBADCSRFTOKEN') {
|
|
return res.status(403).json({
|
|
error: 'Invalid CSRF token',
|
|
message: 'Request blocked for security reasons'
|
|
});
|
|
}
|
|
next(err);
|
|
});
|
|
|
|
// General Error Handler (hides stack traces in production)
|
|
app.use(sanitizeErrorResponse);
|
|
|
|
// Start server
|
|
const PORT = process.env.PORT || 9000;
|
|
app.listen(PORT, () => {
|
|
console.log(`Server running on port ${PORT}`);
|
|
console.log('✅ Security middleware active');
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Step 2: Add Form-Specific Rate Limiting
|
|
|
|
For form submission endpoints (contact, cases, media inquiries), add stricter rate limiting:
|
|
|
|
```javascript
|
|
const { formRateLimiter } = require('./middleware/rate-limit.middleware');
|
|
const { createInputValidationMiddleware } = require('./middleware/input-validation.middleware');
|
|
|
|
// Example: Case submission endpoint
|
|
app.post('/api/cases/submit',
|
|
formRateLimiter, // 5 submissions/min
|
|
createInputValidationMiddleware({
|
|
title: { type: 'text', required: true, maxLength: 200 },
|
|
description: { type: 'text', required: true, maxLength: 5000 },
|
|
contact_email: { type: 'email', required: true },
|
|
contact_name: { type: 'text', required: true, maxLength: 100 }
|
|
}),
|
|
casesController.submitCase
|
|
);
|
|
|
|
// Example: Contact form
|
|
app.post('/api/contact',
|
|
formRateLimiter,
|
|
createInputValidationMiddleware({
|
|
name: { type: 'text', required: true, maxLength: 100 },
|
|
email: { type: 'email', required: true },
|
|
message: { type: 'text', required: true, maxLength: 5000 }
|
|
}),
|
|
contactController.submitContact
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Step 3: Update Client-Side Forms for CSRF
|
|
|
|
Add CSRF token to all forms that POST/PUT/DELETE:
|
|
|
|
```javascript
|
|
// Fetch CSRF token before form submission
|
|
async function submitForm(formData) {
|
|
try {
|
|
// Get CSRF token
|
|
const tokenResponse = await fetch('/api/csrf-token');
|
|
const { csrfToken } = await tokenResponse.json();
|
|
|
|
// Add CSRF token to form data
|
|
formData.append('_csrf', csrfToken);
|
|
|
|
// Submit form
|
|
const response = await fetch('/api/cases/submit', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Submission failed');
|
|
}
|
|
|
|
return await response.json();
|
|
} catch (error) {
|
|
console.error('Form submission error:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
```
|
|
|
|
**For JSON submissions:**
|
|
```javascript
|
|
async function submitJSON(data) {
|
|
// Get CSRF token
|
|
const tokenResponse = await fetch('/api/csrf-token');
|
|
const { csrfToken } = await tokenResponse.json();
|
|
|
|
// Submit with CSRF token
|
|
const response = await fetch('/api/cases/submit', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'CSRF-Token': csrfToken // Can also use header instead of body
|
|
},
|
|
body: JSON.stringify({
|
|
...data,
|
|
_csrf: csrfToken // Or include in body
|
|
})
|
|
});
|
|
|
|
return response.json();
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Step 4: Test the Security Measures
|
|
|
|
### Test Security Headers
|
|
```bash
|
|
# Check headers are present
|
|
curl -I http://localhost:9000
|
|
|
|
# Expected headers:
|
|
# Content-Security-Policy: ...
|
|
# X-Content-Type-Options: nosniff
|
|
# X-Frame-Options: DENY
|
|
# X-XSS-Protection: 1; mode=block
|
|
# Referrer-Policy: strict-origin-when-cross-origin
|
|
# Permissions-Policy: geolocation=(), ...
|
|
```
|
|
|
|
### Test Rate Limiting
|
|
```bash
|
|
# Exceed public limit (should get 429 after 100 requests)
|
|
for i in {1..110}; do
|
|
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:9000
|
|
done
|
|
|
|
# Expected: First 100 return 200, remaining return 429
|
|
```
|
|
|
|
### Test Input Validation
|
|
```bash
|
|
# Test XSS payload (should be sanitized)
|
|
curl -X POST http://localhost:9000/api/contact \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"name":"<script>alert(1)</script>","email":"test@example.com","message":"test"}'
|
|
|
|
# Expected: Name sanitized to empty or plain text
|
|
```
|
|
|
|
### Test CSRF Protection
|
|
```bash
|
|
# Request without CSRF token (should be rejected)
|
|
curl -X POST http://localhost:9000/api/contact \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"name":"Test","email":"test@example.com","message":"test"}'
|
|
|
|
# Expected: 403 Forbidden with "Invalid CSRF token"
|
|
```
|
|
|
|
### Check Security Logs
|
|
```bash
|
|
# View security events
|
|
tail -f /var/log/tractatus/security-audit.log
|
|
|
|
# Should see JSON entries for:
|
|
# - rate_limit_exceeded
|
|
# - input_sanitized
|
|
# - input_validation_failure
|
|
```
|
|
|
|
---
|
|
|
|
## Step 5: Deploy to Production
|
|
|
|
### 1. Commit Changes
|
|
```bash
|
|
git add src/middleware/ src/utils/security-logger.js src/server.js
|
|
git commit -m "security: implement quick wins (headers, rate limiting, CSRF, input validation)"
|
|
```
|
|
|
|
### 2. Deploy to Production
|
|
```bash
|
|
# Your deployment script
|
|
./scripts/deploy-full-project-SAFE.sh
|
|
|
|
# Or manual deployment
|
|
rsync -avz --chmod=D755,F644 -e "ssh -i ~/.ssh/tractatus_deploy" \
|
|
src/middleware/ src/utils/security-logger.js \
|
|
ubuntu@vps-93a693da.vps.ovh.net:/var/www/tractatus/src/
|
|
|
|
# Restart production server
|
|
ssh -i ~/.ssh/tractatus_deploy ubuntu@vps-93a693da.vps.ovh.net \
|
|
"sudo systemctl restart tractatus"
|
|
```
|
|
|
|
### 3. Verify Production
|
|
```bash
|
|
# Check headers on production
|
|
curl -I https://agenticgovernance.digital
|
|
|
|
# Test rate limiting
|
|
for i in {1..110}; do
|
|
curl -s -o /dev/null -w "%{http_code}\n" https://agenticgovernance.digital
|
|
done
|
|
|
|
# Check SecurityHeaders.com grade
|
|
# Visit: https://securityheaders.com/?q=https://agenticgovernance.digital
|
|
# Expected: Grade A or A+
|
|
```
|
|
|
|
### 4. Monitor for Issues
|
|
```bash
|
|
# Watch production logs
|
|
ssh -i ~/.ssh/tractatus_deploy ubuntu@vps-93a693da.vps.ovh.net \
|
|
"tail -f /var/www/tractatus/logs/combined.log"
|
|
|
|
# Watch security logs
|
|
ssh -i ~/.ssh/tractatus_deploy ubuntu@vps-93a693da.vps.ovh.net \
|
|
"tail -f /var/log/tractatus/security-audit.log"
|
|
```
|
|
|
|
---
|
|
|
|
## What's Next? (Phase 1-6)
|
|
|
|
These quick wins give you immediate protection. When ready for comprehensive security:
|
|
|
|
1. **Phase 1** - Install ClamAV, YARA, fail2ban, Redis
|
|
2. **Phase 2** - File upload malware scanning, email security
|
|
3. **Phase 3** - Enhanced input validation (NoSQL injection, XSS detection)
|
|
4. **Phase 4** - JWT authentication, Redis rate limiting, IP blocking
|
|
5. **Phase 5** - Security dashboard, ProtonMail alerts, Signal notifications
|
|
6. **Phase 6** - Penetration testing, team training, external audit
|
|
|
|
See `docs/plans/security-implementation-roadmap.md` for full details.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### "Cannot find module 'express-rate-limit'"
|
|
```bash
|
|
npm install express-rate-limit validator csurf cookie-parser
|
|
```
|
|
|
|
### "Permission denied: /var/log/tractatus/"
|
|
```bash
|
|
sudo mkdir -p /var/log/tractatus
|
|
sudo chown -R $USER:$USER /var/log/tractatus
|
|
sudo chmod 750 /var/log/tractatus
|
|
```
|
|
|
|
### CSRF token errors on GET requests
|
|
CSRF protection is only applied to POST/PUT/DELETE/PATCH. GET requests should not be affected.
|
|
|
|
### Rate limit too restrictive
|
|
Adjust limits in `src/middleware/rate-limit.middleware.js`:
|
|
```javascript
|
|
const publicRateLimiter = createRateLimiter({
|
|
windowMs: 15 * 60 * 1000,
|
|
max: 200, // Increase from 100 to 200
|
|
tier: 'public'
|
|
});
|
|
```
|
|
|
|
### Security headers causing issues
|
|
Temporarily disable specific headers in `src/middleware/security-headers.middleware.js` while debugging.
|
|
|
|
---
|
|
|
|
## Performance Impact
|
|
|
|
Expected performance impact: **<10ms per request**
|
|
|
|
- Security headers: <1ms (set headers only)
|
|
- Input validation: 2-5ms (depends on input size)
|
|
- Rate limiting: 1-2ms (in-memory lookup)
|
|
- CSRF validation: 1-2ms (token comparison)
|
|
- Response sanitization: 1-2ms (field removal)
|
|
|
|
**Total:** ~5-10ms additional latency per request (negligible for most applications)
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
✅ Security headers present on all responses
|
|
✅ SecurityHeaders.com grade A or A+
|
|
✅ Rate limiting preventing abuse (429 errors after limits)
|
|
✅ CSRF tokens required for POST/PUT/DELETE
|
|
✅ Input sanitization removing HTML tags
|
|
✅ Security events logged to `/var/log/tractatus/security-audit.log`
|
|
✅ Error responses sanitized (no stack traces in production)
|
|
✅ Zero false positives with legitimate traffic
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
**Questions?** Check:
|
|
- `docs/plans/security-implementation-roadmap.md` - Full implementation plan
|
|
- `docs/plans/security-implementation-tracker.md` - Phase tracking
|
|
- `.claude/instruction-history.json` - inst_041 through inst_046
|
|
|
|
**Issues?** Create incident report in `docs/security/incidents/`
|
|
|
|
**Ready for More?** Proceed to Phase 1 in the tracker!
|
|
|
|
---
|
|
|
|
**Last Updated:** 2025-10-14
|
|
**Version:** 1.0 (Quick Wins)
|
|
**Next:** Full Phase 1-6 implementation
|