// 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 := '' + UserInput + '';
// 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 := '' + EscapeHtml(UserInput) + '';
==== 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.
// 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
===== Glejte tudi =====
* [[.:code-konventionen|Kodne konvencije]]
* [[.:debugging|Razhroščevanje]]
* [[.:testing|Testiranje]]
* [[https://owasp.org/www-project-top-ten/|OWASP Top 10]]