Inhaltsverzeichnis
Linee guida sulla sicurezza
Standard di sicurezza vincolanti per la suite WvdS FPC RAD, conformi a KRITIS/NIS2.
Prevenzione OWASP Top 10
CWE-78: Command Injection
Rischio: Esecuzione di comandi arbitrari tramite input manipolato.
// VIETATO - Non sicuro! Exec('cmd /c ' + UserInput); Shell('fpc ' + ProjectPath + ' ' + UserArgs); // CORRETTO - Parametrizzato Options.Shell := False; Args := TStringArray.Create; Args.Add(ProjectPath); Spawn('fpc', Args, Options);
Misure:
- Usare sempre
shell: false - Passare argomenti come array
- Validare i percorsi prima dell'uso
CWE-22: Path Traversal
Rischio: Accesso a file al di fuori della directory consentita.
// VIETATO - Non sicuro! FileName := BasePath + UserInput; ReadFile(FileName); // CORRETTO - Validazione function IsPathSafe(const ABasePath, AUserInput: string): Boolean; var ResolvedPath: string; begin // Non permettere .. 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));
Misure:
- Bloccare sequenze
.. - Normalizzare e validare i percorsi
- Il percorso base deve essere prefisso del percorso risultante
CWE-20: Input Validation
Rischio: Dati non validi causano malfunzionamenti o attacchi.
// VIETATO - Nessuna validazione procedure CreateProject(const AName: string); begin MakeDir(AName); // E se AName = '../../../etc'? end; // CORRETTO - Validazione 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: Credenziali in chiaro
Rischio: Credenziali nel codice o nei log.
// VIETATO - Credenziali nel codice const API_TOKEN = 'ghp_xxxxxxxxxxxx'; DB_PASSWORD = 'secret123'; // VIETATO - Loggare credenziali LogDebug('Token: %s', [Token]); // CORRETTO - Variabili d'ambiente Token := GetEnvironmentVariable('GITHUB_TOKEN'); if Token = '' then raise EWvdSConfigError.Create(rsTokenNotConfigured); // CORRETTO - Logging mascherato LogDebug('Token configurato: %s', [BoolToStr(Token <> '', True)]);
CWE-532: Log Injection
Rischio: Dati sensibili visibili nei log.
// VIETATO LogInfo('Login utente: %s con password: %s', [User, Password]); LogDebug('Risposta API: %s', [FullResponse]); // Puo contenere token! // CORRETTO LogInfo('Login utente: %s', [User]); // Nessuna password LogDebug('Risposta API ricevuta, lunghezza: %d', [Length(Response)]);
CWE-79: Cross-Site Scripting (XSS)
Rischio: Iniezione di codice script nelle WebView.
// VIETATO - HTML non escapato WebView.Html := '<div>' + UserInput + '</div>'; // CORRETTO - 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: Whitelist messaggi WebView
Rischio: Tipi di messaggio sconosciuti permettono attacchi ai gestori WebView.
// VIETATO - Nessun controllo tipo messaggio procedure HandleMessage(AMessage: TJSObject); var MsgType: string; begin MsgType := string(AMessage['type']); case MsgType of 'browse': HandleBrowse(AMessage); 'save': HandleSave(AMessage); end; end; // CORRETTO - Con 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']); // Controllo whitelist PRIMA if not IsAllowedMessageType(MsgType) then begin LogWarning(rsUnknownMessageType, [MsgType]); Exit; end; case MsgType of 'browse': HandleBrowse(AMessage); 'save': HandleSave(AMessage); // ... end; end;
Misure:
- Definire whitelist con tutti i tipi di messaggio consentiti
- Loggare e rifiutare tipi sconosciuti
- Aggiornare la whitelist per nuove funzionalita
Chiamate API Node.js difensive
Rischio: Le API Node.js possono avere metodi diversi a seconda del contesto.
stdout.setEncoding non e una funzione se il processo non e inizializzato correttamente.
// VIETATO - Chiamate metodo dirette senza controllo procedure SetupProcessHandlers; begin asm this.FProcess.stdout.setEncoding('utf8'); this.FProcess.stderr.setEncoding('utf8'); end; end; // CORRETTO - Controlli typeof difensivi 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;
Misure:
typeof … === 'function' prima di ogni chiamata metodo- Verificare esistenza oggetto (
if (obj && obj.property)) - Gestire gracefully API mancanti
Gestione errori
Nessun handler eccezione vuoto
// VIETATO try DoSomething; except // Ingoiare l'errore end; // CORRETTO try DoSomething; except on E: ESpecificError do begin LogError(rsSpecificError, [E.Message]); // Gestione... end; on E: Exception do begin LogError(rsUnexpectedError, [E.ClassName, E.Message]); raise; // O gestire appropriatamente end; end;
Tipi eccezione specifici
// CORRETTO - Eccezioni specifiche 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
Il debug logging richiede due condizioni:
- Compile-time:
{$IFDEF DEBUG} - Runtime: parametro
–debug
{$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}
Cosa si puo loggare
| Consentito | Vietato |
|---|---|
| Nomi file, percorsi | Token, API key |
| Nomi azioni | Password |
| ID numerici | ID sessione |
| Messaggi errore | Request/response completi |
| Chiavi configurazione | Valori configurazione (sensibili) |
Default sicuri
// Valori default sicuri const DEFAULT_TIMEOUT = 30000; // 30 secondi MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB MAX_RECURSION_DEPTH = 100; type TSpawnOptions = record Shell: Boolean; // Default: False (sicuro) Timeout: Integer; // Default: 30000 WorkingDir: string; Environment: TStringArray; end; function DefaultSpawnOptions: TSpawnOptions; begin Result.Shell := False; // IMPORTANTE: Nessuna shell! Result.Timeout := DEFAULT_TIMEOUT; Result.WorkingDir := ''; Result.Environment := nil; end;
Checklist code review
Prima del merge DEVE essere verificato:
SICUREZZA: [ ] Nessuna credenziale hardcoded [ ] Tutti gli input utente validati [ ] Nessuna shell injection possibile [ ] Nessun path traversal possibile [ ] HTML escapato nelle WebView [ ] Nessun dato sensibile nei log GESTIONE ERRORI: [ ] Nessun handler eccezione vuoto [ ] Eccezioni specifiche gestite [ ] Errori loggati (senza dati sensibili) CONFIGURAZIONE: [ ] Default sicuri usati [ ] Timeout definiti [ ] Limiti definiti (dimensione file, ricorsione)
Audit sicurezza
Verifica regolare con strumenti automatizzati:
Controlli sicurezza wvds-lint
wvds-lint security --path sources/
Viene verificato:
- Chiamate Exec/Shell
- Operazioni path senza validazione
- Stringhe hardcoded (potenziali credenziali)
- Handler eccezione vuoti
- Chiamate log con parametri sensibili
Review manuale
Per ogni nuova estensione:
- Verificare tutti gli entry point
- Tracciare tutti gli input esterni
- Verificare comunicazione WebView
Risposta agli incidenti
In caso di scoperta di una vulnerabilita:
- Non pubblicare finche la patch non e pronta
- Issue su GitHub con label „security“ (privata)
- Sviluppare e testare la patch
- Rilasciare nuova versione
- Pubblicare advisory