Inhaltsverzeichnis
Sicherheitsrichtlinien
Verbindliche Sicherheitsstandards für WvdS FPC RAD Suite, konform mit KRITIS/NIS2.
OWASP Top 10 Prävention
CWE-78: Command Injection
Risiko: Ausführung beliebiger Befehle durch manipulierten Input.
// VERBOTEN - Unsicher! Exec('cmd /c ' + UserInput); Shell('fpc ' + ProjectPath + ' ' + UserArgs); // KORREKT - Parameterisiert Options.Shell := False; Args := TStringArray.Create; Args.Add(ProjectPath); Spawn('fpc', Args, Options);
Maßnahmen:
- Immer
shell: falseverwenden - Argumente als Array übergeben
- Pfade validieren vor Verwendung
CWE-22: Path Traversal
Risiko: Zugriff auf Dateien außerhalb des erlaubten Verzeichnisses.
// VERBOTEN - Unsicher! FileName := BasePath + UserInput; ReadFile(FileName); // KORREKT - Validierung function IsPathSafe(const ABasePath, AUserInput: string): Boolean; var ResolvedPath: string; begin // Keine .. erlauben if Pos('..', AUserInput) > 0 then Exit(False); ResolvedPath := ExpandFileName(Concat(ABasePath, AUserInput)); Result := Pos(ABasePath, ResolvedPath) = 1; end; if IsPathSafe(BasePath, UserInput) then ReadFile(Concat(BasePath, UserInput));
Maßnahmen:
..Sequenzen blockieren- Pfade normalisieren und validieren
- Basis-Pfad muß Präfix des Ergebnis-Pfads sein
CWE-20: Input Validation
Risiko: Ungültige Daten führen zu Fehlfunktion oder Angriff.
// VERBOTEN - Keine Validierung procedure CreateProject(const AName: string); begin MakeDir(AName); // Was wenn AName = '../../../etc'? end; // KORREKT - Validierung function ValidateProjectName(const AName: string; out AError: string): Boolean; const ALLOWED_CHARS = ['a'..'z', 'A'..'Z', '0'..'9', '_', '-']; MAX_LENGTH = 64; var I: Integer; begin Result := False; if AName = '' then begin AError := rsProjectNameEmpty; Exit; end; if Length(AName) > MAX_LENGTH then begin AError := Format(rsProjectNameTooLong, [MAX_LENGTH]); Exit; end; for I := 1 to Length(AName) do if not (AName[I] in ALLOWED_CHARS) then begin AError := Format(rsProjectNameInvalidChar, [AName[I]]); Exit; end; Result := True; end;
CWE-316: Cleartext Credentials
Risiko: Credentials im Code oder in Logs.
// VERBOTEN - Credentials im Code const API_TOKEN = 'ghp_xxxxxxxxxxxx'; DB_PASSWORD = 'secret123'; // VERBOTEN - Credentials loggen LogDebug('Token: %s', [Token]); // KORREKT - Environment Variables Token := GetEnvironmentVariable('GITHUB_TOKEN'); if Token = '' then raise EWvdSConfigError.Create(rsTokenNotConfigured); // KORREKT - Maskiertes Logging LogDebug('Token configured: %s', [BoolToStr(Token <> '', True)]);
CWE-532: Log Injection
Risiko: Sensible Daten in Logs sichtbar.
// VERBOTEN LogInfo('User login: %s with password: %s', [User, Password]); LogDebug('API response: %s', [FullResponse]); // Kann Tokens enthalten! // KORREKT LogInfo('User login: %s', [User]); // Kein Password LogDebug('API response received, length: %d', [Length(Response)]);
CWE-79: Cross-Site Scripting (XSS)
Risiko: Einschleusen von Script-Code in WebViews.
// VERBOTEN - Unescaped HTML WebView.Html := '<div>' + UserInput + '</div>'; // KORREKT - Escaping function EscapeHtml(const AText: string): string; begin Result := AText; Result := StringReplace(Result, '&', '&', [rfReplaceAll]); Result := StringReplace(Result, '<', '<', [rfReplaceAll]); Result := StringReplace(Result, '>', '>', [rfReplaceAll]); Result := StringReplace(Result, '"', '"', [rfReplaceAll]); Result := StringReplace(Result, '''', ''', [rfReplaceAll]); end; WebView.Html := '<div>' + EscapeHtml(UserInput) + '</div>';
CWE-20: WebView Message Whitelist
Risiko: Unbekannte Message-Typen ermöglichen Angriffe auf WebView-Handler.
// VERBOTEN - Kein Message-Type-Check procedure HandleMessage(AMessage: TJSObject); var MsgType: string; begin MsgType := string(AMessage['type']); case MsgType of 'browse': HandleBrowse(AMessage); 'save': HandleSave(AMessage); end; end; // KORREKT - Mit Whitelist const ALLOWED_MESSAGE_TYPES: array[0..6] of string = ( 'browse', 'save', 'cancel', 'validatePath', 'autoDetect', 'config', 'pathSelected' ); function IsAllowedMessageType(const AType: string): Boolean; var I: Integer; begin Result := False; for I := Low(ALLOWED_MESSAGE_TYPES) to High(ALLOWED_MESSAGE_TYPES) do if ALLOWED_MESSAGE_TYPES[I] = AType then Exit(True); end; procedure HandleMessage(AMessage: TJSObject); var MsgType: string; begin MsgType := string(AMessage['type']); // Whitelist-Prüfung ZUERST if not IsAllowedMessageType(MsgType) then begin LogWarning(rsUnknownMessageType, [MsgType]); Exit; end; case MsgType of 'browse': HandleBrowse(AMessage); 'save': HandleSave(AMessage); // ... end; end;
Maßnahmen:
- Whitelist mit allen erlaubten Message-Typen definieren
- Unbekannte Typen loggen und ablehnen
- Bei neuen Funktionen Whitelist aktualisieren
Defensive Node.js API-Aufrufe
Risiko: Node.js APIs können je nach Kontext unterschiedliche Methoden haben.
stdout.setEncoding ist keine Funktion wenn Process nicht korrekt initialisiert ist.
// VERBOTEN - Direkte Methodenaufrufe ohne Check procedure SetupProcessHandlers; begin asm this.FProcess.stdout.setEncoding('utf8'); this.FProcess.stderr.setEncoding('utf8'); end; end; // KORREKT - Defensive typeof-Checks procedure SetupProcessHandlers; begin asm if (this.FProcess && this.FProcess.stdout) { if (typeof this.FProcess.stdout.setEncoding === 'function') { this.FProcess.stdout.setEncoding('utf8'); } if (typeof this.FProcess.stdout.on === 'function') { this.FProcess.stdout.on('data', this.HandleStdout.bind(this)); } } if (this.FProcess && this.FProcess.stderr) { if (typeof this.FProcess.stderr.setEncoding === 'function') { this.FProcess.stderr.setEncoding('utf8'); } if (typeof this.FProcess.stderr.on === 'function') { this.FProcess.stderr.on('data', this.HandleStderr.bind(this)); } } end; end;
Maßnahmen:
typeof … === 'function' vor jedem Methodenaufruf- Objektexistenz prüfen (
if (obj && obj.property)) - Fehlende APIs graceful behandeln
Error Handling
Keine leeren Exception-Handler
// VERBOTEN try DoSomething; except // Fehler verschlucken end; // KORREKT try DoSomething; except on E: ESpecificError do begin LogError(rsSpecificError, [E.Message]); // Behandlung... end; on E: Exception do begin LogError(rsUnexpectedError, [E.ClassName, E.Message]); raise; // Oder angemessen behandeln end; end;
Spezifische Exception-Typen
// KORREKT - Spezifische Exceptions try Result := DoOperation; except on E: EFileNotFoundException do HandleFileNotFound(E.FileName); on E: EAccessDenied do HandleAccessDenied(E.Path); on E: ENetworkError do HandleNetworkError(E.Url); on E: Exception do HandleUnexpectedError(E); end;
Debug-Logging
Debug-Logging erfordert zwei Bedingungen:
- Compile-Zeit:
{$IFDEF DEBUG} - Laufzeit:
–debugParameter
{$IFDEF DEBUG} var DebugLogFile: TextFile; DebugEnabled: Boolean; procedure InitDebugLogging; begin DebugEnabled := ParamStr(1) = '--debug'; if DebugEnabled then begin AssignFile(DebugLogFile, Format('debug-%s.log', [FormatDateTime('yymmddhhnnss', Now)])); Rewrite(DebugLogFile); end; end; procedure LogDebugTrace(const AMessage: string; const AArgs: array of const); begin if not DebugEnabled then Exit; WriteLn(DebugLogFile, Format('[%s] %s', [FormatDateTime('hh:nn:ss.zzz', Now), Format(AMessage, AArgs)])); Flush(DebugLogFile); end; {$ENDIF}
Was darf geloggt werden
| Erlaubt | Verboten |
|---|---|
| Dateinamen, Pfade | Tokens, API-Keys |
| Aktionsnamen | Paßwörter |
| Numerische IDs | Session-IDs |
| Fehlermeldungen | Vollständige Requests/Responses |
| Konfigurationskeys | Konfigurationswerte (sensibel) |
Sichere Defaults
// Sichere Standardwerte const DEFAULT_TIMEOUT = 30000; // 30 Sekunden MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB MAX_RECURSION_DEPTH = 100; type TSpawnOptions = record Shell: Boolean; // Default: False (sicher) Timeout: Integer; // Default: 30000 WorkingDir: string; Environment: TStringArray; end; function DefaultSpawnOptions: TSpawnOptions; begin Result.Shell := False; // WICHTIG: Kein Shell! Result.Timeout := DEFAULT_TIMEOUT; Result.WorkingDir := ''; Result.Environment := nil; end;
Code Review Checkliste
Vor Merge MUSS geprüft werden:
SICHERHEIT: [ ] Keine hardcodierten Credentials [ ] Alle Benutzereingaben validiert [ ] Keine Shell-Injection möglich [ ] Keine Path-Traversal möglich [ ] HTML wird escaped in WebViews [ ] Keine sensiblen Daten in Logs ERROR HANDLING: [ ] Keine leeren Exception-Handler [ ] Spezifische Exceptions behandelt [ ] Fehler werden geloggt (ohne sensible Daten) KONFIGURATION: [ ] Sichere Defaults verwendet [ ] Timeouts definiert [ ] Limits definiert (Dateigröße, Rekursion)
Sicherheits-Audit
Regelmäßige Prüfung mit automatisierten Tools:
wvds-lint Sicherheits-Checks
wvds-lint security --path sources/
Geprüft wird:
- Exec/Shell-Aufrufe
- Path-Operationen ohne Validierung
- Hardcodierte Strings (potentielle Credentials)
- Leere Exception-Handler
- Log-Aufrufe mit sensiblen Parametern
Manuelle Review
Bei jeder neuen Extension:
- Alle Entry-Points prüfen
- Alle externen Inputs tracen
- WebView-Kommunikation prüfen
Incident Response
Bei Entdeckung einer Sicherheitslücke:
- Nicht veröffentlichen bis Patch bereit
- Issue auf GitHub mit Label „security“ (privat)
- Patch entwickeln und testen
- Neue Version releasen
- Advisory veröffentlichen