
Web-Sicherheit: Ihre Anwendung vor Hackern schützen
Webanwendungen sind immer Ziele für Angriffe. Eine einzige Schwachstelle kann ein ganzes System zum Einsturz bringen. Dieser Leitfaden behandelt die häufigsten Web-Schwachstellen und ihre Abwehrmaßnahmen.
OWASP Top 10
OWASP (Open Web Application Security Project) veröffentlicht jährlich die Top 10 der gefährlichsten Web-Schwachstellen.
1. SQL Injection
Angriffsprinzip: Tritt auf, wenn Benutzereingaben ohne Validierung direkt in SQL-Abfragen eingefügt werden.
// Anfälliger Code (NIEMALS verwenden!)
app.get('/user', (req, res) => {
const userId = req.query.id;
const query = `SELECT * FROM users WHERE id = ${userId}`;
db.query(query, (err, results) => {
res.json(results);
});
});
// Angriffsbeispiele
// GET /user?id=1 OR 1=1 → Leckt alle Benutzerinformationen
// GET /user?id=1; DROP TABLE users; → Löscht Tabelle
Verteidigungsmethoden:
1) Prepared Statements
// Sicherer Code
app.get('/user', (req, res) => {
const userId = req.query.id;
const query = 'SELECT * FROM users WHERE id = ?';
db.query(query, [userId], (err, results) => {
res.json(results);
});
});
2) ORM verwenden
// Sequelize (ORM)
const user = await User.findOne({
where: { id: userId }
});
// Automatische SQL-Injection-Prävention
3) Eingabevalidierung
const validateUserId = (id) => {
const numericId = parseInt(id, 10);
if (isNaN(numericId) || numericId < 1) {
throw new Error("Ungültige ID");
}
return numericId;
};
2. XSS (Cross-Site Scripting)
Angriffsprinzip: Fügt bösartigen JavaScript-Code in Webseiten zur Ausführung ein.
// Anfälliger Code
app.get('/search', (req, res) => {
const keyword = req.query.q;
res.send(`<h1>Suchergebnisse: ${keyword}</h1>`);
});
// Angriffsbeispiel
// GET /search?q=<script>alert(document.cookie)</script>
Verteidigungsmethoden:
1) HTML-Escaping
const escapeHtml = (unsafe) => {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
};
2) Frameworks wie React verwenden
// React escaped automatisch
function SearchResults({ keyword }) {
return <h1>Suchergebnisse: {keyword}</h1>;
// <script>-Tags als Text angezeigt
}
3) Content Security Policy (CSP)
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' https://trusted-cdn.com"
);
next();
});
3. CSRF (Cross-Site Request Forgery)
Angriffsprinzip: Zwingt Benutzer, unbeabsichtigte Anfragen auszuführen.
<!-- Bösartige Website -->
<img src="https://bank.com/transfer?to=hacker&amount=1000000">
<!-- Wird automatisch ausgeführt, wenn Benutzer bei bank.com angemeldet ist -->
Verteidigungsmethoden:
1) CSRF-Tokens
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.post('/transfer', csrfProtection, (req, res) => {
processTransfer(req.body);
});
2) SameSite-Cookies
res.cookie('sessionId', sessionId, {
httpOnly: true,
secure: true,
sameSite: 'strict' // Blockiert Cookie-Sendung von anderen Websites
});
4. Authentifizierungs- und Sitzungsverwaltungs-Schwachstellen
Sichere Methoden:
1) Passwort-Hashing
const bcrypt = require('bcrypt');
// Registrierung
app.post('/signup', async (req, res) => {
const { username, password } = req.body;
const hashedPassword = await bcrypt.hash(password, 10);
await db.query(
'INSERT INTO users (username, password) VALUES (?, ?)',
[username, hashedPassword]
);
});
// Login
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = await db.query(
'SELECT * FROM users WHERE username = ?',
[username]
);
const isValid = await bcrypt.compare(password, user.password);
if (isValid) {
req.session.userId = user.id;
res.json({ success: true });
}
});
2) Sichere Sitzungsverwaltung
const session = require('express-session');
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: true, // Nur HTTPS
httpOnly: true, // JavaScript-Zugriff blockieren
maxAge: 3600000, // Ablauf nach 1 Stunde
sameSite: 'strict'
}
}));
5. Sensible Datenexposition
Sichere Methoden:
1) Nur notwendige Felder zurückgeben
app.get('/user/:id', async (req, res) => {
const user = await User.findById(req.params.id);
const safeUser = {
id: user.id,
username: user.username,
email: user.email,
// Passwort, SSN usw. ausschließen
};
res.json(safeUser);
});
2) Geheimnisse mit Umgebungsvariablen verwalten
require('dotenv').config();
const dbUrl = process.env.DATABASE_URL;
3) HTTPS verwenden
6. Datei-Upload-Schwachstellen
Sichere Methoden:
const multer = require('multer');
const storage = multer.diskStorage({
destination: './uploads/',
filename: (req, file, cb) => {
const uniqueName = `${Date.now()}-${Math.random().toString(36)}${path.extname(file.originalname)}`;
cb(null, uniqueName);
}
});
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('Dateityp nicht erlaubt'), false);
}
};
const upload = multer({
storage: storage,
fileFilter: fileFilter,
limits: {
fileSize: 5 * 1024 * 1024 // 5MB-Limit
}
});
Sicherheits-Header
const helmet = require('helmet');
app.use(helmet()); // Automatische Sicherheits-Header-Einrichtung
Rate Limiting
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 Minuten
max: 100, // Max 100 Anfragen
message: 'Zu viele Anfragen. Bitte versuchen Sie es später erneut.'
});
app.use('/api/', apiLimiter);
Eingabevalidierung
const validator = require('validator');
app.post('/user', (req, res) => {
const { email, username, age } = req.body;
if (!validator.isEmail(email)) {
return res.status(400).json({ error: 'Ungültige E-Mail' });
}
if (!validator.isAlphanumeric(username)) {
return res.status(400).json({ error: 'Benutzername muss alphanumerisch sein' });
}
const cleanEmail = validator.normalizeEmail(email);
const cleanUsername = validator.escape(username);
saveUser({ email: cleanEmail, username: cleanUsername, age });
});
Sicherheits-Checkliste
Entwicklungsphase
- Alle Eingaben validieren und bereinigen
- SQL Injection verhindern (Prepared Statements)
- XSS verhindern (HTML-Escaping)
- CSRF-Tokens verwenden
- Passwörter hashen (bcrypt, argon2)
- HTTPS verwenden
- Sicherheits-Header setzen (Helmet.js)
- Rate Limiting anwenden
- Datei-Uploads validieren
- Geheimnisse mit Umgebungsvariablen verwalten
Bereitstellungsphase
- Abhängigkeitsschwachstellen prüfen (
npm audit) - Neueste Sicherheitspatches anwenden
- Firewall konfigurieren
- Protokolle überwachen
- Regelmäßige Backups
- Penetrationstests
Web-Sicherheit ist keine einmalige Aufgabe, sondern ein fortlaufender Prozess.
Kernprinzipien:
- Allen Eingaben misstrauen
- Prinzip der geringsten Privilegien befolgen
- Verteidigung in der Tiefe-Strategie verwenden
- Regelmäßige Sicherheitsaudits durchführen
Ein kleiner Fehler kann zu großem Schaden führen. Sicherheit ist nicht optional—sie ist wesentlich.
