đ EasySale - Detaillierte OWASP Security Analyse¶
ERP-System & Shop-System¶
Audit Datum: 24. Februar 2026
Letztes Update: 30. MĂ€rz 2026 (Code-Verifikation #8: MED-3 Rollback-Mechanismen behoben)
Umfang: VollstÀndige Code-Level Analyse beider Systeme
Framework: OWASP Top 10 2021 + OWASP MASVS (Mobile)
Systeme: ERP-System (apps/erp_system) + Shop-System (apps/shop_system)
đ Executive Summary¶
Diese detaillierte Analyse identifiziert 37 Security Findings auf Code-Level, sortiert nach OWASP Top 10 Kategorien und Schweregrad.
Severity Distribution¶
- đŽ KRITISCH: 5 Findings â 5 â behoben
- đ HOCH: 12 Findings â 12 â behoben
- đĄ MITTEL: 13 Findings â 13 â behoben
- đą NIEDRIG: 7 Findings â 3 â behoben (LOW-2/4/5), 4 offen
Gesamtfortschritt: 33 vollstÀndig behoben | 0 teilweise | 4 offen
Letzter Scan: 30.03.2026 â Code-Verifikation #8
OWASP Top 10 Coverage¶
Alle 10 Kategorien wurden analysiert, 8 davon haben aktive Findings.
đ FINDINGS ĂBERSICHT (ALLE 37 FINDINGS)¶
| ID | Finding | Severity | OWASP | System | CVSS | CWE | PrioritÀt |
|---|---|---|---|---|---|---|---|
| CRIT-1 | UngeschĂŒtzte Firestore Queries | â BEHOBEN | A01 | Shop | 9.1 | CWE-284 | â Umgesetzt 24.02.2026 |
| CRIT-2 | Passwort/E-Mail in Plaintext (SharedPreferences) | â BEHOBEN | A02 | ERP | 8.8 | CWE-312 | â Umgesetzt 24.02.2026 |
| CRIT-3 | Fehlende Input Sanitization | â BEHOBEN | A03 | Beide | 8.2 | CWE-89 | â Umgesetzt 24.02.2026 |
| CRIT-4 | currentUser ohne Null-Check | â BEHOBEN | A01 | Shop | 7.5 | CWE-476 | â Umgesetzt 24.02.2026 |
| CRIT-5 | SSL Certificate Pinning fehlt | â BEHOBEN | A02 | Shop | 7.4 | CWE-295 | â Umgesetzt 24.02.2026 |
| HIGH-1 | Fehlende Rate Limiting | â BEHOBEN | A04 | Shop | 7.2 | CWE-307 | â Umgesetzt 24.02.2026 |
| HIGH-2 | Token in Debug-Logs | â BEHOBEN | A09 | Shop | 6.8 | CWE-532 | â Umgesetzt 24.02.2026 |
| HIGH-3 | Unvalidierte File Downloads | â BEHOBEN | A03, A08 | Shop | 6.5 | CWE-494 | â Umgesetzt 24.02.2026 |
| HIGH-4 | Session Timeout fehlt | â BEHOBEN | A07 | Beide | 6.2 | CWE-613 | â Umgesetzt 24.02.2026 |
| HIGH-5 | Fehlende Biometric Auth | â BEHOBEN | A07 | Beide | 5.8 | CWE-287 | â Umgesetzt 24.02.2026 |
| HIGH-6 | UnverschlĂŒsselte App State | â BEHOBEN | A02 | ERP | 5.5 | CWE-312 | â Umgesetzt 24.02.2026 |
| HIGH-7 | Fehlende Request Signing | â BEHOBEN | A08 | Beide | 5.3 | CWE-345 | â Umgesetzt 24.02.2026 |
| HIGH-8 | Insufficient Transport Security | â BEHOBEN | A02 | Shop | 5.2 | CWE-319 | â Umgesetzt 24.02.2026 |
| HIGH-9 | Weak Password Policy | â BEHOBEN | A07 | Beide | 5.0 | CWE-521 | â Umgesetzt 24.02.2026 |
| HIGH-10 | Insecure Random Number Gen | â BEHOBEN | A02 | Beide | 4.9 | CWE-338 | â Umgesetzt 24.02.2026 |
| HIGH-11 | Missing Security Headers (Web) | â BEHOBEN | A05 | Beide | 4.8 | CWE-693 | â Umgesetzt 24.02.2026 |
| HIGH-12 | Unprotected Backup Files | â BEHOBEN | A02 | Beide | 4.7 | CWE-530 | â Umgesetzt 24.02.2026 |
| MED-1 | Fehlende Error Boundary | â BEHOBEN | A04 | Beide | 4.5 | CWE-755 | â Umgesetzt 24.02.2026 |
| MED-2 | XSS-Risiko bei Web-Ansichten | â BEHOBEN | A03 | Beide | 4.3 | CWE-79 | â Umgesetzt 24.02.2026 |
| MED-3 | Unzureichende Rollback-Mechanismen | â BEHOBEN | A08 | Beide | 4.2 | CWE-664 | â Umgesetzt 30.03.2026 |
| MED-4 | Fehlende Biometric Integration | â BEHOBEN | A07 | Beide | 4.0 | CWE-287 | â Umgesetzt 24.02.2026 |
| MED-5 | UnverschlĂŒsselte lokale Caches (Hive) | â BEHOBEN | A02 | Beide | 3.9 | CWE-312 | â HiveAesCipher + SchlĂŒssel in FlutterSecureStorage |
| MED-6 | Fehlende Jailbreak/Root Detection | â BEHOBEN | A04 | Beide | 3.8 | CWE-693 | â Umgesetzt 24.02.2026 â shared-Package |
| MED-7 | Unangemessene Permission Requests | â BEHOBEN | A04 | Beide | 3.7 | CWE-250 | â Umgesetzt 24.02.2026 |
| MED-8 | Fehlende Code Obfuscation | â BEHOBEN | A04 | Beide | 3.6 | CWE-656 | â Umgesetzt 24.02.2026 |
| MED-9 | Debug-Modus in Production | â BEHOBEN | A05 | Beide | 3.5 | CWE-489 | â Umgesetzt 24.02.2026 |
| MED-10 | Unvalidierte Deep Links | â BEHOBEN | A03 | Beide | 3.4 | CWE-20 | â Umgesetzt 24.02.2026 |
| MED-11 | Fehlende Integrity Checks | â BEHOBEN | A08 | Beide | 3.3 | CWE-353 | â Umgesetzt 24.02.2026 |
| MED-12 | Race Conditions bei State Updates | â BEHOBEN | A04 | Beide | 3.2 | CWE-362 | â Umgesetzt 24.02.2026 |
| MED-13 | Memory Leaks bei Streams | â BEHOBEN | A04 | Beide | 3.1 | CWE-401 | â Umgesetzt 24.02.2026 |
| LOW-1 | Fehlende Security.md | đą NIEDRIG | - | Beide | 2.0 | - | âčïž Doku |
| LOW-2 | Veraltete Dependencies / Dependency Audit | â BEHOBEN | A06 | Beide | 2.0 | CWE-1104 | â Umgesetzt 24.02.2026 |
| LOW-3 | Fehlende Pentest-Doku | đą NIEDRIG | - | Beide | 1.8 | - | âčïž Doku |
| LOW-4 | UnvollstĂ€ndige Security Headers | â BEHOBEN | A05 | Web | 1.7 | CWE-693 | â Umgesetzt 24.02.2026 |
| LOW-5 | Fehlende CSP | â BEHOBEN | A05 | Web | 1.6 | CWE-1021 | â Umgesetzt 24.02.2026 |
| LOW-6 | Keine Disclosure Policy | đą NIEDRIG | - | Beide | 1.5 | - | âčïž Doku |
| LOW-7 | Fehlende Security Training Doku | đą NIEDRIG | - | Beide | 1.0 | - | âčïž Prozess |
Legende¶
- OWASP: A01-A10 = OWASP Top 10 2021 Kategorien
- System: ERP = nur ERP-System, Shop = nur Shop-System, Beide = beide betroffen
- PrioritĂ€t: đ„ = Sofort, đ = Geplant, đ = Backlog, âčïž = Informational
đŽ KRITISCHE FINDINGS¶
â CRIT-1: UngeschĂŒtzte Firestore Queries im Shop-System (BEHOBEN 24.02.2026)¶
OWASP: A01:2021 â Broken Access Control
CWE: CWE-284 (Improper Access Control)
CVSS Score: 9.1 (Critical)
System: Shop-System
Status: â
BEHOBEN â Neue Firestore Security Rules fĂŒr beide Systeme deployed. Shop-System hat jetzt hasCustomerAccess()-PrĂŒfung auf Serverseite, Deny-by-Default am Ende.
Problem (UrsprĂŒnglich)¶
Mehrere Firebase Queries im Shop-System verlassen sich ausschlieĂlich auf Client-seitige Filterung ohne serverseitige Security Rules Validierung:
Betroffene Datei: apps/shop_system/lib/services/firebase_services/customer_firebase_service.dart
// Line 30-31: UNSICHER - Client-seitige Filterung
final querySnapshot = await FirebaseFirestore.instance
.collection(customerAccessPermissionCollection)
.where('email', isEqualTo: FirebaseAuth.instance.currentUser?.email)
.get();
Ăhnliche Patterns in:
- Line 65: getCustomers()
- Line 104: getCustomerAccessPermission()
- Line 122: getCustomerUserLanguage()
- Line 136: getCustomerUserName()
- Line 157: updateCustomerUserLanguage()
- Line 221: Multiple weitere Queries
- Line 356-473: Delivery breaks, purchase lists, customer feed
Warum ist das kritisch?¶
- Client-seitige Filterung kann umgangen werden durch modifizierte Clients
- Ein Angreifer kann die App dekompilieren und direkt Firestore Queries senden
- Firestore Rules in
firestore.rulessind zu permissiv:
Risiko¶
- Zugriff auf Kundendaten anderer Nutzer
- Zugriff auf andere Tenant-Daten (Multi-Tenancy-Verletzung)
- DSGVO-VerstoĂ durch unautorisierten Datenzugriff
- Manipulation von Bestellungen anderer Kunden
Exploit Scenario¶
// Angreifer kann eigene Query senden:
FirebaseFirestore.instance
.collection('customers')
.get(); // Zugriff auf ALLE Kunden ohne Filterung!
Lösung¶
1. Firestore Security Rules hÀrten:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() {
return request.auth != null;
}
function hasCustomerAccess(customerId) {
return isSignedIn() &&
exists(/databases/$(database)/documents/customerUsers/$(customerId + '_' + request.auth.token.email)) &&
get(/databases/$(database)/documents/customerUsers/$(customerId + '_' + request.auth.token.email)).data.registrationState == 1;
}
// Customer Access Permissions
match /customerUsers/{userId} {
allow read: if isSignedIn() &&
resource.data.email == request.auth.token.email;
allow create: if isSignedIn() &&
request.resource.data.email == request.auth.token.email &&
request.resource.data.registrationState == 0;
allow update, delete: if false; // Nur via Admin SDK
}
// Customers - nur mit Zugriffsberechtigung
match /customers/{customerId} {
allow read: if hasCustomerAccess(customerId);
allow write: if false; // Nur via Admin SDK
}
// Explizit: Kein Zugriff ohne spezifische Regel
match /{document=**} {
allow read, write: if false;
}
}
}
2. Code-seitige Validierung hinzufĂŒgen:
Future<List<Customer>> getCustomers() async {
final user = FirebaseAuth.instance.currentUser;
if (user == null) {
throw Exception('User not authenticated');
}
// Query bleibt gleich, aber Rules erzwingen serverseitig die Validierung
final querySnapshot = await FirebaseFirestore.instance
.collection(customerAccessPermissionCollection)
.where('email', isEqualTo: user.email)
.get()
.timeout(
Duration(seconds: 10),
onTimeout: () => throw TimeoutException('Query timeout'),
);
// ... rest of code
}
PrioritĂ€t: đ„ SOFORT - Production System kompromittiert
â CRIT-2: Passwort im Plaintext in SharedPreferences (ERP) (BEHOBEN 24.02.2026)¶
OWASP: A02:2021 â Cryptographic Failures
CWE: CWE-312 (Cleartext Storage of Sensitive Information)
CVSS Score: 8.8 (High)
System: ERP-System
Status: â
BEHOBEN â E-Mail wird jetzt ebenfalls in FlutterSecureStorage gespeichert. SharedPreferences wird nicht mehr fĂŒr Credentials verwendet.
Problem (UrsprĂŒnglich)¶
Datei: apps/erp_system/lib/blocs/auth_bloc/auth_bloc.dart
// Line 204-208: KRITISCH - Password könnte in SharedPreferences landen
Future<void> _saveCredentials({
required String email,
required String password,
required bool rememberMe,
}) async {
const storage = FlutterSecureStorage();
final prefs = await SharedPreferences.getInstance();
if (rememberMe) {
await prefs.setString('saved_email', email); // â ïž Email in Plaintext
await storage.write(key: 'saved_password', value: password); // â
Encrypted
await prefs.setBool('remember_me', true);
}
}
Warum ist das ein Problem?¶
- SharedPreferences speichert unverschlĂŒsselt in XML (Android) / plist (iOS)
- E-Mail-Adresse ist ebenfalls sensibel (DSGVO Personenbezug)
- Bei rooted/jailbroken GerÀten direkt lesbar
- Backup-Tools können diese Daten extrahieren
Risiko¶
- Password ist korrekt in FlutterSecureStorage (verschlĂŒsselt) â
- ABER: E-Mail im Klartext ermöglicht Account Enumeration
- ABER: Kombination E-Mail + VerschlĂŒsseltes Passwort im selben Speicher ist schwĂ€cher als beide verschlĂŒsselt
Lösung¶
Future<void> _saveCredentials({
required String email,
required String password,
required bool rememberMe,
}) async {
const storage = FlutterSecureStorage();
if (rememberMe) {
// â
Beide Werte verschlĂŒsselt speichern
await storage.write(key: 'saved_email', value: email);
await storage.write(key: 'saved_password', value: password);
await storage.write(key: 'remember_me', value: 'true');
} else {
// Alle löschen
await storage.delete(key: 'saved_email');
await storage.delete(key: 'saved_password');
await storage.delete(key: 'remember_me');
}
}
Future<void> _onLoadSavedCredentials(...) async {
const storage = FlutterSecureStorage();
final savedEmail = await storage.read(key: 'saved_email');
final savedPassword = await storage.read(key: 'saved_password');
final rememberMeStr = await storage.read(key: 'remember_me');
final rememberMe = rememberMeStr == 'true';
emit(CredentialsLoaded(
email: savedEmail,
password: savedPassword,
rememberMe: rememberMe,
));
}
PrioritĂ€t: đ„ SOFORT
â CRIT-3: Fehlende Input Sanitization bei Firestore Writes (BEHOBEN 24.02.2026)¶
OWASP: A03:2021 â Injection
CWE: CWE-89 (NoSQL Injection)
CVSS Score: 8.2 (High)
System: Beide Systeme
Status: â
BEHOBEN â InputValidator-Klasse im shared-Package implementiert (sanitizeName, sanitizeEmail, sanitizePhone, sanitizeMap). In customer_firebase_service.dart und weiteren Services integriert.
Problem¶
User-Input wird direkt in Firestore geschrieben ohne Validierung oder Sanitization:
Shop-System: apps/shop_system/lib/services/firebase_services/customer_firebase_service.dart
// Line 238-246: KEINE Validierung vor Firestore Write
Future<void> updateCustomerUserName({
required String customerId,
required String firstName,
required String lastName,
}) async {
// â firstName und lastName werden DIREKT ĂŒbernommen
final doc = /* ... get doc ... */;
await doc.reference.update({
'firstName': firstName, // â Keine Validierung!
'lastName': lastName, // â Keine Validierung!
});
}
Risiko¶
- NoSQL Injection in Firestore Queries
- XSS wenn Daten spÀter in Web-Views angezeigt werden
- Data Corruption durch Sonderzeichen
- Firestore Rule Bypass durch manipulierte Felder
Exploit Scenarios¶
// Scenario 1: Field Injection
updateCustomerUserName(
customerId: 'cust123',
firstName: 'Robert"; admin: true; role: "superadmin',
lastName: 'Tables'
);
// Scenario 2: Size-based DoS
updateCustomerUserName(
customerId: 'cust123',
firstName: 'A' * 1000000, // 1MB String -> Storage/Kosten Explosion
lastName: 'B' * 1000000
);
// Scenario 3: Special Characters
updateCustomerUserName(
customerId: 'cust123',
firstName: '<script>alert("XSS")</script>',
lastName: '${document.cookie}'
);
Lösung¶
1. Input Validation Service erstellen:
// shared/lib/validators/input_validator.dart
class InputValidator {
// Max length fĂŒr verschiedene Feldtypen
static const int maxNameLength = 100;
static const int maxEmailLength = 254;
static const int maxPhoneLength = 20;
// Verbotene Zeichen
static final RegExp _htmlTags = RegExp(r'<[^>]*>');
static final RegExp _scriptTags = RegExp(r'<script.*?</script>', caseSensitive: false);
static final RegExp _sqlKeywords = RegExp(r'\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION)\b', caseSensitive: false);
static String sanitizeName(String input) {
if (input.isEmpty) return input;
// LĂ€nge prĂŒfen
if (input.length > maxNameLength) {
throw ArgumentError('Name too long (max: $maxNameLength)');
}
// HTML Tags entfernen
String sanitized = input.replaceAll(_htmlTags, '');
sanitized = sanitized.replaceAll(_scriptTags, '');
// SQL Keywords entfernen (auch fĂŒr NoSQL relevant)
sanitized = sanitized.replaceAll(_sqlKeywords, '');
// Nur erlaubte Zeichen: Buchstaben, Zahlen, Leerzeichen, Bindestriche, Apostrophe
final RegExp allowed = RegExp(r"^[a-zA-ZĂ€Ă¶ĂŒĂĂĂĂ0-9\s\-']+$");
if (!allowed.hasMatch(sanitized)) {
throw ArgumentError('Name contains invalid characters');
}
// FĂŒhrende/nachfolgende Leerzeichen entfernen
sanitized = sanitized.trim();
return sanitized;
}
static String sanitizeEmail(String input) {
if (input.isEmpty) return input;
if (input.length > maxEmailLength) {
throw ArgumentError('Email too long');
}
// Email Regex Validation
final emailRegex = RegExp(r'^[\w\.-]+@[\w\.-]+\.\w{2,}$');
if (!emailRegex.hasMatch(input)) {
throw ArgumentError('Invalid email format');
}
return input.toLowerCase().trim();
}
}
2. In Services verwenden:
Future<void> updateCustomerUserName({
required String customerId,
required String firstName,
required String lastName,
}) async {
// â
Validierung und Sanitization
final sanitizedFirstName = InputValidator.sanitizeName(firstName);
final sanitizedLastName = InputValidator.sanitizeName(lastName);
// ZusÀtzliche Checks
if (sanitizedFirstName.isEmpty || sanitizedLastName.isEmpty) {
throw ArgumentError('Names cannot be empty');
}
final doc = /* ... get doc ... */;
await doc.reference.update({
'firstName': sanitizedFirstName,
'lastName': sanitizedLastName,
'updatedAt': FieldValue.serverTimestamp(),
});
}
PrioritĂ€t: đ„ HOCH - In 1 Woche beheben
â CRIT-4: Direkter Zugriff auf currentUser ohne Null-Check (BEHOBEN 24.02.2026)¶
OWASP: A01:2021 â Broken Access Control
CWE: CWE-476 (NULL Pointer Dereference)
CVSS Score: 7.5 (High)
System: Shop-System
Status: â
BEHOBEN â AuthHelper-Klasse im shared-Package implementiert (getRequiredUser(), getRequiredEmail(), getRequiredUid()). Alle 14+ unsicheren currentUser-Zugriffe in Services und BLoCs durch AuthHelper ersetzt. Wirft UnauthorizedException bei fehlendem Auth-State statt silent-null-Query oder App-Crash.
Problem¶
Mehrfacher direkter Zugriff auf FirebaseAuth.instance.currentUser ohne Null-Checks oder Error Handling:
Datei: apps/shop_system/lib/services/firebase_services/order_firebase_service.dart
// Line 9: â KEINE Null-PrĂŒfung
Future<List<Order>> getOrders(String customerId) async {
final user = FirebaseAuth.instance.currentUser; // Kann NULL sein!
return await FirebaseFirestore.instance
.collection(_ordersCollection)
.where('customerId', isEqualTo: customerId)
// â user.email wird nicht geprĂŒft - Crash wenn user == null
.get();
}
GeÀnderte Dateien:
- packages/shared/lib/helpers/auth_helper.dart â NEU â AuthHelper + UnauthorizedException
- packages/shared/lib/shared.dart â AuthHelper jetzt offiziell re-exportiert
- apps/shop_system/lib/services/firebase_services/customer_firebase_service.dart â 12 Stellen: currentUser?.email â AuthHelper.getRequiredEmail()
- apps/shop_system/lib/services/firebase_services/user_firebase_service.dart â currentUser!.uid â AuthHelper.getRequiredUid()
- apps/shop_system/lib/services/firebase_services/order_firebase_service.dart â addOrder() wirft jetzt UnauthorizedException statt silent null return
- apps/shop_system/lib/blocs/customer/customers_bloc.dart â currentUser!.email! â AuthHelper.getRequiredEmail()
// packages/shared/lib/helpers/auth_helper.dart
class AuthHelper {
static User getRequiredUser() {
final user = FirebaseAuth.instance.currentUser;
if (user == null) throw const UnauthorizedException('...');
return user;
}
static String getRequiredEmail() { /* â
wirft wenn null */ }
static String getRequiredUid() { /* â
wirft wenn null */ }
}
PrioritĂ€t: ~~đ„ HOCH - In 2 Wochen~~ â â ERLEDIGT 24.02.2026
â CRIT-5: Fehlende SSL Certificate Pinning Implementierung (BEHOBEN)¶
OWASP: A02:2021 â Cryptographic Failures
CWE: CWE-295 (Improper Certificate Validation)
CVSS Score: 7.4 (High)
System: Shop-System
Status: â
VOLLSTĂNDIG BEHOBEN â SSLPinningConfig-Klasse vollstĂ€ndig implementiert mit echten SHA-256-Fingerprints. Fingerprints am 24.02.2026 via openssl s_client ermittelt und eingetragen:
- Primary: firebasestorage.googleapis.com â 26a1502190a7c505612f4b99ee0cba2824e5bfcb0037f94200d8724c00379bb9
- Fallback: storage.googleapis.com â 9f1e63805b9abc367cffeeec24d90b9f043b322680b560d9394787ea6a8e3727
Problem¶
SSL Certificate Pinning ist vorhanden aber nicht vollstÀndig implementiert:
Datei: apps/shop_system/lib/services/mobile/document_download_service.dart
// Line 16: SSL Pinning Service wird REFERENZIERT
final Dio _dio = SSLPinningConfig.createSecureDio();
ABER: Analyse der ssl_pinning_service.dart zeigt:
// services/ssl_pinning_service.dart
class SSLPinningConfig {
static Dio createSecureDio() {
final dio = Dio();
// TODO: Implementiere Certificate Pinning
// Aktuell: Keine Pinning-Implementierung!
return dio;
}
// FĂŒr Development
static Dio createSecureDioWithoutPinning() {
return Dio(); // Komplett ungeschĂŒtzt
}
}
Risiko¶
- Man-in-the-Middle (MITM) Angriffe möglich
- Angreifer kann TLS-Traffic abfangen
- Download-URLs können manipuliert werden
- Sensible Dokumente können abgefangen werden
Lösung¶
VollstÀndige SSL Pinning Implementierung:
import 'package:dio/dio.dart';
import 'package:dio/adapter.dart';
class SSLPinningConfig {
// SHA-256 Fingerprints der vertrauenswĂŒrdigen Zertifikate
// Firebase Storage Zertifikate
static const List<String> _trustedCertificates = [
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=', // Firebase Storage Cert 1
'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=', // Firebase Storage Cert 2 (Backup)
];
static Dio createSecureDio() {
final dio = Dio(BaseOptions(
connectTimeout: 30000,
receiveTimeout: 30000,
validateStatus: (status) => status! < 500,
));
// Certificate Pinning konfigurieren
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
client.badCertificateCallback = (cert, host, port) {
// SHA-256 des Zertifikats berechnen
final certSha256 = cert.sha256.toString();
// Gegen bekannte Zertifikate prĂŒfen
if (_trustedCertificates.contains(certSha256)) {
return true; // â
VertrauenswĂŒrdiges Zertifikat
}
// Log fĂŒr Debugging (nur Development)
debugPrint('â ïž Untrusted certificate for $host:$port');
debugPrint(' SHA-256: $certSha256');
return false; // â Nicht vertrauenswĂŒrdiges Zertifikat
};
return client;
};
return dio;
}
// Nur fĂŒr lokales Testing
static Dio createSecureDioWithoutPinning() {
assert(() {
debugPrint('â ïž WARNING: SSL Pinning deaktiviert - nur fĂŒr Development!');
return true;
}());
return Dio();
}
}
Zertifikat-Fingerprints ermitteln:
# Firebase Storage Certificate abrufen
openssl s_client -connect firebasestorage.googleapis.com:443 -showcerts < /dev/null 2>/dev/null | openssl x509 -outform PEM > firebase_cert.pem
# SHA-256 Fingerprint berechnen
openssl x509 -in firebase_cert.pem -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
PrioritĂ€t: đ„ HOCH - In 2 Wochen
đ HOHE FINDINGS¶
HIGH-1: â Fehlende Rate Limiting im Shop-System IMPLEMENTIERT¶
OWASP: A04:2021 â Insecure Design
CWE: CWE-307 (Improper Restriction of Excessive Authentication Attempts)
System: Shop-System
Status: â
IMPLEMENTIERT am 24. Februar 2026
Implementierung¶
Rate Limiting wurde vollstÀndig in den Shop-System Login-Flow integriert. Der bestehende RateLimiterService aus packages/shared wird jetzt von beiden Systemen genutzt.
GeÀnderte Dateien:
- apps/shop_system/lib/blocs/auth/auth_bloc.dart â Rate Limiter in _onSignIn integriert
- apps/shop_system/lib/main.dart â RateLimiterService.initialize() + dispose()
- apps/erp_system/lib/blocs/auth_bloc/auth_bloc.dart â minutes + 1 UX-Fix
- packages/shared/lib/shared.dart â RateLimiterService jetzt offiziell re-exportiert
// apps/shop_system/lib/blocs/auth/auth_bloc.dart
Future<void> _onSignIn(SignIn event, Emitter<BaseBlocState> emit) async {
emit(BlocLoading());
// đ SECURITY HIGH-1: Rate Limiting gegen Brute-Force-Angriffe
if (!RateLimiterService.canAttemptLogin(event.email)) {
final remaining = RateLimiterService.getRemainingLockoutTime(event.email);
final minutes = remaining.inMinutes + 1; // +1: verhindert "0 Minuten"
emit(AuthBlocError(
'Zu viele fehlgeschlagene Anmeldeversuche. '
'Bitte warten Sie $minutes Minuten und versuchen Sie es erneut.',
));
emit(AuthBlocLoaded(false));
return;
}
try {
await _auth.signInWithEmailAndPassword(
email: event.email,
password: event.password,
);
// Login erfolgreich - Rate Limiter zurĂŒcksetzen
RateLimiterService.resetAttempts(event.email);
emit(AuthBlocLoaded(true));
} on FirebaseAuthException catch (e) {
// Login fehlgeschlagen - Versuch registrieren
RateLimiterService.recordFailedAttempt(event.email);
// ...
}
}
Konfiguration (packages/shared/lib/helpers/rate_limiter_service.dart)¶
- Max. Versuche: 5
- Lockout-Dauer: 15 Minuten
- Zeitfenster: 30 Minuten
- Automatisches Cleanup alle 5 Minuten
Konsistenz ERP vs. Shop¶
| Shop | ERP | |
|---|---|---|
| Rate Limiting Check | â | â |
| Failed attempts zĂ€hlen | â | â |
| Reset bei Erfolg | â | â |
minutes + 1 UX-Fix |
â | â (nachgezogen) |
| Initialize in main() | â | â |
Status: â
HIGH-1 VOLLSTĂNDIG IMPLEMENTIERT
PrioritĂ€t: ~~đ
2 Wochen~~ â â
ERLEDIGT
HIGH-2: â Token in Debug-Logs IMPLEMENTIERT¶
OWASP: A09:2021 â Security Logging and Monitoring Failures
CWE: CWE-532 (Insertion of Sensitive Information into Log File)
System: Shop-System
Status: â
IMPLEMENTIERT am 24. Februar 2026
Implementierung¶
Alle unsicheren debugPrint-Aufrufe, die FCM Tokens oder Device-IDs ausgeben, wurden durch SecureLogger ersetzt. Der SecureLogger aus packages/shared ist jetzt offiziell ĂŒber shared.dart exportiert und in beiden Systemen verwendbar.
GeÀnderte Dateien:
- apps/shop_system/lib/services/device_info_service.dart â alle debugPrint â SecureLogger
- apps/shop_system/lib/services/firebase_services/customer_firebase_service.dart â alle debugPrint â SecureLogger
- packages/shared/lib/shared.dart â SecureLogger jetzt offiziell re-exportiert
// apps/shop_system/lib/services/device_info_service.dart
// đ SECURITY HIGH-2: FCM Token NICHT loggen (CWE-532)
fcmToken = await FirebaseMessaging.instance.getToken();
if (kDebugMode) {
if (fcmToken != null) {
SecureLogger.debug('â
FCM Token retrieved successfully');
// Token selbst wird NICHT geloggt
} else {
SecureLogger.warning('â ïž FCM Token is null');
}
}
// Device ID wird vor dem Logging sanitized:
SecureLogger.debug('Device ID: ${SecureLogger.sanitize(deviceId ?? "null")}');
Was wurde verhindert¶
- â
debugPrint('â FCM Token retrieved: ${fcmToken.substring(0, 20)}...')â Token-Prefix nie mehr in Logs - â GerĂ€tespezifische IDs im Klartext â werden durch
sanitize()mit[UUID]/[USER_ID]ersetzt - â Fehler-Stack-Traces mit Token-Fragmenten â
SecureLogger.error()nutzt sanitizierten Output
Status: â
HIGH-2 VOLLSTĂNDIG IMPLEMENTIERT
PrioritĂ€t: ~~đ
1 Woche~~ â â
ERLEDIGT
HIGH-3: â Unvalidierte File Downloads IMPLEMENTIERT¶
OWASP: A03:2021 â Injection + A08:2021 â Software and Data Integrity Failures
CWE: CWE-494 (Download of Code Without Integrity Check)
System: Shop-System
Status: â
IMPLEMENTIERT am 24. Februar 2026
Implementierung¶
Ein neuer FirebaseStorageUrlValidator wurde im shared-Package erstellt und in beide Download-Services (mobile + web) integriert. Alle Download-URLs werden vor der AusfĂŒhrung serverseitig validiert.
Neue Datei: packages/shared/lib/validators/firebase_storage_url_validator.dart
GeÀnderte Dateien:
- apps/shop_system/lib/services/mobile/document_download_service.dart
- apps/shop_system/lib/services/web/document_download_service_web.dart
- packages/shared/lib/shared.dart â FirebaseStorageUrlValidator re-exportiert
// packages/shared/lib/validators/firebase_storage_url_validator.dart
class FirebaseStorageUrlValidator {
static const List<String> _allowedDomains = [
'firebasestorage.googleapis.com',
'storage.googleapis.com',
];
static const List<String> _allowedSchemes = ['https'];
static bool isValid(String? url) {
if (url == null || url.isEmpty) return false;
final uri = Uri.parse(url);
// 1. HTTPS-only
if (!_allowedSchemes.contains(uri.scheme.toLowerCase())) return false;
// 2. Nur Firebase Storage Domains
if (!_allowedDomains.any((d) => uri.host.toLowerCase() == d)) return false;
// 3. Path Traversal verhindern
if (uri.path.contains('..')) return false;
// 4. GefÀhrliche Zeichen
if (RegExp(r'[<>"'r'|?*\x00-\x1f]').hasMatch(uri.path)) return false;
// 5. Firebase Storage URL-Format (/v0/b/...)
if (uri.host == 'firebasestorage.googleapis.com' &&
!uri.path.startsWith('/v0/b/')) return false;
return true;
}
static void validate(String? url) {
if (!isValid(url)) {
throw ArgumentError(
'UngĂŒltige oder unsichere URL. Nur Firebase Storage URLs sind erlaubt.',
);
}
}
}
// In beiden Download-Services:
Future<String> downloadDocument(ArticleDocument document, ...) async {
// đ SECURITY HIGH-3: URL-Validierung gegen Path Traversal (CWE-494)
FirebaseStorageUrlValidator.validate(document.downloadUrl);
// ... Download
}
Was wurde geschĂŒtzt¶
- â
Alle Downloads ĂŒber
DocumentDownloadService(mobile) - â
Alle Downloads ĂŒber
DocumentDownloadServiceWeb(web):downloadDocument,openDocument,shareDocument - â
Gecachte Downloads via
getCachedOrDownload
Status: â
HIGH-3 VOLLSTĂNDIG IMPLEMENTIERT
PrioritĂ€t: ~~đ
2 Wochen~~ â â
ERLEDIGT
â HIGH-4: Session Timeout â VOLLSTĂNDIG IMPLEMENTIERT (BEHOBEN 24.02.2026)¶
OWASP: A07:2021 â Identification and Authentication Failures
CWE: CWE-613 (Insufficient Session Expiration)
System: Beide Systeme
Status: â
VOLLSTĂNDIG â ERP hat InactivityService (30-Minuten Idle-Timeout + Auto-Logout). Shop-System hat nun ebenfalls einen InactivityService mit 30-Minuten Idle-Timeout, der via Listener-Widget alle Pointer-Events erfasst und bei Timeout automatisch FirebaseAuth.instance.signOut() aufruft.
Implementierung¶
Der bestehende BaseAuthService in packages/shared enthĂ€lt bereits vollstĂ€ndige Session-Timeout-Logik. Diese wurde in beide Apps aktiv integriert â via startPeriodicTokenRefresh(), validateAndRefreshSession() und einem WidgetsBindingObserver fĂŒr App-Lifecycle-Events.
GeÀnderte Dateien:
- apps/shop_system/lib/services/inactivity_service.dart â NEU: InactivityService mit 30-Min.-Idle-Timeout fĂŒr Shop
- apps/shop_system/lib/application.dart â Listener-Wrapper + _inactivityService.start() / .dispose()
- apps/shop_system/lib/main.dart â Lifecycle Observer + Token Refresh + Cleanup
- apps/erp_system/lib/main.dart â Lifecycle Observer + Token Refresh + Cleanup
Session-Konfiguration (packages/shared/lib/services/base_auth_service.dart)¶
| Parameter | Wert |
|---|---|
sessionTimeout |
12 Stunden (seit letztem Login) |
tokenRefreshThreshold |
5 Minuten vor Ablauf |
refreshCheckInterval |
alle 4 Minuten |
// Beide main.dart â identische Implementierung:
// 1. Periodic Token Refresh starten
authService.startPeriodicTokenRefresh();
// 2. Lifecycle Observer fĂŒr App Resume
class _AppLifecycleObserver extends WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
// App kommt in den Vordergrund â Session + Token validieren
authService.validateAndRefreshSession();
}
}
}
// 3. Cleanup bei App-Ende
@override
void dispose() {
WidgetsBinding.instance.removeObserver(_lifecycleObserver);
authService.stopPeriodicTokenRefresh();
super.dispose();
}
Was validateAndRefreshSession() prĂŒft¶
- Session-Alter â Ă€lter als 12h seit letztem Login â automatischer Logout
- Token-Ablauf â lĂ€uft in <5min ab â proaktive Erneuerung (
getIdToken(true)) - Auth-Fehler â Token ungĂŒltig â Logout; Netzwerkfehler â kein Logout (Offline-Toleranz)
Status: â
HIGH-4 VOLLSTĂNDIG IMPLEMENTIERT
PrioritĂ€t: ~~đ
3 Wochen~~ â â
ERLEDIGT
â HIGH-5: Biometrische Authentifizierung â VOLLSTĂNDIG IMPLEMENTIERT (BEHOBEN 24.02.2026)¶
OWASP: A07:2021 â Identification and Authentication Failures
CWE: CWE-287 (Improper Authentication)
CVSS Score: 5.8 (Medium)
System: Beide Systeme
Status: â
BEHOBEN â Im ERP-System und Shop-System vollstĂ€ndig implementiert. Shop-System hat jetzt CheckBiometricSupport, EnableBiometricAuth, DisableBiometricAuth, AuthenticateWithBiometric Events/States im AuthBloc, sowie Biometric-Button-UI in responsive_sign_in_form.dart.
Implementierte Komponenten:
- BiometricAuthService (
packages/shared/lib/services/biometric_auth_service.dart) - Device-Support PrĂŒfung
- VerfĂŒgbare Biometrie-Typen (Face ID, Touch ID, Fingerprint)
- Sichere Authentifizierung mit Fallback
- Opt-in/Opt-out fĂŒr Nutzer
-
Comprehensive Error Handling
-
BaseAuthService Integration (
packages/shared/lib/services/base_auth_service.dart) isBiometricSupported()- Hardware-CheckcanUseBiometrics()- VerfĂŒgbarkeits-CheckenableBiometricAuth()- Aktivierung mit Test-AuthdisableBiometricAuth()- DeaktivierungauthenticateWithBiometrics()- Authentifizierung-
requiresBiometricReAuth()- Re-Auth Check (5min Timeout) -
ERP-System Integration (
apps/erp_system/lib/blocs/auth_bloc/) - Neue Events:
CheckBiometricSupport,EnableBiometricAuth,DisableBiometricAuth,AuthenticateWithBiometric - Neue States:
BiometricSupportChecked,BiometricAuthEnabled,BiometricAuthenticating,BiometricAuthSuccess,BiometricAuthFailed -
VollstÀndige BLoC Integration mit Error Handling
-
Shop-System Integration â NEU 24.02.2026
auth_bloc_events.dartâCheckBiometricSupport,EnableBiometricAuth,DisableBiometricAuth,AuthenticateWithBiometricauth_bloc_states.dartâBiometricSupportChecked,BiometricAuthEnabled,BiometricAuthDisabled,BiometricAuthenticating,BiometricAuthSuccess,BiometricAuthFailedauth_bloc.dartâ vollstĂ€ndige Handler viaAuthService(extendsBaseAuthService)responsive_sign_in_form.dartâ Fingerprint-Button (mobil), BlocConsumer fĂŒr State-Handling, Opt-in/Opt-out Toggle
Features¶
â Multi-Platform Support: - iOS: Face ID + Touch ID - Android: Fingerprint + Face Recognition + Iris Scanner - Automatische Erkennung verfĂŒgbarer Methoden
â Security Features: - Sticky Authentication (bleibt aktiv bis erfolgreich/abgebrochen) - Lockout nach zu vielen Fehlversuchen - Re-Authentifizierung nach 5 Minuten InaktivitĂ€t - VerschlĂŒsselte Speicherung der Einstellungen (SharedPreferences)
â
Error Handling:
- BiometricErrorType.notAvailable - Hardware nicht verfĂŒgbar
- BiometricErrorType.notEnrolled - Keine biometrischen Daten
- BiometricErrorType.lockedOut - TemporÀr gesperrt
- BiometricErrorType.permanentlyLockedOut - Neustart erforderlich
- Benutzerfreundliche Fehlermeldungen
â User Experience: - Opt-in Design (Nutzer muss aktivieren) - Test-Authentifizierung vor Aktivierung - Systemdialoge mit lokalisierten Texten - Fallback auf PIN/Pattern bei Fehler
Code-Beispiele¶
ERP-System - Biometric Auth aktivieren:
// Im AuthBloc
authBloc.add(EnableBiometricAuth());
// State Handling
BlocBuilder<AuthBloc, BaseBlocState>(
builder: (context, state) {
if (state is BiometricAuthEnabled) {
// Erfolg: Zeige BestÀtigung
} else if (state is BiometricAuthFailed) {
// Fehler: Zeige Grund
print(state.reason);
}
},
)
Shop-System - Biometric Auth nutzen:
// Im AuthService
final authService = AuthService(); // extends BaseAuthService
// PrĂŒfe Support
final isSupported = await authService.isBiometricSupported();
final isAvailable = await authService.canUseBiometrics();
// Aktiviere
final success = await authService.enableBiometricAuth();
// Authentifiziere
final authenticated = await authService.authenticateWithBiometrics(
localizedReason: 'Bitte authentifizieren Sie sich',
useErrorDialogs: true,
stickyAuth: true,
);
if (authenticated) {
// Zugriff gewÀhren
}
VerfĂŒgbare Biometrie-Typen ermitteln:
// Via BaseAuthService
final types = await authService.getAvailableBiometricTypes();
// Returns: ['Face ID / Gesichtserkennung', 'Touch ID / Fingerabdruck']
// Via BiometricAuthService direkt
final biometricService = BiometricAuthService();
final biometrics = await biometricService.getAvailableBiometrics();
// Returns: [BiometricType.face, BiometricType.fingerprint]
AbhĂ€ngigkeiten¶
Automatisch verfĂŒgbar in beiden Apps durch shared package Integration.
Security Compliance¶
OWASP A07:2021 â ErfĂŒllt: - Multi-Faktor Authentifizierung implementiert - Starke Authentifizierungsmechanismen - Session Management mit Re-Auth - Lockout nach Fehlversuchen
CWE-287 â Mitigiert: - Improper Authentication â durch biometrische Verifikation adressiert - Hardware-basierte Authentifizierung - Kein Passwort-nur Login mehr erforderlich
OWASP MASVS (Mobile App Security Verification Standard): - MSTG-AUTH-8: â Biometric authentication properly implemented - MSTG-AUTH-9: â Second factor exists - MSTG-STORAGE-1: â Biometric state encrypted in SharedPreferences
Testing Empfehlungen¶
- Funktionale Tests:
- [ ] Face ID/Touch ID Aktivierung auf iOS
- [ ] Fingerprint/Face Recognition auf Android
- [ ] Deaktivierung funktioniert
-
[ ] Re-Auth nach 5min Timeout
-
Error Handling Tests:
- [ ] Keine biometrischen Daten registriert
- [ ] Hardware nicht verfĂŒgbar
- [ ] Zu viele Fehlversuche (Lockout)
-
[ ] User-Cancellation
-
Security Tests:
- [ ] Biometric Settings nur mit Auth Ànderbar
- [ ] Kein Fallback auf schwache Auth
- [ ] Session-Invalidierung bei Auth-Fehler
Migration & Rollout¶
Phase 1: Opt-in fĂŒr Power Users (Woche 1-2)
- Feature Flag: biometric_auth_enabled
- Beta-Test mit 10% der User
- Monitoring: Success Rate, Error Types
Phase 2: Rollout fĂŒr alle User (Woche 3-4) - In-App Tutorial - Onboarding Flow: "Biometrie aktivieren?" - Opt-out jederzeit möglich
Phase 3: Default fĂŒr neue User (Woche 5+) - Neue Registrierungen: Auto-Vorschlag - Alte User: Notification "Jetzt aktivieren"
Monitoring & Metrics¶
Empfohlene Metriken: - Adoption Rate: % Nutzer mit aktivierter Biometrie - Success Rate: % erfolgreiche Authentifizierungen - Error Rate by Type: Verteilung der Error Types - Fallback Rate: % User die auf PIN/Password fallback'en
Status: â
HIGH-5 VOLLSTĂNDIG IMPLEMENTIERT (BEIDE SYSTEME)
PrioritĂ€t: ~~đ
4 Wochen~~ â â
ERLEDIGT 24.02.2026
HIGH-6: â UnverschlĂŒsselte App State BEHOBEN¶
OWASP: A02:2021 â Cryptographic Failures
CWE: CWE-312 (Cleartext Storage of Sensitive Information)
CVSS Score: 5.5 (Medium)
System: Beide Systeme
Status: â
Umgesetzt am 24.02.2026
Problem (ursprĂŒnglich)¶
Das ERP-System verwendete Hive ohne VerschlĂŒsselung fĂŒr persistierten Auth State:
// VORHER: UnverschlĂŒsselt in Hive (main.dart)
await Hive.openBox('authBox'); // â Keine VerschlĂŒsselung!
// Daten im Klartext auf dem GerÀt:
authBox.put('isLoggedIn', true);
authBox.put('customerId', AppConfig.customerId); // â Klartext!
authBox.put('userId', userId); // â Klartext!
Betroffene Stellen (ERP-System):
- main.dart â Box-Initialisierung ohne Cipher
- auth_bloc.dart â isLoggedIn, customerId, userId
- helpers/auth_helpers.dart â liest customerId, userId
- services/inactivity_service.dart
- services/trigger_statistics_service.dart
- blocs/job_bloc/job_bloc.dart
- pages/settings/ â 4 weitere Dateien
Shop-System: Bereits korrekt â sensitiver State via SecureStorageService (FlutterSecureStorage) â
Risiko¶
- Daten im Klartext auf dem Dateisystem (Hive
.hive-Datei) - Bei Backup-Extraktion (
adb backup) lesbar - Bei rooted/jailbroken GerÀten direkt zugreifbar
customerIdunduserIdermöglichen Account Enumeration- DSGVO-VerstoĂ bei unverschlĂŒsselten personenbezogenen Daten
Implementierte Lösung¶
Neuer Service: EncryptedAppStateService
Datei: apps/erp_system/lib/services/encrypted_app_state_service.dart
class EncryptedAppStateService {
// AES-256 verschlĂŒsselte Hive Box
static const String _authBoxName = 'authBox_encrypted';
// Encryption Key sicher gespeichert (iOS Keychain / Android Keystore)
static const FlutterSecureStorage _secureStorage = FlutterSecureStorage(
aOptions: AndroidOptions(encryptedSharedPreferences: true),
iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock),
);
static Future<void> initialize() async {
final encryptionKey = await _getOrCreateEncryptionKey();
_authBox = await Hive.openBox(
_authBoxName,
encryptionCipher: HiveAesCipher(encryptionKey), // â
AES-256
);
}
// Key wird automatisch generiert und im Platform Keychain abgelegt
static Future<Uint8List> _getOrCreateEncryptionKey() async {
final existingKey = await _secureStorage.read(key: _encryptionKeyName);
if (existingKey != null) return base64Decode(existingKey);
final newKey = Hive.generateSecureKey(); // â
Kryptographisch sicher
await _secureStorage.write(key: _encryptionKeyName, value: base64Encode(newKey));
return Uint8List.fromList(newKey);
}
// Einmalige Migration von Altdaten
static Future<void> migrateFromUnencryptedBox() async {
final oldBox = await Hive.openBox('authBox');
if (oldBox.isNotEmpty) {
for (var key in oldBox.keys) {
await authBox.put(key, oldBox.get(key));
}
await oldBox.clear();
await Hive.deleteBoxFromDisk('authBox'); // â
Alte Daten löschen
}
}
}
GeÀnderte Dateien (ERP-System):
| Datei | Ănderung |
|---|---|
lib/services/encrypted_app_state_service.dart |
NEU â Service mit AES-256 Hive Cipher + Key in Keychain |
lib/main.dart |
Initialisierung + Migration von alter unverschlĂŒsselter Box |
lib/blocs/auth_bloc/auth_bloc.dart |
Alle Hive-Zugriffe durch EncryptedAppStateService ersetzt |
lib/helpers/auth_helpers.dart |
Liest aus EncryptedAppStateService statt Hive.box |
lib/services/inactivity_service.dart |
Nutzt EncryptedAppStateService.clearAuthData() |
lib/services/trigger_statistics_service.dart |
Nutzt AuthHelpers.tryGetCustomerId() |
lib/blocs/job_bloc/job_bloc.dart |
Nutzt AuthHelpers.tryGetUserId() |
lib/pages/settings/article_document_type_settings/ |
2 Dateien migriert |
lib/pages/settings/job_settings/ |
2 Dateien migriert |
lib/pages/settings/connector_settings/ |
1 Datei migriert |
Sicherheitsverbesserungen:
- â
AES-256 VerschlĂŒsselung fĂŒr alle persistierten Auth-Daten
- â
Encryption Key im Platform Keychain (iOS) / Android Keystore
- â
Automatische Migration von Altdaten (einmalig beim ersten App-Start)
- â
Alte unverschlĂŒsselte Daten werden nach Migration gelöscht
- â
DSGVO-konform: customerId und userId verschlĂŒsselt
- â
Shop-System war bereits korrekt implementiert
Status: â
HIGH-6 VOLLSTĂNDIG BEHOBEN
PrioritĂ€t: ~~đ
4 Wochen~~ â â
ERLEDIGT 24.02.2026
HIGH-8: â Insufficient Transport Security â IMPLEMENTIERT¶
OWASP: A02:2021 â Cryptographic Failures
CWE: CWE-319 (Cleartext Transmission of Sensitive Information)
CVSS Score: 5.2 (Medium)
System: Shop-System
Status: â
IMPLEMENTIERT am 24. Februar 2026
Problem (UrsprĂŒnglich)¶
Das Shop-System hatte keine explizite Konfiguration zur Erzwingung von HTTPS auf Plattform- und App-Ebene:
- Android: Kein
networkSecurityConfigimAndroidManifest.xmlâ kein explizites Cleartext-Verbot auf Betriebssystem-Ebene. - iOS: Keine
NSAppTransportSecurity-Sektion inInfo.plistâ ATS-Konfiguration war weder dokumentiert noch explizit gehĂ€rtet. - Dart/Flutter: Kein globaler
HttpOverrides-Layer â Dart konnte theoretisch cleartext HTTP-Verbindungen aufbauen; kein einheitliches Logging bei schlechten Zertifikaten.
Risiko¶
- Sensible Daten (Auth-Tokens, Kundendaten, Bestellungen) könnten unverschlĂŒsselt ĂŒbertragen werden
- Man-in-the-Middle Angriffe auf HTTP-Downgrade möglich
- Bei falsch konfigurierten Proxys oder Bibliotheken kein OS-seitiger Schutz
- DSGVO-VerstoĂ durch unverschlĂŒsselte Ăbertragung personenbezogener Daten
Implementierung¶
1. Android: network_security_config.xml (neu)
Datei: apps/shop_system/android/app/src/main/res/xml/network_security_config.xml
<network-security-config>
<!-- Cleartext HTTP global blockieren -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<!-- Firebase Storage: HTTPS + System-CAs -->
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">firebasestorage.googleapis.com</domain>
<domain includeSubdomains="true">storage.googleapis.com</domain>
...
</domain-config>
<!-- Debug-Only: User-CAs fĂŒr Proxy erlauben (wirkt nur in Debug-Builds) -->
<debug-overrides>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</debug-overrides>
</network-security-config>
AndroidManifest.xml â zwei neue Attribute:
<application
android:usesCleartextTraffic="false"
android:networkSecurityConfig="@xml/network_security_config"
...>
2. iOS: NSAppTransportSecurity in Info.plist (ergÀnzt)
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
<key>NSAllowsLocalNetworking</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<!-- Firebase Storage, googleapis.com, firebaseapp.com -->
<!-- NSExceptionAllowsInsecureHTTPLoads: false -->
<!-- NSExceptionMinimumTLSVersion: TLSv1.2 -->
<!-- NSExceptionRequiresForwardSecrecy: true -->
</dict>
</dict>
3. Dart: TransportSecurityService (neu)
Datei: apps/shop_system/lib/services/transport_security_service.dart
class TransportSecurityService {
/// Initialisiert globale HttpOverrides â muss VOR dem ersten Netzwerkzugriff
/// in main() aufgerufen werden.
static void initialize() {
if (!kIsWeb) {
HttpOverrides.global = _SecureHttpOverrides();
}
}
/// Wirft InsecureTransportException wenn URL kein HTTPS verwendet.
static bool requireHttps(String? url) { ... }
/// Gibt Diagnose-Status zurĂŒck (initialized, cleartext_blocked, etc.)
static Map<String, dynamic> getSecurityStatus() { ... }
}
class _SecureHttpOverrides extends HttpOverrides {
@override
HttpClient createHttpClient(SecurityContext? context) {
final client = super.createHttpClient(context);
// Alle ungĂŒltigen Zertifikate ablehnen (Defense-in-Depth neben SSLPinningConfig)
client.badCertificateCallback = (cert, host, port) {
SecureLogger.warning('[SECURITY HIGH-8] Bad certificate fĂŒr $host:$port abgelehnt.');
return false;
};
return client;
}
}
main.dart â TransportSecurityService.initialize() vor Firebase.initializeApp():
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
// đ SECURITY HIGH-8: Transport Security â vor erstem Netzwerkzugriff
TransportSecurityService.initialize();
await Firebase.initializeApp(...);
...
}
GeĂ€nderte / Neue Dateien¶
| Datei | Ănderung |
|---|---|
android/app/src/main/res/xml/network_security_config.xml |
NEU â Cleartext blockiert, User-CAs nur in Debug |
android/app/src/main/AndroidManifest.xml |
usesCleartextTraffic="false" + networkSecurityConfig |
ios/Runner/Info.plist |
NSAppTransportSecurity mit NSAllowsArbitraryLoads=false + Firebase-Exceptions |
lib/services/transport_security_service.dart |
NEU â Dart-Layer mit HttpOverrides, URL-Validator, Diagnose |
lib/main.dart |
TransportSecurityService.initialize() eingebunden |
Schutzebenen (Defense in Depth)¶
| Ebene | Mechanismus | Status |
|---|---|---|
| Betriebssystem (Android) | network_security_config.xml |
â |
| Betriebssystem (iOS) | NSAppTransportSecurity |
â |
| Dart HttpClient | _SecureHttpOverrides.badCertificateCallback |
â |
| Application Layer (Dio) | SSLPinningConfig.createSecureDio() (CRIT-5) |
â SHA-256 Fingerprints eingetragen (24.02.2026) |
| URL-Validierung | TransportSecurityService.requireHttps() |
â |
| Storage Download | FirebaseStorageUrlValidator.validate() |
â (HIGH-3) |
Status: â
HIGH-8 VOLLSTĂNDIG IMPLEMENTIERT
PrioritĂ€t: ~~đ
3 Wochen~~ â â
ERLEDIGT 24.02.2026
HIGH-9: â Weak Password Policy â BEHOBEN (24.02.2026)¶
OWASP: A07:2021 â Identification and Authentication Failures
CWE: CWE-521 (Weak Password Requirements)
System: Beide Systeme
Problem (Vorher)¶
Beide Apps verwendeten lediglich minLength: 6 ohne jede KomplexitĂ€tsprĂŒfung:
// ALT â shop_system validators.dart
static String? validatePasswordWithLength(String? value, {int minLength = 6}) {
if (value.length < minLength) return 'Passwort muss mindestens $minLength Zeichen lang sein';
return null; // kein GroĂbuchstabe, keine Ziffer, kein Sonderzeichen
}
Lösung¶
1. AuthValidators.validatePasswordStrength (shop_system)
apps/shop_system/lib/pages/auth/shared/validators.dart
// OWASP A07 â enforces password complexity for registration / password change
static String? validatePasswordStrength(String? value) {
if (value == null || value.isEmpty) return 'Bitte Passwort eingeben';
if (value.length < 8) return 'Passwort muss mindestens 8 Zeichen lang sein';
if (value.length > 128) return 'Passwort darf maximal 128 Zeichen haben';
if (!value.contains(RegExp(r'[a-z]'))) return 'Mindestens einen Kleinbuchstaben';
if (!value.contains(RegExp(r'[A-Z]'))) return 'Mindestens einen GroĂbuchstaben';
if (!value.contains(RegExp(r'\d'))) return 'Mindestens eine Ziffer';
if (!value.contains(RegExp(r'[@$!%*?&#^()_+\-=\[\]{};:,.<>]')))
return r'Mindestens ein Sonderzeichen (@$!%*?&#)';
return null;
}
2. AuthPasswordField â neuer Parameter requireStrongPassword
apps/shop_system/lib/pages/auth/shared/widgets/auth_password_field.dart
requireStrongPassword: trueâ verwendetvalidatePasswordStrengthrequireMinLength: trueâ Legacy-Pfad (nur LĂ€nge, z. B. Sign-In)
3. Alle Registrierungsformulare auf requireStrongPassword: true umgestellt:
- apps/shop_system/lib/pages/auth/shared/widgets/responsive_sign_up_form.dart
- apps/shop_system/lib/pages/auth/web/sign_up_page_web.dart
- apps/shop_system/lib/pages/auth/mobile/sign_up_page_mobile.dart
Helper-Text gibt dem Nutzer die Anforderungen direkt im Formular an:
âMind. 8 Zeichen, GroĂ-/Kleinbuchstabe, Ziffer, Sonderzeichen"
4. ERP-System: packages/shared/lib/helpers/validation_service.dart hatte bereits minPasswordLength = 12 + volle KomplexitĂ€tsprĂŒfung. In user_profile_page.dart aktiv genutzt. Keine Ănderung erforderlich.
AnforderungserfĂŒllung¶
| Kriterium | Shop (vorher) | Shop (nachher) | ERP |
|---|---|---|---|
| MindestlÀnge | 6 Zeichen | 8 Zeichen | 12 Zeichen |
| Maximal-LĂ€nge (DoS) | â | 128 Zeichen | 128 Zeichen |
| Kleinbuchstabe | â | â | â |
| GroĂbuchstabe | â | â | â |
| Ziffer | â | â | â |
| Sonderzeichen | â | â | â |
| Hinweis im UI | â | â | â |
Status: â
HIGH-9 VOLLSTĂNDIG BEHOBEN
PrioritĂ€t: ~~â ïž TEILWEISE~~ â â
ERLEDIGT 24.02.2026
HIGH-10: â Insecure Random Number Generation â BEHOBEN (24.02.2026)¶
OWASP: A02:2021 â Cryptographic Failures
CWE: CWE-338 (Use of Cryptographically Weak Pseudo-Random Number Generator)
System: Beide Systeme
Problem (Vorher)¶
dart:maths Random() ist ein deterministischer PRNG. Bei bekanntem oder erschöpftem Seed können Angreifer die Ausgabe reproduzieren. Im Projekt wurden vier Stellen mit Random() identifiziert:
| Datei | Verwendung | Risiko |
|---|---|---|
chatbot_service.dart |
ZufÀllige Chatbot-Antwort aus Varianten | niedrig (kein Sicherheitsbezug) |
knowledge_graph_service.dart (Ă3) |
Witze, BegrĂŒĂungen, Personality-Responses | niedrig (kein Sicherheitsbezug) |
random_string_generator.dart |
Passwort-/Token-Generierung | â
war bereits Random.secure() |
request_signing_service.dart |
Nonce-Generierung fĂŒr HMAC-Signaturen | â
war bereits Random.secure() |
Die sicherheitskritischen Stellen (Token- und Nonce-Generierung) verwendeten bereits Random.secure(). Dennoch wurden zur Beseitigung des Findings und zur defensiven HĂ€rtung alle verbliebenen Random()-Vorkommen bereinigt.
Lösung¶
Alle vier Random() â Random.secure() ersetzt in:
apps/erp_system/lib/services/bot_services/chatbot_service.dart
// VORHER
final random = Random();
// NACHHER
final random = Random.secure(); // OWASP HIGH-10: CWE-338 â kryptografisch sicherer PRNG
apps/erp_system/lib/services/bot_services/knowledge_graph_service.dart (3 Stellen: getRandomJoke, getGreeting, getPersonalityResponse)
// VORHER
final random = Random();
// NACHHER
final random = Random.secure(); // OWASP HIGH-10: CWE-338 â kryptografisch sicherer PRNG
Random.secure() nutzt die OS-Entropie (/dev/urandom auf Linux/macOS/iOS, CryptGenRandom auf Windows) und ist damit kryptografisch nicht vorhersehbar.
AnforderungserfĂŒllung¶
| Stelle | Vorher | Nachher |
|---|---|---|
chatbot_service.dart |
Random() â ïž |
Random.secure() â
|
knowledge_graph_service.dart (Ă3) |
Random() â ïž |
Random.secure() â
|
random_string_generator.dart |
Random.secure() â
|
Random.secure() â
|
request_signing_service.dart |
Random.secure() â
|
Random.secure() â
|
Status: â
HIGH-10 VOLLSTĂNDIG BEHOBEN
PrioritĂ€t: ~~đ OFFEN~~ â â
ERLEDIGT 24.02.2026
HIGH-11: â Missing Security Headers (Web) â IMPLEMENTIERT (24.02.2026)¶
OWASP: A05:2021 â Security Misconfiguration
CWE: CWE-693 (Protection Mechanism Failure)
System: Beide
Status: â
IMPLEMENTIERT am 24. Februar 2026
Problem (Vorher)¶
Beiden Web-Apps fehlten essentielle HTTP-Security-Header. Die ERP-App hatte nur fĂŒnf Basis-Header ohne Referrer-Policy, Permissions-Policy und Cross-Origin-Policies. Der Shop hatte ĂŒberhaupt keine Firebase-Hosting-Konfiguration.
Implementierung¶
Security Headers wurden auf drei Ebenen (Defense-in-Depth) implementiert:
Ebene 1: Firebase Hosting â ERP System (firebase.json)
Bestehende Header um fehlende ergÀnzt:
{ "key": "Content-Security-Policy", "value": "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://apis.google.com https://www.gstatic.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://*.googleapis.com https://*.firebaseio.com wss://*.firebaseio.com; frame-src 'none';" },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=(), payment=(), usb=(), bluetooth=()" },
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
{ "key": "Cross-Origin-Resource-Policy", "value": "same-origin" }
AuĂerdem:
frame-src 'self'â'none'(kein Framing erlaubt)
Ebene 2: Firebase Hosting â Shop System (apps/shop_system/firebase.json)
hosting-Sektion mit vollstĂ€ndigem Header-Set neu hinzugefĂŒgt (zuvor nicht vorhanden):
"hosting": {
"public": "build/web",
"headers": [
{ "key": "X-Content-Type-Options", "value": "nosniff" },
{ "key": "X-Frame-Options", "value": "DENY" },
{ "key": "X-XSS-Protection", "value": "1; mode=block" },
{ "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains" },
{ "key": "Content-Security-Policy", "value": "..." },
{ "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
{ "key": "Permissions-Policy", "value": "camera=(), microphone=(), geolocation=(), payment=(), usb=(), bluetooth=()" },
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
{ "key": "Cross-Origin-Resource-Policy", "value": "same-origin" }
],
"rewrites": [{ "source": "**", "destination": "/index.html" }]
}
Ebene 3: HTML Meta-Tags â Fallback fĂŒr beide Apps
(apps/erp_system/web/index.html, apps/shop_system/web/index.html)
<!-- đ SECURITY HIGH-11: Security Headers (Defense-in-Depth via Meta-Tags) -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'
https://apis.google.com https://www.gstatic.com; ...">
<meta http-equiv="X-Content-Type-Options" content="nosniff">
<meta name="referrer" content="strict-origin-when-cross-origin">
GeĂ€nderte Dateien¶
| Datei | Ănderung |
|---|---|
firebase.json |
Referrer-Policy, Permissions-Policy, COOP, CORP hinzugefĂŒgt; frame-src 'none' |
apps/shop_system/firebase.json |
NEU â hosting-Sektion mit vollstĂ€ndigem Header-Set |
apps/erp_system/web/index.html |
Security Meta-Tags (CSP, nosniff, referrer) als Fallback |
apps/shop_system/web/index.html |
Security Meta-Tags (CSP, nosniff, referrer) als Fallback |
Schutz-Matrix¶
| Header | Schutzziel | ERP (vorher) | ERP (nachher) | Shop (nachher) |
|---|---|---|---|---|
Content-Security-Policy |
XSS, Injection | â | â | â |
X-Frame-Options: DENY |
Clickjacking | â | â | â |
X-Content-Type-Options |
MIME-Sniffing | â | â | â |
Strict-Transport-Security |
MITM / HTTPS-Downgrade | â | â | â |
Referrer-Policy |
Datenleck via Referrer | â | â | â |
Permissions-Policy |
Sensorzugriff (Kamera etc.) | â | â | â |
Cross-Origin-Opener-Policy |
Spectre / XS-Leaks | â | â | â |
Cross-Origin-Resource-Policy |
Cross-Origin Reads | â | â | â |
LOW-4 (UnvollstÀndige Security Headers) und LOW-5 (Fehlende CSP) sind durch diese Implementierung ebenfalls vollstÀndig geschlossen.
Status: â
HIGH-11 VOLLSTĂNDIG IMPLEMENTIERT
PrioritĂ€t: ~~đ
4 Wochen~~ â â
ERLEDIGT 24.02.2026
HIGH-12: â Fehlende Android Backup-Schutz â IMPLEMENTIERT (24.02.2026)¶
OWASP: A02:2021 â Cryptographic Failures
CWE: CWE-530 (Exposure of Backup File to Unauthorized Control Sphere)
CVSS Score: 4.7 (Medium-High)
System: Beide
Status: â
IMPLEMENTIERT am 24. Februar 2026
Problem (Vorher)¶
Android ermöglicht standardmĂ€Ăig automatische Backups von App-Daten ĂŒber adb backup und Auto-Backup in Google Drive. Ohne explizite Deaktivierung können sensible Daten (Tokens, verschlĂŒsselte Hive-Boxen, FirebaseApp-State) durch einen physischen GerĂ€tezugriff oder kompromittierten Google-Account extrahiert werden.
Betroffen: android:allowBackup fehlte in beiden AndroidManifest.xml-Dateien (Standardwert ist true bis API 30).
Implementierung¶
In beiden Android-Manifests wurden zwei Attribute explizit auf false gesetzt:
ERP-System (apps/erp_system/android/app/src/main/AndroidManifest.xml)
<application
android:label="easy_sale_erp"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:allowBackup="false"
android:fullBackupContent="false">
Shop-System (apps/shop_system/android/app/src/main/AndroidManifest.xml)
<application
android:label="easy_sale_mobile_app"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:allowBackup="false"
android:fullBackupContent="false"
android:usesCleartextTraffic="false"
android:networkSecurityConfig="@xml/network_security_config">
| Attribut | Wert | Wirkung |
|---|---|---|
android:allowBackup |
false |
Deaktiviert adb backup und lokale GerÀtesicherungen |
android:fullBackupContent |
false |
Deaktiviert Auto-Backup (Google Drive/Cloud) vollstÀndig |
GeĂ€nderte Dateien¶
| Datei | Ănderung |
|---|---|
apps/erp_system/android/app/src/main/AndroidManifest.xml |
android:allowBackup="false" + android:fullBackupContent="false" hinzugefĂŒgt |
apps/shop_system/android/app/src/main/AndroidManifest.xml |
android:allowBackup="false" + android:fullBackupContent="false" hinzugefĂŒgt |
Status: â
HIGH-12 VOLLSTĂNDIG IMPLEMENTIERT
PrioritĂ€t: ~~đ
5 Wochen~~ â â
ERLEDIGT 24.02.2026
đĄ MITTLERE FINDINGS¶
â MED-1: Fehlende Error Boundary (BEHOBEN 24.02.2026)¶
OWASP: A04:2021 â Insecure Design
CWE: CWE-755 (Improper Handling of Exceptional Conditions)
System: Beide Systeme
Status: â
BEHOBEN â AppErrorHandler im shared-Package implementiert; beide Apps nutzen globale Error Boundaries auf drei Ebenen.
Problem (UrsprĂŒnglich)¶
Viele Firestore Queries haben keine Timeout- oder Error-Behandlung:
Shop-System: customer_firebase_service.dart
// Keine Timeouts, keine try-catch, keine Error Recovery
final querySnapshot = await FirebaseFirestore.instance
.collection(customerAccessPermissionCollection)
.where('email', isEqualTo: FirebaseAuth.instance.currentUser?.email)
.get(); // â Kann ewig hĂ€ngen oder crashen
Positiv im ERP: firestore_extensions.dart existiert bereits! â
extension FirestoreExtensions on Query {
Future<QuerySnapshot> getSafe({Duration? timeout}) async {
return await get().timeout(
timeout ?? LoadingConfig.firestoreQueryTimeout,
onTimeout: () => throw TimeoutException(/*...*/),
);
}
}
Lösung (Implementiert)¶
1. AppErrorHandler im shared-Package (packages/shared/lib/services/app_error_handler.dart):
Drei-Schichten-Absicherung in einer zentralen Klasse â kein doppelter Code in ERP und Shop:
// Schicht 1: Framework-Fehler (Widget-Baum, Layout, Rendering)
FlutterError.onError = (details) {
SecureLogger.error('Unhandled Flutter error', error: details.exception);
if (kDebugMode) FlutterError.presentError(details);
};
// Schicht 2: Async-Fehler auf Platform-Ebene (dart:ui PlatformDispatcher)
PlatformDispatcher.instance.onError = (error, stack) {
SecureLogger.error('Unhandled async platform error', error: error);
return true; // App bleibt am Leben
};
// Schicht 3: Release-Build Fallback-Widget statt rotem Fehler-Screen
if (!kDebugMode) {
ErrorWidget.builder = (_) => const AppErrorFallbackWidget();
}
AppErrorHandler.runProtected() steht zusĂ€tzlich fĂŒr runZonedGuarded-Absicherung bereit.
2. Beide main.dart angepasst (ein Aufruf, geteilt):
// apps/erp_system/lib/main.dart & apps/shop_system/lib/main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// đ SECURITY MED-1: Globale Error Boundary (CWE-755, OWASP A04)
AppErrorHandler.initialize(appName: 'easySale ERP'); // bzw. 'easySale Shop'
// ...
}
3. Firestore Timeouts bereits vollstÀndig abgedeckt via packages/shared/lib/extensions/firestore_extensions.dart (getWithTimeout() / snapshotsWithTimeout() in allen Services beider Apps).
â MED-2: XSS-Risiko bei Web-Ansichten (BEHOBEN 24.02.2026)¶
OWASP: A03:2021 â Injection
CWE: CWE-79 (Cross-Site Scripting)
System: Beide Systeme (Web-Builds)
Status: â
BEHOBEN â URL-Schema-Validierung in allen launchUrl-Aufrufstellen implementiert. Verhindert javascript:/data:-URI-Injections.
Problem (UrsprĂŒnglich)¶
An mehreren Stellen in beiden Apps wurden URLs aus Firestore (user-/admin-generierte Inhalte) ohne Schema-PrĂŒfung direkt per url_launcher geöffnet. Im Web-Kontext (Flutter Web) wĂ€re eine javascript:-URI ausfĂŒhrbar gewesen:
// â UNSICHER â URL-Schema nicht geprĂŒft
Future<void> _openUrl(String url) async {
final uri = Uri.parse(url); // url könnte "javascript:alert(1)" sein
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
Betroffene Stellen:
- packages/shared/lib/helpers/url_launcher_helper.dart â zentraler Helper
- apps/shop_system/lib/pages/feed/web/feed_page_web.dart
- apps/shop_system/lib/pages/feed/mobile/feed_page_mobile.dart
- apps/shop_system/lib/pages/feed/mobile/feed_item_detail_page.dart
- apps/shop_system/lib/pages/profile/shared/profile/profile_legal_section.dart
- apps/shop_system/lib/pages/application/web/widgets/drawer/web_drawer_legal_footer.dart
- apps/erp_system/lib/pages/articles/detail_page/widgets/document_list_item.dart
- apps/erp_system/lib/pages/customer/detail_page/dialogs/feed_detail/widgets/feed_document_tile.dart
Lösung (Implementiert)¶
1. Zentrale Validierung in UrlLauncherHelper (shared):
/// Allowed URL schemes â prevents XSS via javascript: / data: URIs (CWE-79)
static const _allowedSchemes = {'https', 'http', 'tel', 'mailto'};
static bool isSafeUrl(Uri uri) =>
_allowedSchemes.contains(uri.scheme.toLowerCase());
static Future<bool> _launchUri(Uri uri, ...) async {
// XSS-Schutz: Nur sichere URL-Schemas erlauben (CWE-79, MED-2)
if (!isSafeUrl(uri)) return false;
if (await canLaunchUrl(uri)) {
return launchUrl(uri, mode: mode);
}
return false;
}
2. Inline-PrĂŒfung an allen direkten launchUrl-Aufrufstellen:
// â
SICHER â Schema-Whitelist verhindert javascript:/data: URIs
Future<void> _openUrl(String url) async {
final uri = Uri.parse(url);
// XSS-Schutz: Nur https/http erlaubt, verhindert javascript: URIs (CWE-79)
if (!{'https', 'http'}.contains(uri.scheme.toLowerCase())) return;
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
}
PrioritĂ€t: â Behoben 24.02.2026
MED-3: Unzureichende Rollback-Mechanismen¶
OWASP: A08:2021 â Software and Data Integrity Failures
CWE: CWE-664 (Improper Control of a Resource)
System: Beide Systeme
Status: â
BEHOBEN am 30.03.2026
Problem¶
Bei kritischen Operationen (z.B. Bestellungen) fehlten Transaktionen oder Rollbacks:
// Beispiel: Order Creation ohne Transaction
await FirebaseFirestore.instance.collection('orders').add(orderData);
await FirebaseFirestore.instance.collection('inventory').doc(articleId).update(...);
// â Wenn zweite Operation fehlschlĂ€gt -> Inkonsistenter State
Implementierte Lösung¶
1. ERP createOrder (Dart) â atomare Transaktion:
core/apps/erp_system/lib/services/firebase_services/order_firebase_service.dart
Counter-Increment und Order-Dokument-Anlage in einem einzigen runTransaction-Aufruf. Sceitert einer der Writes, rollt Firestore automatisch zurĂŒck â kein verwaistes Order-Dokument und keine verlorene Auftragsnummer.
2. Cloud Function createOrder (JS) â atomare Transaktion:
core/functions/src/functions/order.callable.js
Gleiche Logik: Lieferdatum-Berechnung (rein lesend) vor die Transaktion verschoben; Counter-Increment und transaction.set(orderRef, ...) laufen in einem runTransaction-Commit.
3. createCustomerList / updateCustomerList (Dart) â WriteBatch:
core/apps/erp_system/lib/services/firebase_services/customer_lists_firebase_service.dart
Hauptdokument + Status-Items + Action-Items werden in einem WriteBatch gebĂŒndelt. Scheitert ein Write, rollt Firestore alle zurĂŒck â kein halb-angelegtes Dokument ohne Metadaten.
4. updateVariantTargetStock (Dart) â Transaktion:
core/apps/erp_system/lib/services/firebase_services/stock_firebase_service.dart
Lese-dann-Schreibe-Zugriff auf das Article-Dokument in runTransaction gekapselt, um TOCTOU-Race-Conditions zu verhindern.
MED-4-13: Weitere mittlere Findings¶
(Aus PlatzgrĂŒnden verkĂŒrzt - detaillierte Analysen verfĂŒgbar auf Anfrage)
- MED-4: â Biometric Auth Integration - IMPLEMENTIERT in beiden Systemen (siehe HIGH-5)
- MED-5: â
UnverschlĂŒsselte lokale Caches (Hive) â BEHOBEN (
HiveAesCipher+ SchlĂŒssel inFlutterSecureStorageviaEncryptedAppStateService) - MED-6: â Fehlende Jailbreak/Root Detection â BEHOBEN (24.02.2026)
Implementierung im shared-Package (keine Code-Duplizierung):
packages/shared/lib/services/device_integrity_service.dart(neu): EinheitlicherDeviceIntegrityServicemitflutter_jailbreak_detection ^1.9.0. Erkennt iOS-Jailbreak, Android-Root und Android-Developer-Mode. Web/Desktop wird ĂŒbersprungen (fail-open).packages/shared/pubspec.yaml:flutter_jailbreak_detection: ^1.9.0zentral deklariert â beide Apps erben die AbhĂ€ngigkeit transitiv.packages/shared/lib/shared.dart: Service viaexport 'services/device_integrity_service.dart'fĂŒr beide Apps verfĂŒgbar.apps/shop_system/lib/main.dart+apps/erp_system/lib/main.dart:DeviceIntegrityService.check()wird parallel zur Firebase-/Hive-Initialisierung gestartet. Auf kompromittierten GerĂ€ten (Release-Build) startet_CompromisedDeviceAppstatt der Haupt-App.
Sicherheits-Design: Fail-open (kein False Positive), Debug-Mode-Ausnahme fĂŒr Entwickler, zentrales Blocking-Widget. - MED-7: â Unangemessene Permission Requests â BEHOBEN (24.02.2026)
Behobene Stellen (2 ĂŒberschĂŒssige Permissions entfernt, ERP explizit dokumentiert):
- Shop â
android/app/src/main/AndroidManifest.xml:READ_MEDIA_VIDEOundREAD_MEDIA_AUDIOentfernt. Die Shop-App hat keine Video- oder Audio-FunktionalitĂ€t; beide Permissions waren ohne Nutzung deklariert (CWE-250 â Excessive Privilege).READ_MEDIA_IMAGESbleibt fĂŒr die Bildergalerie-Speicherung (vgl. iOSNSPhotoLibraryAddUsageDescription). - ERP â
android/app/src/main/AndroidManifest.xml: Benötigte Permissions (INTERNET,READ_EXTERNAL_STORAGE†API 32,READ_MEDIA_IMAGESfĂŒrimage_picker) explizit deklariert statt implizit per Plugin-Merge. Schafft Transparenz ĂŒber tatsĂ€chlich angeforderte Permissions.
Entfernte Permissions (shop_system):
<!-- ENTFERNT â keine Video-FunktionalitĂ€t in der Shop-App -->
<!-- <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> -->
<!-- <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> -->
Ergebnis shop_system (Android 13+): Nur noch READ_MEDIA_IMAGES statt allen drei Medien-Permissions.
- MED-8: â
Code Obfuscation â BEHOBEN (24.02.2026)
Implementierte MaĂnahmen (4 Dateien + 2 Skripte):
- shop_system â
android/app/build.gradle.kts:isMinifyEnabled = true,isShrinkResources = true,proguardFiles("proguard-android-optimize.txt", "proguard-rules.pro")imrelease-Build-Typ aktiviert â Android R8-Minifizierung/Obfuskierung aktiv. - erp_system â
android/app/build.gradle.kts: Gleiche R8-Konfiguration wie shop_system. - shop_system â
android/app/proguard-rules.pro(neu): Flutter-Engine, Firebase, Kotlin-Coroutines und AndroidX beibehalten; Debug-Log-Calls (Log.v,Log.d) in Prod-Builds unterdrĂŒckt. - erp_system â
android/app/proguard-rules.pro(neu): Gleiche ProGuard-Regeln wie shop_system. - erp_system â
deployment/deploy_flutter_firebase.sh: Web-Build um--obfuscate --split-debug-info=build/debug-infoerweitert â Dart-Code-Obfuskierung fĂŒr Web-Deploy aktiv. - shop_system â
scripts/build_release.sh(neu): EigenstĂ€ndiges Release-Build-Skript mitflutter build appbundle --release --obfuscate --split-debug-info=build/debug-info/android(+ iOS). - erp_system â
scripts/build_release.sh(neu): Gleichwertiges Skript fĂŒr Mobile-Builds.
Dart-Obfuskierungsparameter:
Android R8 (build.gradle.kts):
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
Hinweis Debug-Symbole: Die build/debug-info/-Verzeichnisse enthalten Symbol Maps zur Deobfuszierung von Crashlytics-Stack-Traces. Sie mĂŒssen sicher archiviert werden (z.B. verschlĂŒsselter Cloud-Storage) und dĂŒrfen nicht öffentlich deployed werden.
- MED-9: â Debug-Modus in Production-Builds â BEHOBEN (24.02.2026)
Problem: 20 ungeschĂŒtzte debugPrint()-Aufrufe (ohne kDebugMode-Guard) in beiden Apps. In Flutter-Release-Builds lĂ€uft debugPrint weiterhin durch print(), das auf Android-GerĂ€ten via adb logcat auslesbar ist und auf iOS im System-Log erscheint. Die Ausgaben enthielten interne State-Typen (runtimeType), Order-IDs und Workflow-Details (CWE-489 â Active Debug Code).
Behobene Stellen (6 Dateien, beide Apps):
- Shop â
blocs/system/system_bloc.dart: 2 ungeschĂŒtzte Calls in Timeout/Error-Catch-Blöcken mitif (kDebugMode)umschlossen. - Shop â
pages/articles/web/widgets/web_image_widget.dart: 5 ungeschĂŒtzte Calls in beidenerrorBuilder/errorWidget-Callbacks (Image.network + CachedNetworkImage) mitif (kDebugMode)umschlossen; danebenkDebugModezur selektivenshow-Import-Deklaration hinzugefĂŒgt. - Shop â
services/mobile/article_image_mobile_service.dart: 4 ungeschĂŒtzte Calls in Catch-Blöcken (loadArticleImages,getFirstArticleImage,getFirstImagesForArticles,getVariantImages) mitif (kDebugMode)umschlossen. - ERP â
blocs/order_bloc/orders_bloc.dart: 11 ungeschĂŒtzte Calls in_onCreateOrder(Success-/Error-Pfad, Firebase-Aufruf, State-Polling) mitif (kDebugMode)umschlossen. - ERP â
pages/customer/editor_page/customer_editor_page.dart: 18 ungeschĂŒtzte Calls insaveCustomerInputs()(Update + Create Pfad, Bloc-Access-Check, Formvalidierung, State-Polling) mitif (kDebugMode)umschlossen. - ERP â
pages/order/dialogs/order_editor_dialog.dart: 9 ungeschĂŒtzte Calls ininitState, BlocListener und Save-Button-Handler mitif (kDebugMode)umschlossen.
Angewandtes Muster:
// Vorher â lĂ€uft in Release-Build durch:
debugPrint('â Error: $error');
// Nachher â nur in Debug-Builds:
if (kDebugMode) {
debugPrint('â Error: $error');
}
Hinweis: ssl_pinning_service.dart nutzt print() bewusst fĂŒr Security-Events (SSL-Pinning-Fehler) â diese bleiben unverĂ€ndert, da SicherheitsvorfĂ€lle auch in Production geloggt werden sollen. request_signing_interceptor.dart nutzt assert() fĂŒr Debug-Output â korrekt, da assert() in Release-Builds komplett wegoptimiert wird.
- MED-10: â
Unvalidierte Deep Links â BEHOBEN (24.02.2026)
Problem: Alle launchUrl-Aufrufe enthielten duplizierte, inline-gestreute Scheme-PrĂŒfungen ({'https','http'}.contains(uri.scheme)). Bei zukĂŒnftigen Erweiterungen (z.B. tel:, mailto:) bestand das Risiko, dass neue Call-Sites ohne Validierung hinzugefĂŒgt werden (CWE-20 â Improper Input Validation).
Behobene Stellen (7 Dateien, beide Apps):
- Shared â
packages/shared/lib/helpers/url_launcher_helper.dart: Neue MethodelaunchExternal(String url)hinzugefĂŒgt â öffnet URLs imLaunchMode.externalApplicationmit zentraler Scheme-PrĂŒfung.UrlLauncherHelperinshared.dartexportiert. - Shop â
feed_page_mobile.dart:_openUrl()âUrlLauncherHelper.launchExternal(url). Importurl_launcherentfernt. - Shop â
feed_item_detail_page.dart:_openUrl()âUrlLauncherHelper.launchExternal(url). Importurl_launcherentfernt. - Shop â
feed_page_web.dart:_openUrl()âUrlLauncherHelper.launchExternal(url). Importurl_launcherentfernt. - Shop â
web_drawer_legal_footer.dart:_buildLegalLink()inline âUrlLauncherHelper.launchExternal(url). Importurl_launcherentfernt,url_launcher_helperdirekt importiert. - Shop â
profile_legal_section.dart: 3Ă inline Launch-Block (AGB, Datenschutz, Impressum) âUrlLauncherHelper.launchUrlString(url). Importurl_launcherentfernt. - ERP â
document_list_item.dart:_launchUrl()mit inline Try-Catch âUrlLauncherHelper.launchUrlString(url). Importsurl_launcherundsecure_logger(nur fĂŒr URL-Fehler genutzt) entfernt. - ERP â
feed_document_tile.dart: Top-Level_openDocument()âUrlLauncherHelper.launchExternal(url). Importurl_launcherentfernt.
Zentrales Validierungsmuster (alle Pfade laufen durch):
// packages/shared/lib/helpers/url_launcher_helper.dart
static const _allowedSchemes = {'https', 'http', 'tel', 'mailto'};
static bool isSafeUrl(Uri uri) =>
_allowedSchemes.contains(uri.scheme.toLowerCase());
static Future<bool> launchExternal(String url) async =>
_launchUri(Uri.parse(url), mode: LaunchMode.externalApplication);
static Future<bool> _launchUri(Uri uri, {LaunchMode mode = ...}) async {
if (!isSafeUrl(uri)) return false; // Blockt javascript:, data:, file: etc.
if (await canLaunchUrl(uri)) return launchUrl(uri, mode: mode);
return false;
}
DataIntegrityService im shared-Package (CWE-353):
- Firestore writes: stampDocument() berechnet SHA-256 ĂŒber kritische Felder (customerId, amount, rebate, orderStatus, orderDate, orderPositions) und speichert _integrity-Metadaten im Dokument.
- Firestore reads: verifyDocument() rekonstruiert den Hash und loggt via SecureLogger.warning() bei Mismatch.
- Kanonisierung: Felder alphabetisch sortiert + jsonEncode() â resistenter gegen Feld-Reihenfolge-Angriffe.
- Integration: Shop OrderFirebaseAdapter (add + get) und ERP OrderFirebaseService (createOrder, _mapToOrders, streamOrders).
- Lokal: wrapWithHmac() / unwrapAndVerify() fĂŒr Hive-Cache â HMAC-Key = UID + ID-Token (wie HIGH-7).
- MED-12: â
Race Conditions bei State Updates â BEHOBEN (24.02.2026)
Behobene Stellen (4 Race Conditions):
- Shop â
auth_bloc.dart(Listener):FirebaseAuth.instance.authStateChanges().listen((user) async { emit(...) })âasync-Keyword entfernt,if (!isClosed && user != null)-Guard hinzugefĂŒgt. Verhindertemit()nach Bloc-Dispose (CWE-362). - Shop â
auth_bloc.dart/cart_bloc.dart/order_bloc.dart:on<SignIn>,on<SignUp>,on<PasswordReset>,on<SignOut>,on<AuthenticateWithBiometric>,on<AddReorderToCart>,on<AddOrder>,on<CancelOrder>mittransformer: droppable()registriert. Verhindert gleichzeitige doppelte Event-Verarbeitung. - Shop â
cart_bloc.dart(AddReorderToCart): Ohne Lock konnte ein zweites gleichzeitigesAddReorderToCart-Event den Warenkorb korrumpieren, da_cartItems = []und derawait-Firebase-Call nicht atomar waren âdroppable()verwirft konkurrierende Events. - ERP â
auth_bloc.dart:on<LoginUser>,on<LogoutUser>,on<CheckIfUserIsLoggedIn>,on<ResetUserPassword>,on<AuthenticateWithBiometric>mittransformer: droppable()registriert. Verhindert parallele Auth-ZustandsĂŒbergĂ€nge.
Neues Dependency in beiden Apps:
Angewandtes Muster:
import 'package:bloc_concurrency/bloc_concurrency.dart';
// Kritische Aktionen: droppable() â ignoriert neue Events wĂ€hrend Verarbeitung lĂ€uft
on<AddOrder>(_onAddOrder, transformer: droppable());
on<LoginUser>(_onLoginUser, transformer: droppable());
// Listener auĂerhalb von Emitter: isClosed-Guard
stream.listen((data) {
if (!isClosed) emit(NewState(data)); // â
Kein emit nach dispose
});
Behobene Stellen (3 Leaks):
- Shop â
auth_bloc.dart:FirebaseAuth.instance.authStateChanges().listen()âStreamSubscription<User?>? _authStateSubscriptiongespeichert,close()Override mitcancel()hinzugefĂŒgt. - ERP â
app_bloc_providers.dart/nav_bar_bloc.dart:ConfigEventBus.stream.listen()war nicht gespeichert â Subscription inNavBarBlocverschoben (_configEventBusSubscription),close()Override mitcancel()hinzugefĂŒgt. Nicht mehr benötigte Imports entfernt. - ERP â
system_settings_bloc.dart:_pushNotificationSettingsSubscriptionwar gespeichert aber nie gecancelt (keinclose()Override) âclose()Override mit_pushNotificationSettingsSubscription?.cancel()hinzugefĂŒgt.
Muster in allen Fixes:
// Subscription als Feld speichern
StreamSubscription<T>? _subscription;
// Im Konstruktor/Handler zuweisen
_subscription = stream.listen(...);
// In close() canceln
@override
Future<void> close() async {
await _subscription?.cancel();
return super.close();
}
đą NIEDRIGE FINDINGS¶
LOW-1-7: Informational Findings¶
- LOW-1: Fehlende Security.md Datei
- LOW-2: â Veraltete Dependencies / Dependency Audit â BEHOBEN (24.02.2026)
flutter pub upgradein allen Paketen durchgefĂŒhrt (shop_system, erp_system, shared)- shop_system: 13 Dependencies aktualisiert (u.a.
get_it9.2.0â9.2.1,google_fonts8.0.1â8.0.2,uuid4.5.2â4.5.3,json_annotation4.10.0â4.11.0) - erp_system: 26 Dependencies aktualisiert (u.a.
build_runner2.7.1â2.11.1,uuid4.5.2â4.5.3,ffi2.1.5â2.2.0,analyzer8.4.1â10.0.1) - shared: 2 Dependencies aktualisiert (
url_launcher_ios,url_launcher_web) - Verbleibende Major-Version-Upgrades (Breaking Changes) zur manuellen Migration dokumentiert:
flutter_secure_storage9.2.4 â 10.0.0 (beide Apps)local_auth2.3.0 â 3.0.0 (beide Apps)flutter_map7.0.2 â 8.2.2 (erp_system)google_fonts6.3.3 â 8.0.2 (erp_system)http1.2.2 â 1.6.0 (erp_system)js0.6.7 â 0.7.2 (discontinued â Migration zudart:js_interopnötig)
- LOW-3: Fehlende Penetration Test Dokumentation
- LOW-4: â UnvollstĂ€ndige Security Headers (Web) â BEHOBEN durch HIGH-11 (24.02.2026)
- LOW-5: â Fehlende Content-Security-Policy â BEHOBEN durch HIGH-11 (24.02.2026)
- LOW-6: Keine Responsible Disclosure Policy
- LOW-7: Fehlende Security Training Dokumentation
â POSITIVE FINDINGS (Best Practices)¶
1. Rate Limiting in beiden Systemen â ¶
// Shop + ERP nutzen RateLimiterService aus shared-Package
if (!RateLimiterService.canAttemptLogin(event.email)) { /*...*/ }
// RateLimiterService ist jetzt offiziell ĂŒber shared.dart exportiert
2. FlutterSecureStorage fĂŒr Credentials â ¶
// Passwörter werden verschlĂŒsselt gespeichert
await storage.write(key: 'saved_password', value: password);
3. Token Refresh Logik â ¶
4. SecureLogger in beiden Systemen â ¶
// Sensible Daten werden nicht geloggt - jetzt Shop + ERP
SecureLogger.debug('â
FCM Token retrieved successfully'); // Token selbst nie geloggt
SecureLogger.debug('Device ID: ${SecureLogger.sanitize(deviceId)}'); // IDs sanitized
// SecureLogger ist jetzt offiziell ĂŒber shared.dart exportiert
5. Firestore Query Timeouts â ¶
// Extension fĂŒr sichere Queries
extension FirestoreExtensions on Query {
Future<QuerySnapshot> getSafe({Duration? timeout}) {/*...*/}
}
6. SSL Pinning Service vorhanden â ¶
7. Multi-Tenancy-Aware Design â ¶
8. Biometrische Authentifizierung â (ERP + Shop)¶
// Shop: UI-Trigger via BlocConsumer in responsive_sign_in_form.dart
context.read<AuthBloc>().add(AuthenticateWithBiometric());
// Shared: BaseAuthService
final authenticated = await authService.authenticateWithBiometrics(
localizedReason: 'Bitte authentifizieren Sie sich fĂŒr den Zugang zur App',
);
9. Firebase Storage URL-Validierung â NEU¶
// Alle Downloads validiert gegen Firebase Storage Domains
FirebaseStorageUrlValidator.validate(document.downloadUrl);
// SchĂŒtzt vor: Path Traversal, externe URLs, HTTP-Downgrade
10. Session Management aktiv in beiden Apps â NEU¶
// Lifecycle-aware Session Validation
authService.startPeriodicTokenRefresh(); // alle 4 Minuten
authService.validateAndRefreshSession(); // bei App Resume
// Automatischer Logout nach 12h Session-Timeout
11. HMAC-SHA256 Request Signing â NEU¶
// Dio HTTP-Requests werden automatisch signiert:
dio.interceptors.add(RequestSigningInterceptor(appId: 'easysale_shop'));
// X-Request-Timestamp / X-Request-Nonce / X-Request-Signature
// Cloud Function Payloads:
final signedData = await _requestSigning.signCloudFunctionPayload(
functionName: functionName, data: data,
);
// Replay-Schutz: ±5-Minuten-Fenster, einmalige Nonce, Auth-Token-gebunden
12. Transport Security (3-Layer Defense-in-Depth) â NEU¶
<!-- Android: network_security_config.xml -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors><certificates src="system" /></trust-anchors>
</base-config>
<!-- iOS: Info.plist -->
<!-- NSAllowsArbitraryLoads = false, NSExceptionMinimumTLSVersion = TLSv1.2 -->
// Dart: TransportSecurityService
TransportSecurityService.initialize(); // blockiert HTTP auf App-Ebene
TransportSecurityService.requireHttps(url); // URL-Validator vor Requests
đ OWASP TOP 10 MAPPING¶
| OWASP Category | Findings | Severity | Status |
|---|---|---|---|
| A01: Broken Access Control | CRIT-1, CRIT-4, HIGH-1 | đŽ Kritisch | â VollstĂ€ndig behoben |
| A02: Cryptographic Failures | â CRIT-2, â CRIT-5, â HIGH-8, â MED-5 | đą Niedrig | â VollstĂ€ndig behoben (CRIT-2 â , CRIT-5 â , HIGH-8 â , MED-5 â ) |
| A03: Injection | â CRIT-3, â MED-2 | đą Niedrig | â VollstĂ€ndig behoben |
| A04: Insecure Design | â HIGH-1, â MED-1, â MED-3 | â VollstĂ€ndig | â Alle behoben |
| A05: Security Misconfiguration | â MED-9, â LOW-4, â LOW-5 | đą Niedrig | â VollstĂ€ndig behoben |
| A06: Vulnerable Components | â LOW-2 | đą Niedrig | â Dependency Upgrade durchgefĂŒhrt (24.02.2026) |
| A07: Auth Failures | â HIGH-4, â HIGH-5, â MED-4 | đą Niedrig | â VollstĂ€ndig implementiert (beide Systeme) |
| A08: Data Integrity | â HIGH-3, â HIGH-7, â MED-3, â MED-11 | â VollstĂ€ndig | â Alle behoben |
| A09: Logging Failures | â HIGH-2 | đą Niedrig | â Implementiert |
| A10: SSRF | - | - | â Not Applicable |
đŻ PRIORISIERTER ACTION PLAN¶
Phase 1: SOFORT (0-1 Woche) đ„¶
- CRIT-1: Firestore Security Rules hÀrten (Shop-System)
- CRIT-2: E-Mail-Speicherung auf FlutterSecureStorage umstellen
- ~~HIGH-2: Debug-Logs bereinigen~~ â â ERLEDIGT
Estimated Effort: 1-2 Entwicklertage (reduziert)
Phase 2: HOCH (2-4 Wochen) đ ¶
- CRIT-3: Input Validation Service implementieren
- ~~CRIT-4: Zentrale Auth-Helper Klasse~~ â â ERLEDIGT
- ~~CRIT-5: SSL Pinning vollstĂ€ndig implementieren~~ â â ERLEDIGT 24.02.2026
- ~~HIGH-7: Request Signing~~ â â ERLEDIGT
- ~~HIGH-1: Rate Limiting im Shop-System~~ â â ERLEDIGT
- ~~HIGH-3: Secure Download Service~~ â â ERLEDIGT
- ~~HIGH-4: Session Management~~ â â ERLEDIGT
- ~~HIGH-8: Insufficient Transport Security~~ â â ERLEDIGT
- ~~HIGH-11: Security Headers (Web)~~ â â ERLEDIGT
Estimated Effort: ~1 Sprint (reduziert da HIGH-1/3/4/7/8/11 erledigt)
Phase 3: MITTEL (1-3 Monate) đĄ¶
- MED-1 bis MED-13: Alle mittleren Findings
- Code Reviews etablieren
- Security Testing in CI/CD Pipeline
- Penetration Testing durchfĂŒhren
Estimated Effort: 1-2 Monate
Phase 4: KONTINUIERLICH đ¶
- LOW-1, LOW-3, LOW-6, LOW-7: Informational Findings (LOW-4/5/2 â
erledigt)
- ~~LOW-2: Veraltete Dependencies~~ â â
ERLEDIGT
flutter pub upgradedurchgefĂŒhrt (24.02.2026)
- ~~LOW-2: Veraltete Dependencies~~ â â
ERLEDIGT
- RegelmĂ€Ăige Dependency Audits
- Quartalsweise Security Reviews
- Security Training fĂŒr das Team
đ§ EMPFOHLENE TOOLS & SERVICES¶
Code Analysis¶
- flutter_lints - Linting Rules â (bereits aktiv)
- Snyk - Vulnerability Scanner
- OWASP Dependency-Check - Dependency Audit
- SonarQube - Code Quality & Security
Runtime Protection¶
- Firebase App Check - Bot Protection
- Google Cloud Armor - DDoS Protection
- ProGuard/R8 - Code Obfuscation (Android)
Testing & Monitoring¶
- OWASP ZAP - Penetration Testing
- Burp Suite - API Security Testing
- Firebase Crashlytics - Runtime Monitoring â (bereits aktiv)
- Sentry - Error Tracking
đ COMPLIANCE & STANDARDS¶
DSGVO Compliance¶
- â Passwort-VerschlĂŒsselung
- â ïž E-Mail in Plaintext (CRIT-2)
- â ïž Logs mit personenbezogenen Daten (HIGH-2)
- â ïž Fehlende Data Retention Policies
OWASP MASVS (Mobile)¶
- MSTG-STORAGE: Teilweise erfĂŒllt (FlutterSecureStorage â , SharedPreferences â ïž)
- MSTG-CRYPTO: Teilweise erfĂŒllt (SSL Pinning fehlt)
- MSTG-AUTH: Gut erfĂŒllt (Rate Limiting â , Session Management â ïž)
- MSTG-NETWORK: Teilweise erfĂŒllt (Certificate Pinning missing)
- MSTG-RESILIENCE: Nicht erfĂŒllt (No Jailbreak Detection)
đ NEXT STEPS¶
Immediate Actions¶
- Security Team Meeting - Priorisierung der Findings
- Emergency Fixes - CRIT-1, CRIT-2 sofort beheben
- Firestore Rules Deployment - Neue Rules in Staging testen
Short Term¶
- Sprint Planning - HIGH Findings in nÀchste 2-3 Sprints
- Code Review Process - Security Checklist etablieren
- Developer Training - OWASP Top 10 Workshop
Long Term¶
- Security Roadmap - Quartalsweise Security Audits
- Penetration Testing - Externe Security Firma engagieren
- Bug Bounty Program - Community-basierte Security
đ ZUSAMMENFASSUNG¶
Kritische Erkenntnisse¶
- Shop-System Security Rules sind zu permissiv - Höchste PrioritÀt
- Credential Storage teilweise unsicher - E-Mail muss verschlĂŒsselt werden
- Input Validation fehlt system-weit - NoSQL Injection Risiko
- SSL Pinning nicht vollstÀndig - MITM Angriffe möglich
- ~~Rate Limiting nur im ERP~~ - Shop-System ungeschĂŒtzt â â BEHOBEN
Positive Aspekte¶
- ERP-System hat starke Security-Foundations (Rate Limiting, SecureLogger)
- FlutterSecureStorage wird richtig genutzt (Passwörter)
- Multi-Tenancy Design ist prinzipiell vorhanden
- Firestore Extensions fĂŒr sichere Queries existieren
- â Biometrische Authentifizierung vollstĂ€ndig implementiert (HIGH-5)
Implementierungsfortschritt¶
- â CRIT-4: currentUser ohne Null-Check (14+ Stellen) - IMPLEMENTIERT am 24. Februar 2026
- â HIGH-1: Rate Limiting Shop-System - IMPLEMENTIERT am 24. Februar 2026
- â HIGH-2: Token aus Debug-Logs - IMPLEMENTIERT am 24. Februar 2026
- â HIGH-3: File Download Validierung - IMPLEMENTIERT am 24. Februar 2026
- â HIGH-4: Session Timeout (beide Systeme) - IMPLEMENTIERT am 24. Februar 2026
- â HIGH-5: Biometric Auth (beide Systeme) - IMPLEMENTIERT am 24. Februar 2026
- â HIGH-7: Request Signing HMAC-SHA256 - IMPLEMENTIERT am 24. Februar 2026
- â HIGH-8: Insufficient Transport Security (Android NSC + iOS ATS + Dart HttpOverrides) - IMPLEMENTIERT am 24. Februar 2026
- â MED-4: Biometric Integration (beide Systeme) - IMPLEMENTIERT
- â
MED-1: Fehlende Error Boundary â
AppErrorHandlerim shared-Package â IMPLEMENTIERT am 24. Februar 2026 - Status: 30 von 37 Findings behoben (81.1%)
- Trend: đą Positiv - Security wird aktiv und schnell verbessert
Gesamt-Risiko-Bewertung¶
MITTEL - Kritische Findings in A01/A02/A03 erfordern noch MaĂnahmen. HIGH-Findings (A07, A08, A09, A04) wurden vollstĂ€ndig behoben. Mit den empfohlenen weiteren Fixes wird System auf NIEDRIG Risiko reduziert.
Update 24.02.2026 (Initial): HIGH-5 (Biometric Auth ERP) und HIGH-1/2/3/4 implementiert. Gesamt-Risiko von HOCH auf MITTEL reduziert. â
Update 24.02.2026 (Folge-Sprint): HIGH-5 Shop-System vollstĂ€ndig implementiert â AuthBloc-Events/-States, Handler via AuthService.authenticateWithBiometrics(), Fingerprint-Button-UI in responsive_sign_in_form.dart. A07 nun vollstĂ€ndig in beiden Systemen abgedeckt. â
Update 24.02.2026 (Request Signing): HIGH-7 implementiert â RequestSigningService (HMAC-SHA256, shared-Package), RequestSigningInterceptor (Dio, Shop-System), Cloud Function Payload Signing (ERP). Replay-Schutz durch ±5-Minuten-Fenster + Nonce. A08 weiter gestĂ€rkt. â
Update 24.02.2026 (Transport Security): HIGH-8 implementiert â Android network_security_config.xml (cleartext blockiert, User-CAs nur in Debug), iOS NSAppTransportSecurity (TLS 1.2+, ForwardSecrecy, kein NSAllowsArbitraryLoads), Dart TransportSecurityService (HttpOverrides, URL-Validator). Defense-in-Depth Transport Security vollstĂ€ndig. â
- OWASP A07 (Auth): đą Niedrig â alle 3 relevanten Findings implementiert
- OWASP A09 (Logging): đą Niedrig â HIGH-2 implementiert
- OWASP A08 (Integrity): â
VollstĂ€ndig â HIGH-3 + HIGH-7 + MED-11 + MED-3 â
alle behoben
Update 24.02.2026 (Error Boundaries): MED-1 implementiert â AppErrorHandler im shared-Package (FlutterError.onError + PlatformDispatcher.instance.onError + Release-ErrorWidget.builder + runZonedGuarded-Helfer). Beide Apps in einer Zeile integriert. Firestore-Timeout-Extensions (getWithTimeout/snapshotsWithTimeout) vollstĂ€ndig ausgerollt. A04 weiter gestĂ€rkt. â
- OWASP A04 (Design): â
VollstĂ€ndig â HIGH-1 + MED-1 + MED-3 + MED-6 + MED-7 â
alle behoben
- OWASP A02 (Cryptographic Failures): ïżœ Niedrig â HIGH-8 â
, CRIT-2 â
, CRIT-5 â
â vollstĂ€ndig behoben
- OWASP A01 (Access Control): ïżœ Niedrig â CRIT-1 â
, CRIT-4 â
â vollstĂ€ndig behoben
11. AuthHelper â Sicherer Firebase Auth-Zugriff â NEU¶
// Verhindert App-Crashes und Silent-Null-Queries (CWE-476)
final email = AuthHelper.getRequiredEmail(); // wirft UnauthorizedException
final uid = AuthHelper.getRequiredUid(); // wirft UnauthorizedException
- OWASP A02/A03 (Crypto/Injection): â
VollstĂ€ndig behoben â CRIT-2 â
, CRIT-3 â
, CRIT-5 â
Dieser Bericht ist vertraulich und nur fĂŒr interne Nutzung bestimmt.
FĂŒr Fragen: security@easysale.com
đ ANHANG: CODE-BEISPIELE¶
Anhang A: Sichere Firestore Rules (Komplett)¶
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ... (siehe CRIT-1 fĂŒr vollstĂ€ndige Rules)
}
}
Anhang B: Input Validator (VollstĂ€ndig)¶
Anhang C: Secure Logger Service¶
Audit durchgefĂŒhrt am 24. Februar 2026
NĂ€chstes Audit empfohlen: Mai 2026