Inhaltsverzeichnis
Varnostne smernice
Obvezujoči varnostni standardi za WvdS FPC RAD Suite, skladni s KRITIS/NIS2.
Preprečevanje OWASP Top 10
CWE-78: Vstavljanje ukazov
Tveganje: Izvajanje poljubnih ukazov prek manipuliranega vnosa.
// PREPOVEDANO - Nevarno! Exec('cmd /c ' + UserInput); Shell('fpc ' + ProjectPath + ' ' + UserArgs); // PRAVILNO - Parametrizirano Options.Shell := False; Args := TStringArray.Create; Args.Add(ProjectPath); Spawn('fpc', Args, Options);
Ukrepi:
- Vedno uporabite
shell: false - Argumente predajte kot polje
- Validirajte poti pred uporabo
CWE-22: Prečkanje poti
Tveganje: Dostop do datotek izven dovoljenega imenika.
// PREPOVEDANO - Nevarno! FileName := BasePath + UserInput; ReadFile(FileName); // PRAVILNO - Validacija function IsPathSafe(const ABasePath, AUserInput: string): Boolean; var ResolvedPath: string; begin // Brez .. dovoljenih 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));
Ukrepi:
- Blokirajte sekvence
.. - Normalizirajte in validirajte poti
- Osnovna pot mora biti predpona rezultatne poti
CWE-20: Validacija vnosa
Tveganje: Neveljavni podatki povzročijo napačno delovanje ali napad.
// PREPOVEDANO - Brez validacije procedure CreateProject(const AName: string); begin MakeDir(AName); // Kaj če je AName = '../../../etc'? end; // PRAVILNO - Validacija 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: Poverilnice v čistem besedilu
Tveganje: Poverilnice v kodi ali dnevnikih.
// PREPOVEDANO - Poverilnice v kodi const API_TOKEN = 'ghp_xxxxxxxxxxxx'; DB_PASSWORD = 'secret123'; // PREPOVEDANO - Beleženje poverilnic LogDebug('Token: %s', [Token]); // PRAVILNO - Okoljske spremenljivke Token := GetEnvironmentVariable('GITHUB_TOKEN'); if Token = '' then raise EWvdSConfigError.Create(rsTokenNotConfigured); // PRAVILNO - Maskirano beleženje LogDebug('Token configured: %s', [BoolToStr(Token <> '', True)]);
CWE-532: Vstavljanje v dnevnike
Tveganje: Občutljivi podatki vidni v dnevnikih.
// PREPOVEDANO LogInfo('User login: %s with password: %s', [User, Password]); LogDebug('API response: %s', [FullResponse]); // Lahko vsebuje žetone! // PRAVILNO LogInfo('User login: %s', [User]); // Brez gesla LogDebug('API response received, length: %d', [Length(Response)]);
CWE-79: Medspletno izvajanje skript (XSS)
Tveganje: Vstavljanje skriptne kode v WebViews.
// PREPOVEDANO - Neubežan HTML WebView.Html := '<div>' + UserInput + '</div>'; // PRAVILNO - Ubežanje 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: Beli seznam sporočil WebView
Tveganje: Neznani tipi sporočil omogočajo napade na obravnavalnike WebView.
// PREPOVEDANO - Brez preverjanja tipa sporočila procedure HandleMessage(AMessage: TJSObject); var MsgType: string; begin MsgType := string(AMessage['type']); case MsgType of 'browse': HandleBrowse(AMessage); 'save': HandleSave(AMessage); end; end; // PRAVILNO - Z belim seznamom 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']); // Preverjanje belega seznama NAJPREJ if not IsAllowedMessageType(MsgType) then begin LogWarning(rsUnknownMessageType, [MsgType]); Exit; end; case MsgType of 'browse': HandleBrowse(AMessage); 'save': HandleSave(AMessage); // ... end; end;
Ukrepi:
- Definirajte beli seznam z vsemi dovoljenimi tipi sporočil
- Zabeležite in zavrnite neznane tipe
- Pri novih funkcijah posodobite beli seznam
Obrambni klici Node.js API
Tveganje: Node.js API-ji imajo lahko različne metode glede na kontekst.
stdout.setEncoding ni funkcija, če proces ni pravilno inicializiran.
// PREPOVEDANO - Neposredni klici metod brez preverjanja procedure SetupProcessHandlers; begin asm this.FProcess.stdout.setEncoding('utf8'); this.FProcess.stderr.setEncoding('utf8'); end; end; // PRAVILNO - Obrambna preverjanja typeof 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;
Ukrepi:
typeof … === 'function' pred vsakim klicem metode- Preverite obstoj objekta (
if (obj && obj.property)) - Manjkajoče API-je obravnavajte elegantno
Obravnava napak
Brez praznih obravnavalcev izjem
// PREPOVEDANO try DoSomething; except // Napaka se potlači end; // PRAVILNO try DoSomething; except on E: ESpecificError do begin LogError(rsSpecificError, [E.Message]); // Obravnava... end; on E: Exception do begin LogError(rsUnexpectedError, [E.ClassName, E.Message]); raise; // Ali ustrezno obravnavajte end; end;
Specifični tipi izjem
// PRAVILNO - Specifične izjeme 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;
Razhroščevalno beleženje
Razhroščevalno beleženje zahteva dva pogoja:
- Čas prevajanja:
{$IFDEF DEBUG} - Čas izvajanja: parameter
–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}
Kaj se sme beležiti
| Dovoljeno | Prepovedano |
|---|---|
| Imena datotek, poti | Žetoni, API ključi |
| Imena akcij | Gesla |
| Numerični ID-ji | ID-ji sej |
| Sporočila napak | Popolni zahtevki/odgovori |
| Konfiguracijski ključi | Konfiguracijske vrednosti (občutljive) |
Varne privzete vrednosti
// Varne privzete vrednosti const DEFAULT_TIMEOUT = 30000; // 30 sekund MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB MAX_RECURSION_DEPTH = 100; type TSpawnOptions = record Shell: Boolean; // Privzeto: False (varno) Timeout: Integer; // Privzeto: 30000 WorkingDir: string; Environment: TStringArray; end; function DefaultSpawnOptions: TSpawnOptions; begin Result.Shell := False; // POMEMBNO: Brez lupine! Result.Timeout := DEFAULT_TIMEOUT; Result.WorkingDir := ''; Result.Environment := nil; end;
Kontrolni seznam pregledov kode
Pred združitvijo JE TREBA preveriti:
VARNOST: [ ] Brez zakodiranih poverilnic [ ] Vsi uporabniški vnosi so validirani [ ] Vstavljanje ukazov ni mogoče [ ] Prečkanje poti ni mogoče [ ] HTML je ubežan v WebViews [ ] Brez občutljivih podatkov v dnevnikih OBRAVNAVA NAPAK: [ ] Brez praznih obravnavalcev izjem [ ] Specifične izjeme obravnavane [ ] Napake se beležijo (brez občutljivih podatkov) KONFIGURACIJA: [ ] Uporabljene varne privzete vrednosti [ ] Časovne omejitve definirane [ ] Omejitve definirane (velikost datoteke, rekurzija)
Varnostna revizija
Redna preverjanja z avtomatiziranimi orodji:
Varnostna preverjanja wvds-lint
wvds-lint security --path sources/
Preveri se:
- Klici Exec/Shell
- Operacije poti brez validacije
- Zakodirani nizi (potencialne poverilnice)
- Prazni obravnavalci izjem
- Klici beleženja z občutljivimi parametri
Ročni pregled
Pri vsaki novi razširitvi:
- Preverite vse vstopne točke
- Sledite vsem zunanjim vnosom
- Preverite komunikacijo WebView
Odziv na incidente
Ob odkritju varnostne ranljivosti:
- Ne objavite dokler popravek ni pripravljen
- Ustvarite issue na GitHub z oznako „security“ (zasebno)
- Razvijte in testirajte popravek
- Objavite novo različico
- Objavite obvestilo