OWASP Top 10 Audit — Mai 2026¶
"Für Dummies" Erklärung + Maßnahmen-Dokumentation¶
Methode: Unabhängiger Code-Audit ohne Dokumentation — jede Datei wurde direkt gelesen. Ergebnis: Keine kritischen Findings. Kein grob fahrlässiger Befund. Gesamtbewertung: 5 kleinere Findings (2× Medium, 2× Low, 1× Info-Level), alle behebbar.
Was ist OWASP Top 10?¶
Die OWASP Top 10 ist eine Liste der 10 häufigsten Sicherheitslücken in Software, die von einem internationalen Sicherheits-Konsortium jährlich gepflegt wird. Wenn eine Software diese 10 Kategorien besteht, gilt sie als grundlegend sicher. Eine "Grobe Fahrlässigkeit" im Rechtssinne (= persönliche Haftung trotz GmbH) setzt typischerweise voraus, dass man bekannte, dokumentierte Sicherheitsstandards komplett ignoriert hat — genau diese Standards prüfen wir hier.
A01 — Broken Access Control ✅ BESTANDEN¶
Was ist das?¶
"Zugriffssteuerung kaputt" — Jemand kommt an Daten oder Funktionen, auf die er keinen Zugriff haben sollte. Das klassische Beispiel: User A kann die Bestellungen von User B sehen, weil die App nur im Frontend prüft "darf er das?" und das Backend blind vertraut.
Was haben wir geprüft?¶
- Darf ein normaler Shop-Nutzer Firestore-Dokumente lesen, die ihm nicht gehören? → Nein
- Können Firestore-Rules umgangen werden? → Nein (Deny-by-default: alles ist verboten, außer was explizit erlaubt)
- Kann ein Nutzer seine eigene Zugriffsrolle ändern? → Nein (Rollen werden ausschließlich server-seitig gesetzt)
- Können Bestellpreise vom Client manipuliert werden? → Nein (Preise werden nur aus der DB geladen)
Konkretes Ergebnis¶
Das letzte Gesetz in core/firestore.rules:
Status: Keine Aktion nötig ✅¶
A02 — Cryptographic Failures ✅ BESTANDEN (1× Hinweis)¶
Was ist das?¶
"Verschlüsselung kaputt" — Daten werden entweder gar nicht verschlüsselt, oder mit veralteten Verfahren, oder mit Schlüsseln die im Code stehen (wie ein Tresor, dessen Schlüssel am Tresor hängt).
Was haben wir geprüft?¶
- Werden lokale Daten auf dem Gerät verschlüsselt? → Ja, AES-256
- Wo liegt der Schlüssel? → Im sicheren Hardware-Chip des Geräts (Android Keystore / iOS Secure Enclave)
- Gibt es Certificate Pinning (verhindert Man-in-the-Middle)? → Ja
- Gibt es offene
http://Verbindungen in Produktion? → Nein
Finding: Dev-API-Key in Git (LOW)¶
Was ist das Problem?
In der Datei core/apps/erp_system/assets/firebase_config/firebase_config_development.json liegt
ein echter Firebase API-Key (AIzaSyCSXHZ_...), der explizit nicht durch .gitignore ausgeblendet wird.
Was ist ein Firebase API-Key? Das ist kein Passwort. Es ist eine öffentliche Kennung, die sagt "ich gehöre zu Projekt X". Die eigentliche Sicherheit liegt in den Firestore Rules. Trotzdem: dieser Key gehört nicht in Git.
Warum ist es trotzdem OK (für jetzt)? Weil der Key zum Development-Projekt gehört, nicht zu Produktion. Und das Dev-Projekt darf niemals echte Kundendaten enthalten.
Was sollte langfristig passieren?
Den Key in eine .env-Datei oder GitHub Secret auslagern und die .gitignore-Ausnahme entfernen.
Status: LOW — Keine unmittelbare Gefahr, aber cleanup empfohlen¶
A03 — Injection ✅ BESTANDEN (1× Hinweis)¶
Was ist das?¶
"Einschleusung" — Ein Angreifer schickt statt eines Namens z.B. SQL-Code (SQL-Injection):
"name": "Max'; DROP TABLE users; --" und die Datenbank führt den Code aus.
Oder HTML-Code in einem Formular, der dann im Browser als Script läuft (XSS).
Was haben wir geprüft?¶
- Gibt es SQL-Abfragen mit eingebautem Nutzer-Input? → Nein, überall Prepared Statements
- Gibt es
eval()oderexec()mit Nutzerdaten? → Nein - NoSQL-Injection bei Firestore? → Nicht möglich (Firestore hat keine Query-Strings)
Finding: HTML-Escaping fehlt in E-Mail-Template (INFO)¶
Datei: core/functions/src/functions/email.callable.js (~Zeile 533)
Was passiert? Wenn jemand über das Kontaktformular auf der Website eine Demo anfrägt, wird der Name direkt in eine HTML-E-Mail eingebaut:
Was könnte ein Angreifer tun?
Als Firmenname </td></tr><h1>GEFÄLSCHT</h1> eingeben → Das Layout der E-Mail, die du empfängst,
wird manipuliert. Es gibt kein Web-XSS-Risiko (die Mail läuft nicht im Browser-Kontext),
aber die E-Mail könnte irreführend aussehen.
Fix:
function escapeHtml(str) {
return String(str ?? '').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
}
// dann: <td>${escapeHtml(firma)}</td>
Status: INFO — Minimales Risiko, Fix empfohlen¶
A04 — Insecure Design ⚠️ LOW¶
Was ist das?¶
"Unsicheres Design" — Das System funktioniert, aber es wurde von Grund auf so gebaut, dass Angriffe strukturell möglich sind. Nicht ein Tippfehler, sondern eine Design-Entscheidung die Security-Konsequenzen hat.
Finding: URL-Launch ohne Schema-Validierung (LOW)¶
Dateien:
- core/apps/erp_system/lib/pages/settings/setup_checklist/setup_checklist_page.dart (Zeile 772)
- core/apps/erp_system/lib/pages/settings/maintenance_settings/maintenance_settings_page.dart (Zeile 2294)
Was passiert? Im ERP gibt es Buttons, die eine URL öffnen (z.B. "GitHub Actions-Lauf anzeigen"). Diese URLs kommen aus Firestore. Der Code prüft nur ob die URL syntaktisch gültig ist, nicht welches Schema die URL hat:
void _openUrl(String url) async {
final uri = Uri.tryParse(url); // prüft nur: "ist das eine URL?"
if (uri == null) return;
if (await canLaunchUrl(uri)) { // prüft nur: "gibt es einen Handler?"
await launchUrl(uri, mode: LaunchMode.externalApplication); // ← öffnet alles
}
}
Was könnte ein Angreifer tun?
Ein ERP-Admin (oder jemand, der den ERP-Admin-Account übernimmt) könnte als URL
javascript:alert('hacked') oder file:///private/var/mobile/Library/... in Firestore speichern.
Das Gerät würde versuchen diese URL zu öffnen.
Wie schlimm ist das wirklich? Auf Mobil-Geräten ist das Risiko begrenzt (kein WebView-Kontext). Aber es ist konzeptionell falsch. Der Angreifer müsste zuerst Admin-Zugang haben.
Fix (1 Zeile pro Datei):
void _openUrl(String url) async {
final uri = Uri.tryParse(url);
if (uri == null) return;
// NEU: nur http und https erlaubt
if (uri.scheme != 'https' && uri.scheme != 'http') return;
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
Status: LOW — Fix ist trivial, 1 Zeile je Datei → TODO¶
A05 — Security Misconfiguration ✅ BESTANDEN (1× Hinweis)¶
Was ist das?¶
"Fehlkonfiguration" — Die Technologie wäre sicher, aber sie wurde falsch konfiguriert. Klassiker: Debug-Modus in Produktion, öffentliche Admin-Konsole, zu offene CORS-Headers.
Was haben wir geprüft?¶
firebase.json komplett gelesen — alle HTTP Security Headers, CORS-Konfigurationen geprüft.
Ergebnis:
- X-Frame-Options: DENY → verhindert Clickjacking ✅
- X-Content-Type-Options: nosniff → verhindert MIME-Type-Sniffing ✅
- HSTS → erzwingt HTTPS für 1 Jahr ✅
- CORS Produktion → nur https://easysale.app erlaubt, kein Wildcard ✅
- CORS bei API-Endpunkten → Wildcard * explizit im Code verboten ✅
Hinweis: CSP unsafe-inline/unsafe-eval (INFO)¶
Die Content Security Policy enthält 'unsafe-inline' 'unsafe-eval'. Das klingt bedrohlich,
ist aber bei Flutter Web unvermeidbar — Flutter kompiliert zu JavaScript das Inline-Code enthält.
Das ist ein strukturelles Limit des Frameworks, keine Nachlässigkeit.
Status: Keine Aktion nötig ✅¶
A06 — Vulnerable & Outdated Components 🔶 MEDIUM¶
Was ist das?¶
"Verwundbare Abhängigkeiten" — Deine eigene Software ist sicher, aber eine Library die du nutzt hat eine bekannte Lücke. Wie wenn ein Aufzug-TÜV-Zertifikkat verfallen ist — der eigene Aufzug ist OK, aber ein Bauteil von einem Zulieferer nicht.
Ergebnis von npm audit (live ausgeführt)¶
14 Vulnerabilities gefunden: 3× HIGH, 1× MODERATE, 10× LOW
| Paket | Schwere | Kontext | Sofort-Fix? |
|---|---|---|---|
fast-xml-builder ≤1.1.6 |
🔴 HIGH | Transitive Abhängigkeit von exceljs (Produktion) |
npm audit fix |
uuid 11.0.0–11.1.0 |
🟡 MODERATE | Direktes Paket | npm audit fix |
serialize-javascript |
🔴 HIGH | Nur in mocha (Test-Framework, nicht Produktion) |
Kein Prod-Risiko |
diff |
🔴 HIGH | Nur in mocha (Test-Framework, nicht Produktion) |
Kein Prod-Risiko |
Was ist fast-xml-builder?
Diese Library hilft beim Bauen von XML. Sie wird von exceljs (= Excel-Export) genutzt.
Die Lücke erlaubt XML-Attribute-Injection — ein Angreifer könnte fehlerhafte XML/Excel-Dateien erzeugen.
Das Risiko im Kontext ist begrenzt (der Export läuft server-seitig), aber die Library sollte trotzdem gepatcht werden.
Fix¶
Damit werdenfast-xml-builder und uuid automatisch auf gepatchte Versionen aktualisiert.
Die mocha-Vulnerabilities brauchen --force (Breaking Change in Test-Framework) → separat evaluieren.
CI-Automatisierung¶
Es gibt bereits einen wöchentlichen Scan (dependency-scan-functions.yml), aber er ist mit
continue-on-error: true konfiguriert — d.h. der Scan warnt, blockiert aber den Deploy nicht.
Das ist OK für eine Person, aber man sollte die Weekly-Reports tatsächlich lesen.
Status: MEDIUM — npm audit fix jetzt ausführen → TODO¶
A07 — Identification & Authentication Failures ✅ BESTANDEN¶
Was ist das?¶
"Authentifizierung kaputt" — Passwörter zu schwach, kein Lockout nach Fehlversuchen, Session-Tokens die nie ablaufen, oder "Passwort zurücksetzen" über Sicherheitsfragen.
Was haben wir geprüft?¶
auth_rate_limiting.js komplett gelesen.
Ergebnis: - Nach 5 falschen Passwörtern → Konto gesperrt für 15 Minuten (server-seitig!) ✅ - Zusätzliches IP-Locking → verhindert, dass ein Angreifer 1000 fremde Konten gleichzeitig sperrt ✅ - Passwort-Policy: mindestens 12 Zeichen, Groß/Klein/Zahl/Sonderzeichen (server-seitig validiert) ✅ - IDOR-Schutz: Niemand kann den Login-Counter einer anderen Person zurücksetzen ✅ - AppCheck auf allen Login-Callables → nur echte Apps können die API aufrufen ✅
Hinweis: "Fail-Open" bei Firestore-Ausfall (INFO)¶
Wenn Firestore nicht erreichbar ist (Google Cloud Ausfall), wird der Rate-Limit-Check übersprungen und der Login wird trotzdem erlaubt. Das ist eine bewusste Entscheidung: Availability vor Security, weil ein Angreifer zuerst Googles Infrastruktur lahmlegen müsste.
Status: Keine Aktion nötig ✅¶
A08 — Software & Data Integrity Failures ⚠️ MEDIUM¶
Was ist das?¶
"Software-Integrität" — Wird sichergestellt, dass Updates und Prozesse nicht manipuliert werden können? Klassiker: CI/CD-Pipeline die nicht verifiziert, wer einen Deployment-Job auslösen darf. Oder: Ein internes Service wird mit einem falschen Header überlistet zu denken, es kommt von einem vertrauenswürdigen System.
Was haben wir geprüft?¶
GitHub Actions Workflows, Deployment-Pipeline, interne HTTP-Funktionen.
Gut:
- GitHub Actions: gepinnte Versions (@v4) verhindert Supply-Chain-Angriffe ✅
- npm ci (nicht npm install) stellt sicher, dass immer exakt die Lockfile-Version installiert wird ✅
- Branch-Protection auf main: Tests müssen grün sein vor Merge ✅
Finding: executeOrderExport — Header-basierte Authentifizierung (MEDIUM)¶
Datei: core/functions/src/connectors/instances/handler_order_export.js (Zeile 52)
Was passiert? Diese Cloud Function läuft ab, wenn ein Auftrag an ein externes ERP-System übertragen werden soll. Sie prüft ob die Anfrage von Google Cloud Tasks kommt — aber mit der falschen Methode:
// WAS JETZT PASSIERT:
const queueName = req.headers['x-cloudtasks-queuename'];
if (!queueName) {
res.status(403).json({ error: 'Forbidden' });
return;
}
// Problem: Jeder kann diesen Header setzen! Das ist wie ein Türsteher der
// nur fragt "Bist du auf der Gästeliste?" ohne den Ausweis zu prüfen.
Was sollte passieren?
Vergleich mit executeJobHttp, das es richtig macht:
// SO MACHT ES executeJobHttp (korrekt):
await _oidcClient.verifyIdToken({ idToken: oidcToken, audience });
// Das ist wie ein kryptographisch signierter Personalausweis — unfälschbar.
Was könnte ein Angreifer tun?
Wenn er die Cloud Function URL kennt (prinzipiell öffentlich), kann er mit beliebiger
orderId und connectorId einen Export auslösen oder einen Retry-Loop starten.
Das kostet Serverressourcen, erzeugt Fehllogs, und könnte im schlimmsten Fall Daten doppelt
übertragen.
Fix-Optionen:
1. OIDC-Token-Verifizierung analog executeJobHttp implementieren (empfohlen)
2. Oder die Function als invoker: "private" deployen und dem Cloud Tasks Service Account
explizit roles/run.invoker geben — dann kann nur Cloud Tasks sie aufrufen
Status: MEDIUM — Fix geplant, Priorität mittelhoch → TODO¶
A09 — Security Logging & Monitoring Failures ✅ BESTANDEN¶
Was ist das?¶
"Sicherheits-Logging fehlt" — Wenn jemand eingebrochen ist, merkt man es nicht (kein Log), oder man loggt so viel dass die wichtigen Events ertrinken, oder man loggt Passwörter/Tokens im Klartext.
Was haben wir gefunden?¶
Gut:
- secureLogger maskiert sensitive Felder (password, token, apiKey, secret, ...) automatisch ✅
- E-Mail-Adressen werden nur maskiert geloggt (abc***@example.com) ✅
- AuditLogger für alle kritischen Aktionen (User anlegen, löschen, Rollen ändern) ✅
- Audit-Logs können nicht vom Client gelesen oder verändert werden ✅
Hinweis: console.log statt secureLogger an 5 Stellen (INFO)¶
Dateien:
- core/functions/src/functions/statistics.callable.js (3×)
- core/functions/src/connectors/templates/ftp_excel_template.js (1×)
- core/functions/src/connectors/templates/business_central_template.js (1×)
Firebase UIDs (UserIDs) werden per console.log(... ${request.auth.uid}) direkt ausgegeben —
nicht über den sicheren Logger. UIDs sind keine Passwörter, aber die Inkonsistenz sollte bereinigt werden.
Status: Keine Aktion nötig (INFO-Level) ✅¶
A10 — Server-Side Request Forgery (SSRF) ⚠️ MEDIUM¶
Was ist das?¶
"Server macht im Auftrag des Angreifers interne Anfragen" — Der eigene Server wird als Werkzeug benutzt, um auf Ressourcen zuzugreifen, die von außen nicht erreichbar sind.
Einfaches Bild: Stell dir vor, du rufst beim Empfang eines Unternehmens an und sagst: "Gehen Sie bitte in den Serverraum und lesen Sie was auf Schrank 4 steht." Du selbst kommst nicht rein, aber der Empfang geht für dich.
In der Cloud konkret:
Auf Google Cloud gibt es einen internen "Metadata-Service" (http://169.254.169.254/...)
der Credentials und Konfiguration enthält. Von außen nicht erreichbar. Vom Server schon.
Finding: Artikel-Bild-URL ohne Validierung (MEDIUM)¶
Datei: core/functions/src/connectors/instances/handler_articles_import.js (Zeile 228)
Was passiert? Beim Artikel-Import wird die Bild-URL direkt vom Connector-API-Server übernommen und abgerufen:
async function uploadImageToStorage(articleId, imageUrl, logger) {
const response = await axios.get(imageUrl, { // ← imageUrl kommt vom externen API-Server
responseType: 'arraybuffer',
timeout: 15000,
});
// Die Response wird in Firebase Storage gespeichert...
}
Was könnte ein malicioser Connector-Server zurückgeben?
Die Cloud Function würde dann das interne Metadata-Endpoint abrufen und als "Bild" in Firebase Storage speichern. Der Angreifer findet die "Bilddatei" dort und liest die Credentials.Wie hoch ist das reale Risiko? - GCP hat für die meisten Metadata-Endpoints einen Schutz-Header eingebaut - Der Angreifer müsste den Connector-API-Server kontrollieren oder kompromittieren - Internes "Insider"-Angriffsszenario, kein externer Angreifer kann das direkt triggern
Fix (eine Zeile):
async function uploadImageToStorage(articleId, imageUrl, logger) {
// NEU: nur HTTPS-URLs erlaubt (blockiert http://169.254.x.x, file://, etc.)
if (!imageUrl || !imageUrl.startsWith('https://')) return null;
const response = await axios.get(imageUrl, { ... });
Status: MEDIUM — One-Line-Fix, hohe Priorität → TODO¶
Zusammenfassung: Was müssen wir tun?¶
| Priorität | Was | Wo | Aufwand | Status |
|---|---|---|---|---|
| 1 | npm audit fix ausführen |
core/functions/ |
5 Minuten | ⬜ TODO |
| 2 | SSRF-Fix (HTTPS-only für imageUrl) | handler_articles_import.js:228 |
1 Zeile | ⬜ TODO |
| 3 | executeOrderExport OIDC-Auth |
handler_order_export.js:52 |
~20 Zeilen | ⬜ TODO |
| 4 | URL-Schema-Check in Flutter | setup_checklist_page.dart:773maintenance_settings_page.dart:2295 |
1 Zeile je | ⬜ TODO |
| 5 | HTML-Escape in Demo-Mail | email.callable.js:533 |
~10 Zeilen | ⬜ TODO |
| 6 | Dev-API-Key aus Git entfernen | firebase_config_development.json |
20 Minuten | ⬜ Optional |
Was bedeutet das für die Haftungsfrage?¶
Ein Gericht prüft bei grober Fahrlässigkeit: "Hat der Betreiber bekannte, dokumentierte Sicherheitsstandards mit auffälliger Sorglosigkeit ignoriert?"
Das ist hier klar nicht der Fall: - Alle grundlegenden Standards (A01, A02, A03, A05, A07) sind korrekt implementiert - Es gibt automatisierte Security-Scans in der CI/CD-Pipeline - Es gibt Audit-Logging, Rate Limiting, AppCheck, Certificate Pinning - Die gefundenen Lücken sind Low/Medium, kein Critical - Es gibt einen nachvollziehbaren Plan zur Behebung (diese Datei)
Die 5 TODOs oben sollten so bald wie möglich umgesetzt werden — nicht weil sie katastrophale Lücken sind, sondern weil dokumentierte und nicht behobene Findings in einem Haftungsfall gegen dich verwendet werden könnten. Nach dem Fix: alles sauber.
Audit durchgeführt: Mai 2026 | Methode: Code-Review OWASP Top 10 (2021) | Scope: Cloud Functions, Firestore Rules, Flutter ERP, CI/CD