15 KiB
15 KiB
🔒 Sicherheit
Sicherheitsrichtlinien und Best Practices für das Ninja Cross Parkour System.
📋 Inhaltsverzeichnis
- 🛡️ Sicherheitsübersicht
- 🔐 Authentifizierung
- 🔑 Autorisierung
- 🛡️ Datenverschlüsselung
- 🌐 Netzwerksicherheit
- 🗄️ Datenbanksicherheit
- 📱 API-Sicherheit
- 🔍 Monitoring
- 🚨 Incident Response
🛡️ Sicherheitsübersicht
Sicherheitsprinzipien
- Defense in Depth - Mehrschichtige Sicherheit
- Least Privilege - Minimale Berechtigungen
- Zero Trust - Kein Vertrauen ohne Verifikation
- Security by Design - Sicherheit von Anfang an
Bedrohungsmodell
- Externe Angriffe - Unbefugter Zugriff von außen
- Interne Bedrohungen - Missbrauch durch autorisierte Benutzer
- Datenlecks - Unbefugte Offenlegung von Daten
- Service-Ausfälle - Verfügbarkeitsprobleme
🔐 Authentifizierung
Passwort-Sicherheit
// Passwort-Hashing mit bcrypt
const bcrypt = require('bcrypt');
// Passwort hashen
const saltRounds = 12;
const hashedPassword = await bcrypt.hash(password, saltRounds);
// Passwort verifizieren
const isValid = await bcrypt.compare(password, hashedPassword);
Passwort-Richtlinien
- Mindestlänge: 12 Zeichen
- Komplexität: Groß-/Kleinbuchstaben, Zahlen, Sonderzeichen
- Keine Wörterbuchwörter
- Regelmäßige Rotation (alle 90 Tage)
Multi-Faktor-Authentifizierung (MFA)
// TOTP-Implementierung
const speakeasy = require('speakeasy');
// Secret generieren
const secret = speakeasy.generateSecret({
name: 'Ninja Parkour',
account: 'admin@example.com'
});
// Token verifizieren
const verified = speakeasy.totp.verify({
secret: secret.base32,
encoding: 'base32',
token: userToken,
window: 2
});
Session-Management
// Sichere Session-Konfiguration
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000, // 24 Stunden
sameSite: 'strict'
}
}));
🔑 Autorisierung
Rollenbasierte Zugriffskontrolle (RBAC)
// Rollen definieren
const ROLES = {
ADMIN: 'admin',
MODERATOR: 'moderator',
USER: 'user',
GUEST: 'guest'
};
// Berechtigungen definieren
const PERMISSIONS = {
READ_PLAYERS: 'read:players',
WRITE_PLAYERS: 'write:players',
DELETE_PLAYERS: 'delete:players',
READ_TIMES: 'read:times',
WRITE_TIMES: 'write:times',
ADMIN_ACCESS: 'admin:access'
};
// Rollen-Berechtigungen
const rolePermissions = {
[ROLES.ADMIN]: Object.values(PERMISSIONS),
[ROLES.MODERATOR]: [
PERMISSIONS.READ_PLAYERS,
PERMISSIONS.WRITE_PLAYERS,
PERMISSIONS.READ_TIMES,
PERMISSIONS.WRITE_TIMES
],
[ROLES.USER]: [
PERMISSIONS.READ_PLAYERS,
PERMISSIONS.READ_TIMES
]
};
API-Key-Management
// API-Key generieren
const generateAPIKey = () => {
return crypto.randomBytes(32).toString('hex');
};
// API-Key validieren
const validateAPIKey = async (apiKey) => {
const token = await db.query(
'SELECT * FROM api_tokens WHERE token = $1 AND is_active = true',
[apiKey]
);
return token.rows.length > 0;
};
// Berechtigungen prüfen
const checkPermission = (userRole, requiredPermission) => {
const userPermissions = rolePermissions[userRole] || [];
return userPermissions.includes(requiredPermission);
};
Middleware für Autorisierung
// Authentifizierung prüfen
const requireAuth = (req, res, next) => {
if (!req.session || !req.session.userId) {
return res.status(401).json({ error: 'Nicht authentifiziert' });
}
next();
};
// Rolle prüfen
const requireRole = (role) => {
return (req, res, next) => {
if (req.session.role !== role) {
return res.status(403).json({ error: 'Keine Berechtigung' });
}
next();
};
};
// Berechtigung prüfen
const requirePermission = (permission) => {
return (req, res, next) => {
const userRole = req.session.role;
if (!checkPermission(userRole, permission)) {
return res.status(403).json({ error: 'Keine Berechtigung' });
}
next();
};
};
🛡️ Datenverschlüsselung
Verschlüsselung im Ruhezustand
// Datenbank-Verschlüsselung
const crypto = require('crypto');
// Verschlüsselung
const encrypt = (text, key) => {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipher('aes-256-cbc', key);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return iv.toString('hex') + ':' + encrypted;
};
// Entschlüsselung
const decrypt = (text, key) => {
const textParts = text.split(':');
const iv = Buffer.from(textParts.shift(), 'hex');
const encryptedText = textParts.join(':');
const decipher = crypto.createDecipher('aes-256-cbc', key);
let decrypted = decipher.update(encryptedText, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
};
Verschlüsselung in Bewegung
// HTTPS-Konfiguration
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem'),
ciphers: [
'ECDHE-RSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES256-SHA384',
'ECDHE-RSA-AES128-SHA256'
].join(':'),
honorCipherOrder: true
};
https.createServer(options, app).listen(443);
Passwort-Verschlüsselung
// Starke Passwort-Generierung
const generatePassword = (length = 16) => {
const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*';
let password = '';
for (let i = 0; i < length; i++) {
password += charset.charAt(Math.floor(Math.random() * charset.length));
}
return password;
};
// Passwort-Stärke prüfen
const validatePassword = (password) => {
const minLength = 12;
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
const hasNumbers = /\d/.test(password);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
return password.length >= minLength &&
hasUpperCase &&
hasLowerCase &&
hasNumbers &&
hasSpecialChar;
};
🌐 Netzwerksicherheit
Firewall-Konfiguration
# UFW-Firewall (Ubuntu)
sudo ufw enable
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Erlaubte Ports
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 3000/tcp # App (nur intern)
# Rate Limiting
sudo ufw limit ssh
DDoS-Schutz
// Rate Limiting
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 Minuten
max: 100, // Max 100 Requests pro IP
message: 'Zu viele Anfragen von dieser IP',
standardHeaders: true,
legacyHeaders: false
});
app.use('/api/', limiter);
// Strikte Rate Limits für sensible Endpoints
const strictLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5,
message: 'Zu viele Login-Versuche'
});
app.use('/api/login', strictLimiter);
CORS-Konfiguration
// Sichere CORS-Einstellungen
const cors = require('cors');
const corsOptions = {
origin: (origin, callback) => {
const allowedOrigins = [
'https://ninja.reptilfpv.de',
'https://www.ninja.reptilfpv.de'
];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Nicht erlaubt durch CORS'));
}
},
credentials: true,
optionsSuccessStatus: 200
};
app.use(cors(corsOptions));
SSL/TLS-Konfiguration
// SSL-Redirect
app.use((req, res, next) => {
if (req.header('x-forwarded-proto') !== 'https') {
res.redirect(`https://${req.header('host')}${req.url}`);
} else {
next();
}
});
// Security Headers
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"]
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
🗄️ Datenbanksicherheit
Verbindungssicherheit
// Sichere Datenbankverbindung
const dbConfig = {
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
ssl: {
rejectUnauthorized: true,
ca: fs.readFileSync('ca-certificate.pem')
},
connectionTimeoutMillis: 5000,
idleTimeoutMillis: 30000,
max: 20
};
SQL-Injection-Schutz
// Parametrisierte Queries
const getUser = async (userId) => {
const query = 'SELECT * FROM players WHERE id = $1';
const values = [userId];
const result = await db.query(query, values);
return result.rows[0];
};
// Input-Validierung
const validateInput = (input) => {
if (typeof input !== 'string') {
throw new Error('Invalid input type');
}
// SQL-Injection-Patterns erkennen
const sqlPatterns = [
/(\b(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER|EXEC|UNION|SCRIPT)\b)/gi,
/(\b(OR|AND)\s+\d+\s*=\s*\d+)/gi,
/(\b(OR|AND)\s+['"]\s*=\s*['"])/gi
];
for (const pattern of sqlPatterns) {
if (pattern.test(input)) {
throw new Error('Potential SQL injection detected');
}
}
return input;
};
Datenbank-Berechtigungen
-- Benutzer mit minimalen Rechten erstellen
CREATE USER ninja_app WITH PASSWORD 'secure_password';
-- Nur notwendige Berechtigungen gewähren
GRANT SELECT, INSERT, UPDATE, DELETE ON players TO ninja_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON times TO ninja_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON locations TO ninja_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON achievements TO ninja_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON player_achievements TO ninja_app;
-- Keine Admin-Rechte
REVOKE ALL ON SCHEMA public FROM ninja_app;
📱 API-Sicherheit
Input-Validierung
// Joi-Schema für Validierung
const Joi = require('joi');
const playerSchema = Joi.object({
firstname: Joi.string().min(2).max(50).required(),
lastname: Joi.string().min(2).max(50).required(),
birthdate: Joi.date().max('now').required(),
rfiduid: Joi.string().pattern(/^[A-F0-9:]{11}$/).optional()
});
const validatePlayer = (req, res, next) => {
const { error } = playerSchema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}
next();
};
API-Versionierung
// API-Versionierung
app.use('/api/v1', v1Routes);
app.use('/api/v2', v2Routes);
// Deprecation-Warnungen
app.use('/api/v1', (req, res, next) => {
res.set('X-API-Version', '1.0.0');
res.set('X-API-Deprecated', 'false');
next();
});
Request-Logging
// Sicherheitsrelevante Logs
const securityLogger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'logs/security.log' })
]
});
// Login-Versuche loggen
app.post('/api/login', (req, res) => {
const { username, ip } = req.body;
securityLogger.info('Login attempt', { username, ip, timestamp: new Date() });
// ... Login-Logik
});
🔍 Monitoring
Sicherheits-Monitoring
// Anomalie-Erkennung
const detectAnomalies = (req, res, next) => {
const ip = req.ip;
const userAgent = req.get('User-Agent');
// Verdächtige Patterns erkennen
const suspiciousPatterns = [
/sqlmap/i,
/nikto/i,
/nmap/i,
/masscan/i
];
for (const pattern of suspiciousPatterns) {
if (pattern.test(userAgent)) {
securityLogger.warn('Suspicious user agent detected', { ip, userAgent });
return res.status(403).json({ error: 'Request blocked' });
}
}
next();
};
Log-Analyse
# Sicherheitsrelevante Logs analysieren
grep "ERROR\|WARN\|SECURITY" logs/server.log | tail -100
# Failed Login-Versuche
grep "Login attempt" logs/security.log | grep "failed"
# Verdächtige Aktivitäten
grep "suspicious\|anomaly\|attack" logs/security.log
Alerting
// E-Mail-Benachrichtigungen
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransporter({
host: 'smtp.gmail.com',
port: 587,
secure: false,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
});
const sendSecurityAlert = async (message) => {
await transporter.sendMail({
from: process.env.EMAIL_USER,
to: process.env.ADMIN_EMAIL,
subject: 'Security Alert - Ninja Parkour',
text: message
});
};
🚨 Incident Response
Incident-Response-Plan
- Erkennung - Monitoring und Alerts
- Bewertung - Schweregrad bestimmen
- Eindämmung - Schaden begrenzen
- Eliminierung - Bedrohung entfernen
- Wiederherstellung - System reparieren
- Lektionen - Aus Fehlern lernen
Notfall-Kontakte
- Systemadministrator: +49 123 456 789
- Sicherheitsbeauftragter: security@ninjaparkour.de
- Management: management@ninjaparkour.de
Backup-Strategie
# Tägliche Backups
pg_dump -h localhost -U username -d ninjaserver | gzip > backup_$(date +%Y%m%d).sql.gz
# Wöchentliche Vollständige Backups
tar -czf full_backup_$(date +%Y%m%d).tar.gz /var/lib/postgresql/data/
# Backup-Verifizierung
psql -d ninjaserver < backup_$(date +%Y%m%d).sql
Recovery-Prozeduren
# System wiederherstellen
sudo systemctl stop ninjaserver
psql -d ninjaserver < latest_backup.sql
sudo systemctl start ninjaserver
# Datenbank reparieren
psql -d ninjaserver -c "VACUUM FULL;"
psql -d ninjaserver -c "REINDEX DATABASE ninjaserver;"
🔧 Sicherheits-Checkliste
Regelmäßige Überprüfungen
- Passwörter geändert (alle 90 Tage)
- API-Keys rotiert (alle 180 Tage)
- SSL-Zertifikate gültig
- Sicherheits-Updates installiert
- Logs überprüft
- Backups getestet
- Penetrationstests durchgeführt
Wöchentliche Aufgaben
- Sicherheits-Logs analysiert
- System-Updates geprüft
- Backup-Status überprüft
- Performance-Metriken analysiert
Monatliche Aufgaben
- Sicherheits-Audit durchgeführt
- Berechtigungen überprüft
- Incident-Response-Plan getestet
- Sicherheitsschulungen durchgeführt
Wichtig: Diese Sicherheitsrichtlinien müssen regelmäßig überprüft und aktualisiert werden. Bei Sicherheitsvorfällen wenden Sie sich sofort an den Sicherheitsbeauftragten.