Sign in Join
Wiki copytemporary

Secure programming/pl: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11Bezpieczne programowanie
22
33## Przedmowa
44
55Ta strona wiki jest próbą nauczenia innego podejścia do tworzenia oprogramowania. Strona wykorzystuje bardzo proste przykłady, aby pokazać, że wiele problemów można wykorzystać w celu wykonania ataku bezpieczeństwa na komputer, program lub cały system.
66
77Należy pamiętać, że dokument jest tylko początkiem nauki pisania lepszego i nieco bezpieczniejszego kodu, ale nie stanowi kompletnego przewodnika, jak to zrobić. W rzeczywistości jest to tylko krótki przegląd tego, jak musimy widzieć nasz kod i program oraz jak uniknąć wielu typowych problemów, które tam występują.
88
99Proszę pamiętać, że ten dokument dotyczy edukacji w celu lepszego kodowania, a nie przewodnika po hakowaniu i łamaniu programów.
1010
1111Możesz być także zainteresowany zasadami [[The Power of 10/pl|The Power of 10]], aby uzyskać bardziej zwięzłą poradę.
1212
1313## Nie ufaj danym wejściowym
1414
1515Podczas tworzenia programu jest prawdopodobne, że będzie on w jakiś sposób wchodził w interakcję z użytkownikiem, nawet jeśli oznacza to jedynie odczytywanie plików w systemie i prezentację danych.
1616
1717Zwykle w szkołach i na uniwersytetach, gdy zaczyna się pisać programy, osoba ta uczy się, jak otrzymywać dane wejściowe, podczas gdy nauczyciele zwykle mówią tej osobie „załóż, że dane, które otrzymujesz, są prawidłowe”. Wtedy zaczynają się problemy:
1818
1919> **Note:** Nie możemy ufać żadnym danym wejściowym, których nie możemy kontrolować, ponieważ ich zawartość jest nieznana i może wykorzystywać lukę w naszym oprogramowaniu.
2020
2121Czytanie z pliku to odczytywanie niezaufanych danych wejściowych, podobnie jak odczytywanie danych wejściowych użytkownika lub akceptowanie danych wejściowych z sieci.
2222
2323### Dlaczego nie mogę ufać danym wejściowym?
2424
2525Aby zrozumieć, dlaczego dane wejściowe są niebezpieczne, najpierw musimy zrozumieć, czym są dane wejściowe.
2626
2727Dane wejściowe mogą pochodzić z naciśnięcia klawisza, ruchu myszy lub kliknięcia przycisku myszy, lub z odczytywania i akceptowania informacji z wielu innych sposobów, takich jak strumień danych lub nawet funkcje systemowe. W rzeczywistości wszystko, co Twój program otrzymuje z zewnątrz, to są dane wejściowe.
2828
2929Nie ma znaczenia, jaki jest rodzaj danych wejściowych: na przykład użytkownik lub inny system może podać nam błędne dane, a przyczyny mogą być celowe lub pomyłka. Nie możesz kontrolować tego wejścia, a głównym powodem jest to, że nie możesz odgadnąć, jakie będą to dane wejściowe.
3030
3131Wynikiem mogą być puste (NULL) „dane”, które podaje nam użytkownik, liczba wykraczająca poza nasz oczekiwany zakres lub większa ilość znaków niż oczekiwaliśmy, a nawet próba zmiany adresu zmiennej, która akceptuje dane wejściowe od użytkownika. Po prostu nie wiemy, jakie dane wprowadzi użytkownik.
3232
3333Każde „niebezpieczne” postępowanie z danymi wejściowymi może spowodować pobranie krytycznych informacji, których użytkownik nie może zobaczyć, lub modyfikację danych, do której użytkownik nie jest upoważniony, uszkodzenie danych, a nawet uszkodzenie (zawieszenie) samego programu.
3434
3535### Jakich problemów możemy się spodziewać?
3636
3737Przy każdym typie błędu prawdopodobnie znajdziesz inny sposób ataku, ale chciałbym podać krótką listę bardzo powszechnych rodzajów ataków, zamiast opisywać wiele ich typów.
3838
3939Najczęstsze rodzaje ataków to:
4040
4141#### Przepełnienie bufora
4242
4343Gdy otrzymane dane przepełnią ilość pamięci, która została dla nich przydzielona:
4444
4545``` pascal
4646var
4747 iNums : array [0..9] of integer;
4848 ...
4949 FillChar (iNums[-1], 100, #0);
5050 ...
5151 for i := -10 to 10 do
5252 readln (iNums[i]);
5353 ...
5454```
5555
5656Na tym przykładzie widać, że dla statycznej tablicy iNums daliśmy możliwość przyjęcia tylko 10 liczb, podczas gdy potem próbujemy wprowadzić 21 liczb.
5757
5858Należy pamiętać, że chociaż kompilator może ostrzegać w prostych przypadkach, to nie zawsze będzie on reagował w bardziej rozbudowanych formach.
5959
6060Jeśli użytkownik może wprowadzić dane, które są wysyłane do bufora, może on podać pewne wartości, które można zinterpretować jako instrukcje kodu maszynowego, a te zostaną zapisane poza naszym buforem. Komputer mógłby wtedy wykonać ten kod zamiast kodu, który powinien tam być. To jest własnie przepełnienie bufora.
6161
6262#### Atak DoS
6363
6464Denial of Service to nie tylko problem sieciowy, ale może istnieć również na wiele innych sposobów:
6565
6666``` pascal
6767procedure Recurse;
6868begin
6969 while (True) do
7070 begin
7171 Recurse;
7272 end;
7373end;
7474```
7575
7676Ta procedura będzie działać, dopóki system nie wyczerpie zasobów, ponieważ przy każdej rekursji przydziela więcej pamięci stosu i spowoduje, że system przestanie odpowiadać, a nawet ulegnie awarii. Chociaż niektóre systemy – takie jak Linux – będą próbowały dać ci możliwość zaprzestania uruchamiania programu, zajmie ci to dużo czasu.
7777
7878Należy pamiętać, że jest to tylko statyczny przykład, ale przeprowadziliśmy atak DoS na system uruchamiając ten kod.
7979
8080Innym znanym atakiem DoS jest brak zwolnienia zasobów systemowych, takich jak pamięć, gniazda, deskryptory plików itp.
8181
8282Na przykład:
8383
8484``` pascal
8585...
8686 begin
8787 while True do
8888 begin
8989 Getmem(OurPtr, 10);
9090 OurPtr := Something;
9191 end;
9292 end.
9393```
9494
9595Ten przykład pokazuje alokację pamięci (Getmem jest jak malloc w C: rezerwuje pamięć do jakiegoś użycia), ale kończymy wykonywanie kodu bez zwalniania pamięci pod koniec jej używania.
9696
9797#### Wstrzyknięcia
9898
9999Kiedy użytkownik podaje nam dane wejściowe, a my pracujemy na danym wejściu bezpośrednio bez oczyszczania go, użytkownik może umieścić coś w niektórych tagach SQL lub kodzie (takim jak kod skryptu lub kod maszynowy), co sprawi, że nasz program będzie wykonywał pewne dodatkowe działania (np. usunięcie niektórych rekordów/tabeli, wysłanie użytkownikowi pewnych zastrzeżonych danych, takich jak struktura bazy danych/tabeli, użytkownik bazy danych i hasło, zawartość katalogu lub pliku, a nawet uruchomienie programu na komputerze).
100100
101101Przykład wstrzyknięcia SQL:
102102
103103Dane wejściowe użytkownika: Podaj swoje imię i nazwisko: a' OR 1=1
104104
105105Wewnątrz kodu:
106106
107107``` pascal
108108...
109109Write('Proszę, wpisać swoje imię: ');
110110ReadLn(sName);
111111Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name='#32 + sName + #32);
112112...
113113```
114114
115115Przesłanie tej instrukcji SQL jako danych wejściowych użytkownika spowoduje, że nasze zapytanie doda „ OR 1=1” do instrukcji SQL przekazanej do bazy danych: w tym przypadku zawsze skutkuje to prawdą, a użytkownik uzyskał nieautoryzowany dostęp do programu.
116116
117117## Dostęp do Twoich danych i modyfikacja
118118
119119Nie tylko Twój program będzie miał dostęp do danych, z których korzysta. Jeśli przechowujesz dane w plikach lub (zdalnych) bazach danych, atakujący może uzyskać do nich dostęp za pośrednictwem systemu operacyjnego (i/lub bazy danych i/lub warstwy sieci/protokołu bazy danych).
120120
121121### Szyfrowanie: czy to wystarczy?
122122
123123Aby przeciwdziałać opisanemu powyżej wątkowi, programiści często stosują szyfrowanie. Szyfrowanie może służyć do zapewnienia:
124124
125125- ochrony poufności danych
126126- niezaprzeczalności (to dane stworzone przez osobę, która je stworzyła) i integralności (czy dane są niezmienione) (za pomocą dodatkowych mechanizmów, takich jak podpisy cyfrowe/haszowanie)
127127
128128zarówno dla
129129
130130- przekazywania danych
131131- przechowywania/odzyskiwania danych
132132
133133Jeśli korzystasz z tych metod, Twoje dane nie są jednak automatycznie bezpieczne.
134134
135135Istnieje wiele możliwych ataków na zaszyfrowane dane:
136136
137137- atak na niezabezpieczone algorytmy lub implementację (np. przy użyciu ataku ze znanym tekstem jawnym)
138138- atak na klucze szyfrujące (np. poprzez odwrotną inżynierię Twojego programu, np. jeśli przechowuje klucze w pliku lub w sobie, lub poprzez łatanie programu, aby przechwycił klucze/hasła szyfrujące wprowadzane przez użytkownika).
139139
140140Jeśli nie wiesz dokładnie, co robisz (i nie będziesz wiedzieć, chyba że masz przeszkolenie kryptograficzne), użyj (w kolejności malejącej preferencji):
141141
142142- dobrze znane biblioteki, które są utrzymywane/łatane (np. wbudowane biblioteki FPC i biblioteki takie jak DCPCrypt lub biblioteki zewnętrzne, takie jak openssl lub cryptlib), które używają dobrze znane/powszechnie używane protokoły, i które używają dobrze znane/powszechnie używane algorytmy
143143 - Użyj np. zaufane uwierzytelnianie/SSPI dla połączeń z bazą danych SQL Server/Firebird, aby uniknąć wysyłania haseł przez sieć (Firebird 2 i niższe: w postaci zwykłego tekstu!) i polegać na bezpieczeństwie systemu operacyjnego do uwierzytelniania.
144144 - użyj cryptlib lub openssl z Synapse do wdrożenia SSL/TLS zamiast rozwijania własnego rozwiązania
145145- dobrze znane/powszechnie używane protokoły/API zamiast rozwijania własnych. Protokoły te muszą wykorzystywać dobrze poznane/szeroko stosowane algorytmy kryptograficzne/haszujące. Przykłady:
146146 - GPG/PGP
147147 - TLS (SSL) z PKI/CA (najlepiej nie ufaj tylko tym urzędom, których potrzebujesz i wykonuj uwierzytelnianie za pomocą certyfikatu klienta, jeśli wymaga tego analiza zagrożeń)
148148 - SSH (np. z uwierzytelnianiem klucza publicznego/prywatnego, w razie potrzeby wzmocniony hasłami do kluczy)
149149 - w systemie Windows użyj interfejsu API, aby uzyskać aktualnie uwierzytelnionego użytkownika. Jeśli wymuszasz odpowiednie zabezpieczenia systemu operacyjnego (długość hasła, zmiany, dostęp fizyczny itp.), nie musisz zarządzać własnym mechanizmem ogsługi nazwy użytkownika/hasła na poziomie aplikacji.
150150- dobrze poznane algorytmy kryptograficzne/haszujące (takie jak AES/Rijndael, 3DES i SHA512). Zachowaj ostrożność w zakresie akceptowanych algorytmów (np. MD5 jest niezabezpieczony do podpisywania wiadomości).
151151
152152Szyfrowanie jest częścią zestawu możliwych środków bezpieczeństwa; wysiłek/pieniądze na to wydane powinny zostać ocenione w ramach analizy bezpieczeństwa (patrz poniżej).
153153
154154## Mity i założenia
155155
156156Wiele problemów związanych z bezpieczeństwem wynika z ignorowania ważnych ostrzeżeń i informacji przekazanych przez kompilator oraz z myślenia, że ​​program nie zawiera żadnego możliwego do wykorzystania problemu.
157157
158158Oto kilka przykładów tego typu problemów:
159159
160160### mity
161161
162162- Bezpieczeństwo przez ukrycie – Gdy nikt nie wie o problemie, nikt nie może z niego skorzystać; np. użyj niejasnej nazwy kolumny do przechowywania haseł w swojej bazie danych.
163163- Bezpieczny język programowania – Istnieją języki takie jak Perl, które wielu ludzi uważa za bezpieczne przed przepełnieniem bufora i innymi lukami, choć nie jest to prawdą.
164164- Hasło haszowane jest bezpieczne — plik z haszowanym hasłem nie jest bezpieczny. Hash może odpowaidać tylko jednej wartości i nie można odzyskać oryginalnych danych. Ale „zahaszowane hasło może zostać odzyskane za pomocą ataku typu brute force lub tęczowej tablicy i dlatego wymaga użycia soli lub wielu rund haszujących”: --BigChimp 16:19, 24 July 2011 (CEST)''
165165- Nic nie może zepsuć mojego programu – przekonanie, że jesteś jedynym programistą na świecie, który pisze bezbłędny kod, jest prawdopodobnie zbyt optymistyczne. Może masz szczęście i po prostu piszesz kod, który nie działa poprawnie bez luk w zabezpieczeniach, które można wykorzystać...
166166
167167### założenia
168168
169169- Zespół QA znajdzie i naprawi moje błędy bezpieczeństwa.
170170- Użytkownik (lub ktoś inny) nie zaatakuje mojego programu i jego danych.
171171- Mój program będzie używany tylko do pierwotnego użytku.
172172- Wszystkie wyjątki mogą pozostać nieobsłużone.
173173
174174## Analizuj, aby zrozumieć zagrożenia i bezpieczeństwo
175175
176176Generalnie programista (lub jego pracodawcy/właściciele firmy) powinien przeprowadzić analizę wszystkich zagrożeń (od fizycznego dostępu/ataku po ataki logiczne i socjotechniczne) dla całego systemu (w tym infrastruktury – OS, bazy danych, sieci a także fizycznej maszyny/okablowanie/budynki/połączenia zewnętrzne), aby przeanalizować, czy nie pozostawiasz otwartej luki w zabezpieczeniach, która jest niedopuszczalna (z perspektywy ryzyka/korzyści).
177177
178178Zakres analizy powinien zależeć od wartości danych/procesów chronionych przez system. Oczywiście nie ma sensu szaleć, próbując przeanalizować fizyczne bezpieczeństwo swojego domu podczas opracowywania programu hobbystycznego, aby śledzić wyniki brydżowe.
179179
180180Korzyści z tego rodzaju analiz:
181181
182182- zagrożenia pozostające po ogłoszeniu lub ujawnieniu środków bezpieczeństwa. Często toczy się dyskusja na temat prawdopodobieństwa wystąpienia tego ryzyka lub związanego z nim wpływu, ale fakt, że istnieje jakiś potencjalny wektor ataku sprawia, że jest on przynajmniej jasny i można podejmować decyzje na podstawie tych informacji
183183- dość łatwo można zobaczyć, jakie środki bezpieczeństwa są przeprojektowane („zbyt bezpieczne”, marnotrawstwo pieniędzy) lub niedopracowane („niewystarczająco bezpieczne”). Jeśli nie możesz teraz wykorzystać tych informacji, możesz przynajmniej uczyć się z nich dla innych projektów, w tym przyszłej konserwacji/modyfikacji programu
184184
185185## Konkretne rozwiązania
186186
187187Skoro znamy już pewne problemy, które możemy napotkać podczas tworzenia programów, powinniśmy dowiedzieć się, jak je naprawić. Wszystkie problemy, które widzieliśmy powyżej, przejawiają się w dwóch typach: założenia i brak starannego programowania. A żeby nauczyć się je naprawiać, najpierw musimy nauczyć się myśleć w inny sposób niż dotychczas.
188188
189189### Przepełnienie
190190
191191Aby naprawić przepełnienie danych, takie jak bufory i inne rodzaje danych wejściowych, musimy przede wszystkim zidentyfikować rodzaj danych, z którymi musimy pracować.
192192
193193#### Przepełnienie bufora
194194
195195Jeśli wrócimy do naszego przykładu:
196196
197197``` pascal
198198var
199199 iNums: array [0..9] of Integer;
200200 ...
201201 FillChar(iNums[-1], 100, #0);
202202 ...
203203 for i := -10 to 10 do
204204 ReadLn(iNums[i]);
205205 ...
206206```
207207
208208Widzimy tutaj zakres, który został przepełniony naszymi wartościami, nawet nie sprawdzając, czy numer indeksu jest poprawny.
209209
210210W tablicach dynamicznych/otwartych w Pascalu możemy poznać limity przydzielonej pamięci. Więc wszystko, co musimy zrobić, to sprawdzić, czy rozmiar nie jest za mały lub za duży dla naszego bufora i ograniczyć akceptację do rozmiaru, jaki sobie życzymy.
211211
212212Zatem przykład należy zmienić na:
213213
214214``` pascal
215215var
216216 iNums: array [0..9] of Integer;
217217
218218 ...
219219 FillChar (iNums[Low(iNums)], High(iNums), #0);
220220 ...
221221
222222 for i := Low(iNums) to High(iNums) do
223223 ReadLn(iNums[i]);
224224 ...
225225```
226226
227227Ale czekaj, coś jest jeszcze nie tak!
228228
229229Readln przyjmie nieograniczoną ilość znaków i nikt nam nie obiecuje, że będzie to liczba całkowita lub nawet z zakresu, który możemy obsłużyć.
230230
231231#### Przepełnienie liczby
232232
233233Podczas gdy łańcuch w Pascalu jest czystą tablicą (hrmm hrmm.. niezupełnie, przynajmniej nie w FPC, ale załóżmy przez chwilę, że jest, OK?), więc *readln* spróbuje znaleźć i zobaczyć, jakie są jego limity i czy nie spróbujemy przekroczyć zakresu, który nadaliśmy temu typowi, ale liczby nie są takie same.
234234
235235Liczby mają ograniczenia, komputer/kompilator ma wiele ograniczeń dotyczących pamięci i liczb. Może dać tylko „małą” ilość pamięci na liczby (liczby zmiennoprzecinkowe i całkowite). I w większości przypadków nie potrzebujemy uzywać dużego zakresu liczb (jak zmienna logiczna, która zwykle potrzebuje tylko dwóch liczb).
236236
237237W powyższym przykładzie możemy mieć przepełnienie bufora, które spowoduje błąd sprawdzania zakresu, który da nam złą liczbę (problemy z przypomnieniem Carry Flag (flaga przeniesienia)... nie będę ich tutaj wyjaśniać), a także mamy efekt DoS, ponieważ nasz program zatrzyma się w tym momencie.
238238
239239Więc co możemy zrobić, aby to naprawić?
240240
241241Przede wszystkim możemy chcieć pracować ze zmienną łańcuchową, która będzie odpowiadała największej długości liczby +1 (dla znaku minus) lub możemy stworzyć własną procedurę/funkcję readln, która będzie specjalizować się w typie integer.
242242
243243Dla pierwszej opcji możemy wykonać następujące czynności (skopiowane z dokumentacji FPC):
244244
245245``` pascal
246246Program Example74;
247247
248248{ Program do demonstracji funkcji Val. }
249249Var
250250 I, Code: Integer;
251251begin
252252 Val(ParamStr(1), I, Code);
253253 If Code <> 0 then
254254 Writeln('Błąd na pozycji ', code, ' : ', Paramstr(1)[Code])
255255 else
256256 Writeln('Wartość : ', I);
257257end.
258258```
259259
260260Tutaj widzimy, jak przekonwertować łańcuch na liczbę całkowitą z bardzo łatwą obsługą błędów. Funkcja *StrToInt* także może załatwić sprawę, ale wtedy musimy przechwycić wyjątek w przypadku obsługi błędów.
261261
262262Oto mały przykład małej procedury typu *readln* dla liczb całkowitych.
263263
264264``` pascal
265265program MyReadln;
266266uses
267267 CRT;
268268
269269procedure MyIntReadLn(var Param: Integer; ParamLength: Integer);
270270var
271271 Line: string;
272272 ch: char;
273273 Error: Integer;
274274begin
275275 Line := '';
276276
277277 repeat
278278 ch := ReadKey;
279279 if (Length (Line) <> ParamLength) then
280280 begin
281281 if (ch in ['0'..'9']) then
282282 begin
283283 Line := Line + ch;
284284 write (ch);
285285 end
286286 else
287287 if (ch = '-') and (Length(Line) = 0) then
288288 begin
289289 Line := '-';
290290 write (ch);
291291 end;
292292 end;
293293
294294 if (ch = #8) and (Length(Line) <> 0) then // backspace
295295 begin
296296 Line := Copy(Line, 1, Length(Line) - 1);
297297 gotoxy(WhereX - 1, WhereY);
298298 write(' ');
299299 gotoxy(WhereX - 1, WhereY);
300300 end;
301301 until (ch = #13);
302302
303303 val(Line, Param, Error);
304304
305305 if (Error <> 0) then
306306 Param := 0;
307307
308308 writeln;
309309end;
310310
311311var
312312 Num : Integer;
313313begin
314314 Write('Number: ');
315315 MyIntReadLn(Num, 2);
316316 WriteLn('The number is: ', Num);
317317end.
318318```
319319
320320Pamiętaj, że możesz uczynić go jeszcze lepszym i bardziej wydajnym, jeśli chcesz. To tylko bardzo mały przykład pokazujący, jak to zrobić.
321321
322322#### Jakie są zagrożenia bezpieczeństwa w Przepełnieniach?
323323
324324Przepełnienie pamięci może pozwolić na wykonanie dowolnego kodu procesora, a użytkownicy mogą uruchamiać dowolny typ kodu i nic nie może ich powstrzymać.
325325
326326### Blokada usług DoS
327327
328328Blokada usług (DoS) to jeden z najtrudniejszych rodzajów ataków, którym należy zapobiec. Powody to:
329329
330330- Blokada usług może być wykonana nawet bez błędów, które można wykorzystać, na przykład przy użyciu programu „ping” na wielu komputerach w celu DoS komputera podłączonego do Internetu.
331331- Każdy zasób systemowy może być ofiarą DoS, taką jak otwieranie gniazd, odczytywanie plików lub przydzielanie pamięci.
332332- Usunięcie plików takich jak moduł jądra może spowodować duży problem. *Nie rozumiem tego wiersza --BigChimp 19:32, 24 lipca 2011 (CEST)*
333333- Brak konfiguracji lub niewłaściwa konfiguracja może również spowodować blokadę usług, jeśli pozwala to na niewłaściwe wykorzystanie wrażliwych zasobów.
334334- Za dużo uprawnień lub ich brak *Nie rozumiem. Niewystarczająca liczba uprawnień może stanowić problem. Ale za dużo? To oczywiście zagrożenie bezpieczeństwa, ale nie problem DoS. --[[Użytkownik:BigChimp|BigChimp]] 19:32, 24 lipca 2011 r. (CEST)*.
335335- Prawie każdy rodzaj exploita może spowodować blokadę usług.
336336
337337Jak widać, blokada usług może być prawie wszystkim, co może uniemożliwić systemowi działanie tak, jak powinien, z powodu wykorzystania błędów lub błędnego kodu lub po prostu wadliwego programu, który przechwytuje zasoby systemowe.
338338
339339W powyższym przykładzie DoS:
340340
341341``` pascal
342342procedure Recurse;
343343begin
344344 while (True) do
345345 begin
346346 Recurse;
347347 end;
348348end;
349349```
350350
351351Stworzyłem również przepełnienie stosu (inny rodzaj przepełnienia bufora), który spowodował, że komputer potrzebował więcej zasobów pamięci, aby kontynuować wykonywanie kodu.
352352
353353Każdy zasób systemowy, który jest dostępny dla programu, może zostać nadużyty, nie zwracając go z powrotem do systemu, gdy program „już go nie potrzebuje”. Przetrzymywanie zasobów systemowych, takich jak pamięć lub gniazda, blokuje innym programom możliwość wykonywania niektórych z ich działań. W ten sposób większość programów zatrzyma wykonywanie i zgłosi błąd, a niektóre będą się zawieszać i nadal szukać zasobów systemowych.
354354
355355Należy pamiętać, że niektóre nadużycia zasobów systemowych wynikają z błędu w programowaniu, jak oczekiwanie na bufor 150k, podczas gdy rzeczywisty bufor ma tylko 2 bajty, a gdy program nadal szuka bufora 150k, nowe żądanie tworzy kolejny bufor 150k itd., dopóki system nie będzie już w stanie odpowiedzieć na żadne z żądań (jest to znany typ ataku).
356356
357357Dobrym obejściem tego błędu jest ograniczenie liczby niepełnych buforów, które można przydzielić jednocześnie. Jeśli bufor nie jest pełny po upływie limitu czasu, powinien być wolny. Jednak to rozwiązanie spowoduje również odmowę usługi, ponieważ komunikacja i tak zostanie zatrzymana w pewnym momencie lub powolne połączenie może spowodować utratę danych.
358358
359359### Wstrzykiwanie
360360
361361Istnieje wiele sposobów na wstrzyknięcie pewnego rodzaju kodu do naszych programów. Jak widzieliśmy na powyższym przykładzie:
362362
363363Dane wejściowe użytkownika: Podaj swoje imię i nazwisko: a' LUB 1=1
364364
365365Wewnątrz kodu:
366366
367367``` pascal
368368...
369369write('Proszę wpisać swoje imię: ');
370370readln(sName);
371371Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name='#32 + sName + #32);
372372...
373373```
374374
375375Wstrzyknięcie nastąpiło, ponieważ nie filtrujemy (nie odkażamy) naszego kodu: oznacza to sprawdzenie, czy otrzymujemy dokładnie taki rodzaj wejścia, jakiego szukamy, i nic więcej.
376376
377377Na przykład możemy sprawdzić, czy *sName* zawiera spacje. Jeśli tak, nie kontynuuj sprawdzania reszty zmiennej. To pomaga, jeśli nazwa użytkownika może składać się tylko z jednego słowa składającego się z liter, może znaku apostrofu (`'`) i może nawet podkreślenia (`_`) i na tym koniec. Jeśli wprowadzimy liczbę, powinno to być nielegalne (chyba że chcemy używać „języka hackerskiego” (fonetycznie) lub zezwolić na używanie cyfr.
378378
379379Istnieje wiele sposobów oczyszczenia danych. Mniej skuteczny (ale często używany) to:
380380
381381#### Nieskuteczne odkażanie
382382
383383``` pascal
384384function ValidVar (const S: AnsiString; AllowChars: TCharset): Boolean;
385385var
386386 i: Word;
387387begin
388388 i := 0;
389389 Result := True;
390390
391391 while (Result) and (i <= Length(S)) do
392392 begin
393393 Inc(i);
394394 Result := S[i] in AllowChars;
395395 end;
396396end;
397397```
398398
399399Funkcja zwraca wartość true, jeśli mamy poprawną strukturę treści podaną przez parametr *AllowChars* w zmiennej *S*. Należy pamiętać, że ta funkcja jest tylko próbką koncepcji i może wymagać więcej pracy, aby mogła zostać w pełni wykorzystana.
400400
401401Innym sposobem na zrobienie tego samego jest użycie wyrażeń regularnych w następujący sposób (jest to próbka koncepcji wzięta z języka Perl. FPC nie ma w pełni obsługiwanego silnika wyrażeń regularnych, który umożliwia modyfikację ciągów):
402402
403-`$sName =~ s/[^a-z0-9_\']//gi;`
403+```text
404+$sName =~ s/[^a-z0-9_\']//gi;
405+```
404406
405407Wyrażenie regularne usuwa wszelkie niepoprawne znaki z łańcucha i zwraca do nas usunięty łańcuch. Zwróć uwagę, że o ile wiem, to wyrażenie regularne będzie działać również w silnikach ereg, ale z minimalnymi korektami (flaga *g* instruuje Perla, aby zastąpił wszystkie znalezione pasujące wzorce, a *i* oznacza *case insensitivity* (niewrażliwość na wielkość liter)).
406408
407409Teraz, gdy wiemy, że nasze dane wejściowe są prawidłowe, musimy zobaczyć, do czego służy zawartość zmiennej. Jeśli zawartość zmiennej trafia do bazy danych, skryptu cgi lub czegokolwiek innego, co ma własną składnię, musimy zmienić treść zgodnie z niedozwolonymi lub kontrolnymi znakami odpowiedniego języka (np. SQL dla baz danych).
408410
409411Istnieje wiele sposobów na uniknięcie tego typu zawartości. Załóżmy na razie, że ta treść trafia do zapytania bazy danych. Teraz przede wszystkim musimy upewnić się, że nasz dodatkowy znak ucieczki, nie zwiększy rozmiaru naszych danych, powyżej limitu długości pól w naszej bazie danych. Ponieważ, jeśli tak się stanie, możemy doprowadzić do wstrzykiwania, do utraty danych, blokady usług lub przepełnienia bufora (poważna baza danych zwykle obcina dane, ale czasami nie we właściwym miejscu).
410412
411413Zwykle jedyną ucieczkę, jaką musimy zrobić, aby użyć ciągu w bazie danych, jest ucieczka jedynie znaku apostrofu (') (chociaż niektóre bazy danych mogą mieć problemy z większą liczbą znaków niż apostrof). Tak więc wszystko, co powinniśmy zrobić, to reprezentować apostrofy w sposób, który nie wpłynie na silnik bazy danych, jak znak odwrotnego ukośnika (\\') lub podwajać każdy apostrof do dwóch apostrofów (''), a może nawet użyć innego znaku, który zastąpi apostrofy w zapytaniu i zamienić je ponownie, gdy musimy pokazać je użytkownikowi.
412414
413415#### Ograniczanie wprowadzania przez parametry zapytania SQL
414416
415417Obowiązkowa [ilustracja wizualna](https://xkcd.com/327/).
416418
417419Po upewnieniu się, że przestrzegamy ograniczeń, możemy kontynuować nasze próby. Aby uciec od kodu, możemy użyć kilku podejść. Mniej przyjazny dla debugowania sposób, ale pewny sposób na poprawną ucieczkę, to użycie techniki parametrów:
418420
419421``` pascal
420422Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name=?');
421423Query1.Parameters.Add(sName);
422424if (Query1.Execute) then
423425...
424426```
425427
426428Ta technika pozwala silnikowi bazy danych uciec przed parametrem w taki sposób, abyśmy mogli korzystać z zawartości bez żadnych problemów związanych z niedozwolonymi znakami. Ponadto niektóre bazy danych mają zwiększoną wydajność w przypadku powtarzających się wywołań tego kodu, ponieważ mogą one przygotować do tego instrukcję wewnętrzną z parametrami. Wadą jest to, że nigdy nie możemy debugować wyniku zapytania. Oznacza to, że nie możemy zobaczyć, w jaki sposób zawartość *sName* osadzona jest w instrukcji SQL i nigdy nie możemy z tego powodu sprawdzić, czy nasze zapytanie było poprawne.
427429
428430Jednak po przetestowaniu zapytania bez parametrów dodawanie parametrów jest dość łatwe, więc w praktyce ten problem nie jest tak duży, jak się wydaje.
429431
430432### Wydajny kod
431433
432434Wspomniane powyżej środki bezpieczeństwa komplikują kod, jeśli użyłeś najbardziej wydajnego kodu, aby uzyskać potrzebną funkcjonalność. Na szczęście czasami możesz przekazać komplikacje kodu do frameworka/biblioteki, która się tym zajmuje. Na przykład: w przykładzie SQL DoS możesz pozwolić silnikowi bazy danych zająć się ucieknięciem danych za pomocą sparametryzowanych zapytań, przy niewielkim dodatkowym koszcie konieczności użycia parametrów w kodzie.
433435
434436Jednak pisanie wydajnego, ale podatnego na zagrożenia kodu nie pomaga nikomu poza prawnikami od odpowiedzialności cywilnej i ekspertami kryminalistyki.
435437
436438## Poza tym dokumentem
437439
438440O ile w tym dokumencie podałem kilka krótkich (tak, wiem, że to niedopowiedzenie 😉) przykładów i informacji o tym, jak stworzyć lepszy kod, o tyle jest wiele kwestii, których nie poruszyłem w tym dokumencie. Część z nich to uprawnienia użytkownika do uruchamiania programów, rootkity systemu i inne problemy, które nasz kod musi wziąć pod uwagę (zmienna środowiskowa to tylko jeden przykład).
439441
440442Przeczytaj więcej artykułów dotyczących problemów z bezpieczeństwem, takich jak
441443
442444Przepełnienia bufora:
443445
444446- <http://www.securiteam.com/securityreviews/5QP0L0AFFS.html>
445447- <http://www.securiteam.com/securityreviews/5OP0B006UQ.html>
446448- <http://www.securiteam.com/securityreviews/5IP0G1P8AM.html>
447449- <http://www.securiteam.com/securityreviews/6Q00L00BFI.html>
448450- <http://www.securiteam.com/securityreviews/5UP0B0A60Y.html>
449451
450452Blokada usług (DoS):
451453
452454- <http://www.securiteam.com/securityreviews/5AP0V0AA1W.html>
453455- <http://www.securiteam.com/securityreviews/6Z00T0K95O.html>
454456
455457Wstrzykiwanie SQL:
456458
457459- <http://www.securiteam.com/securityreviews/5DP0N1P76E.html>
458460- <http://www.securiteam.com/securityreviews/5UP010A6AA.html>
459461- <http://www.securiteam.com/securityreviews/5GP0E2K7FO.html>
460462
461463---
462464*From the Free Pascal wiki, page "Secure programming/pl", as it stood on 2022-10-02 after a change by Slawek, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
463465Category: Code

Back to history