Sign in Join
Wiki copytemporary

Secure programming/pl: what changed

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

OldNewLine
1-<div style="font-size:1.88em;margin:0.75em 0;">
2-
31Bezpieczne programowanie
4-
5-</div>
62
73## Przedmowa
84
95Ta 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.
106
117Należ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ą.
128
139Proszę pamiętać, że ten dokument dotyczy edukacji w celu lepszego kodowania, a nie przewodnika po hakowaniu i łamaniu programów.
1410
1511Możesz być także zainteresowany zasadami [[The Power of 10/pl|The Power of 10]], aby uzyskać bardziej zwięzłą poradę.
1612
1713## Nie ufaj danym wejściowym
1814
1915Podczas 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.
2016
2117Zwykle 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:
2218
2319> **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.
2420
2521Czytanie z pliku to odczytywanie niezaufanych danych wejściowych, podobnie jak odczytywanie danych wejściowych użytkownika lub akceptowanie danych wejściowych z sieci.
2622
2723### Dlaczego nie mogę ufać danym wejściowym?
2824
2925Aby zrozumieć, dlaczego dane wejściowe są niebezpieczne, najpierw musimy zrozumieć, czym są dane wejściowe.
3026
3127Dane 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.
3228
3329Nie 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.
3430
3531Wynikiem 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.
3632
3733Każ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.
3834
3935### Jakich problemów możemy się spodziewać?
4036
4137Przy 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.
4238
4339Najczęstsze rodzaje ataków to:
4440
4541#### Przepełnienie bufora
4642
4743Gdy otrzymane dane przepełnią ilość pamięci, która została dla nich przydzielona:
4844
4945``` pascal
5046var
5147 iNums : array [0..9] of integer;
5248 ...
5349 FillChar (iNums[-1], 100, #0);
5450 ...
5551 for i := -10 to 10 do
5652 readln (iNums[i]);
5753 ...
5854```
5955
6056Na 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.
6157
6258Należy pamiętać, że chociaż kompilator może ostrzegać w prostych przypadkach, to nie zawsze będzie on reagował w bardziej rozbudowanych formach.
6359
6460Jeś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.
6561
6662#### Atak DoS
6763
6864Denial of Service to nie tylko problem sieciowy, ale może istnieć również na wiele innych sposobów:
6965
7066``` pascal
7167procedure Recurse;
7268begin
7369 while (True) do
7470 begin
7571 Recurse;
7672 end;
7773end;
7874```
7975
8076Ta 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.
8177
8278Należy pamiętać, że jest to tylko statyczny przykład, ale przeprowadziliśmy atak DoS na system uruchamiając ten kod.
8379
8480Innym znanym atakiem DoS jest brak zwolnienia zasobów systemowych, takich jak pamięć, gniazda, deskryptory plików itp.
8581
8682Na przykład:
8783
8884``` pascal
8985...
9086 begin
9187 while True do
9288 begin
9389 Getmem(OurPtr, 10);
9490 OurPtr := Something;
9591 end;
9692 end.
9793```
9894
9995Ten 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.
10096
10197#### Wstrzyknięcia
10298
10399Kiedy 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).
104100
105101Przykład wstrzyknięcia SQL:
106102
107103Dane wejściowe użytkownika: Podaj swoje imię i nazwisko: a' OR 1=1
108104
109105Wewnątrz kodu:
110106
111107``` pascal
112108...
113109Write('Proszę, wpisać swoje imię: ');
114110ReadLn(sName);
115111Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name='#32 + sName + #32);
116112...
117113```
118114
119115Przesł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.
120116
121117## Dostęp do Twoich danych i modyfikacja
122118
123119Nie 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).
124120
125121### Szyfrowanie: czy to wystarczy?
126122
127123Aby przeciwdziałać opisanemu powyżej wątkowi, programiści często stosują szyfrowanie. Szyfrowanie może służyć do zapewnienia:
128124
129125- ochrony poufności danych
130126- 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)
131127
132128zarówno dla
133129
134130- przekazywania danych
135131- przechowywania/odzyskiwania danych
136132
137133Jeśli korzystasz z tych metod, Twoje dane nie są jednak automatycznie bezpieczne.
138134
139135Istnieje wiele możliwych ataków na zaszyfrowane dane:
140136
141137- atak na niezabezpieczone algorytmy lub implementację (np. przy użyciu ataku ze znanym tekstem jawnym)
142138- 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).
143139
144140Jeś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):
145141
146142- 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
147143 - 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.
148144 - użyj cryptlib lub openssl z Synapse do wdrożenia SSL/TLS zamiast rozwijania własnego rozwiązania
149145- 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:
150146 - GPG/PGP
151147 - 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ń)
152148 - SSH (np. z uwierzytelnianiem klucza publicznego/prywatnego, w razie potrzeby wzmocniony hasłami do kluczy)
153149 - 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.
154150- 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).
155151
156152Szyfrowanie 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).
157153
158154## Mity i założenia
159155
160156Wiele 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.
161157
162158Oto kilka przykładów tego typu problemów:
163159
164160### mity
165161
166162- 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.
167163- 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ą.
168164- 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)''
169165- 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ć...
170166
171167### założenia
172168
173169- Zespół QA znajdzie i naprawi moje błędy bezpieczeństwa.
174170- Użytkownik (lub ktoś inny) nie zaatakuje mojego programu i jego danych.
175171- Mój program będzie używany tylko do pierwotnego użytku.
176172- Wszystkie wyjątki mogą pozostać nieobsłużone.
177173
178174## Analizuj, aby zrozumieć zagrożenia i bezpieczeństwo
179175
180176Generalnie 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).
181177
182178Zakres 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.
183179
184180Korzyści z tego rodzaju analiz:
185181
186182- 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
187183- 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
188184
189185## Konkretne rozwiązania
190186
191187Skoro 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.
192188
193189### Przepełnienie
194190
195191Aby naprawić przepełnienie danych, takie jak bufory i inne rodzaje danych wejściowych, musimy przede wszystkim zidentyfikować rodzaj danych, z którymi musimy pracować.
196192
197193#### Przepełnienie bufora
198194
199195Jeśli wrócimy do naszego przykładu:
200196
201197``` pascal
202198var
203199 iNums: array [0..9] of Integer;
204200 ...
205201 FillChar(iNums[-1], 100, #0);
206202 ...
207203 for i := -10 to 10 do
208204 ReadLn(iNums[i]);
209205 ...
210206```
211207
212208Widzimy tutaj zakres, który został przepełniony naszymi wartościami, nawet nie sprawdzając, czy numer indeksu jest poprawny.
213209
214210W 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.
215211
216212Zatem przykład należy zmienić na:
217213
218214``` pascal
219215var
220216 iNums: array [0..9] of Integer;
221217
222218 ...
223219 FillChar (iNums[Low(iNums)], High(iNums), #0);
224220 ...
225221
226222 for i := Low(iNums) to High(iNums) do
227223 ReadLn(iNums[i]);
228224 ...
229225```
230226
231227Ale czekaj, coś jest jeszcze nie tak!
232228
233229Readln 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ć.
234230
235231#### Przepełnienie liczby
236232
237233Podczas 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.
238234
239235Liczby 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).
240236
241237W 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.
242238
243239Więc co możemy zrobić, aby to naprawić?
244240
245241Przede 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.
246242
247243Dla pierwszej opcji możemy wykonać następujące czynności (skopiowane z dokumentacji FPC):
248244
249245``` pascal
250246Program Example74;
251247
252248{ Program do demonstracji funkcji Val. }
253249Var
254250 I, Code: Integer;
255251begin
256252 Val(ParamStr(1), I, Code);
257253 If Code <> 0 then
258254 Writeln('Błąd na pozycji ', code, ' : ', Paramstr(1)[Code])
259255 else
260256 Writeln('Wartość : ', I);
261257end.
262258```
263259
264260Tutaj 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.
265261
266262Oto mały przykład małej procedury typu *readln* dla liczb całkowitych.
267263
268264``` pascal
269265program MyReadln;
270266uses
271267 CRT;
272268
273269procedure MyIntReadLn(var Param: Integer; ParamLength: Integer);
274270var
275271 Line: string;
276272 ch: char;
277273 Error: Integer;
278274begin
279275 Line := '';
280276
281277 repeat
282278 ch := ReadKey;
283279 if (Length (Line) <> ParamLength) then
284280 begin
285281 if (ch in ['0'..'9']) then
286282 begin
287283 Line := Line + ch;
288284 write (ch);
289285 end
290286 else
291287 if (ch = '-') and (Length(Line) = 0) then
292288 begin
293289 Line := '-';
294290 write (ch);
295291 end;
296292 end;
297293
298294 if (ch = #8) and (Length(Line) <> 0) then // backspace
299295 begin
300296 Line := Copy(Line, 1, Length(Line) - 1);
301297 gotoxy(WhereX - 1, WhereY);
302298 write(' ');
303299 gotoxy(WhereX - 1, WhereY);
304300 end;
305301 until (ch = #13);
306302
307303 val(Line, Param, Error);
308304
309305 if (Error <> 0) then
310306 Param := 0;
311307
312308 writeln;
313309end;
314310
315311var
316312 Num : Integer;
317313begin
318314 Write('Number: ');
319315 MyIntReadLn(Num, 2);
320316 WriteLn('The number is: ', Num);
321317end.
322318```
323319
324320Pamię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ć.
325321
326322#### Jakie są zagrożenia bezpieczeństwa w Przepełnieniach?
327323
328324Przepeł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ć.
329325
330326### Blokada usług DoS
331327
332328Blokada usług (DoS) to jeden z najtrudniejszych rodzajów ataków, którym należy zapobiec. Powody to:
333329
334330- 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.
335331- Każdy zasób systemowy może być ofiarą DoS, taką jak otwieranie gniazd, odczytywanie plików lub przydzielanie pamięci.
336332- 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)*
337333- 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.
338334- 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)*.
339335- Prawie każdy rodzaj exploita może spowodować blokadę usług.
340336
341337Jak 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.
342338
343339W powyższym przykładzie DoS:
344340
345341``` pascal
346342procedure Recurse;
347343begin
348344 while (True) do
349345 begin
350346 Recurse;
351347 end;
352348end;
353349```
354350
355351Stworzył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.
356352
357353Każ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.
358354
359355Należ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).
360356
361357Dobrym 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.
362358
363359### Wstrzykiwanie
364360
365361Istnieje wiele sposobów na wstrzyknięcie pewnego rodzaju kodu do naszych programów. Jak widzieliśmy na powyższym przykładzie:
366362
367363Dane wejściowe użytkownika: Podaj swoje imię i nazwisko: a' LUB 1=1
368364
369365Wewnątrz kodu:
370366
371367``` pascal
372368...
373369write('Proszę wpisać swoje imię: ');
374370readln(sName);
375371Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name='#32 + sName + #32);
376372...
377373```
378374
379375Wstrzyknię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.
380376
381377Na 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.
382378
383379Istnieje wiele sposobów oczyszczenia danych. Mniej skuteczny (ale często używany) to:
384380
385381#### Nieskuteczne odkażanie
386382
387383``` pascal
388384function ValidVar (const S: AnsiString; AllowChars: TCharset): Boolean;
389385var
390386 i: Word;
391387begin
392388 i := 0;
393389 Result := True;
394390
395391 while (Result) and (i <= Length(S)) do
396392 begin
397393 Inc(i);
398394 Result := S[i] in AllowChars;
399395 end;
400396end;
401397```
402398
403399Funkcja 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.
404400
405401Innym 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):
406402
407403`$sName =~ s/[^a-z0-9_\']//gi;`
408404
409405Wyraż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)).
410406
411407Teraz, 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).
412408
413409Istnieje 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).
414410
415411Zwykle 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.
416412
417413#### Ograniczanie wprowadzania przez parametry zapytania SQL
418414
419415Obowiązkowa [ilustracja wizualna](https://xkcd.com/327/).
420416
421417Po 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:
422418
423419``` pascal
424420Query1.SQL.Add('SELECT Password FROM tblUsers WHERE Name=?');
425421Query1.Parameters.Add(sName);
426422if (Query1.Execute) then
427423...
428424```
429425
430426Ta 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.
431427
432428Jednak 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.
433429
434430### Wydajny kod
435431
436432Wspomniane 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.
437433
438434Jednak pisanie wydajnego, ale podatnego na zagrożenia kodu nie pomaga nikomu poza prawnikami od odpowiedzialności cywilnej i ekspertami kryminalistyki.
439435
440436## Poza tym dokumentem
441437
442438O 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).
443439
444440Przeczytaj więcej artykułów dotyczących problemów z bezpieczeństwem, takich jak
445441
446442Przepełnienia bufora:
447443
448444- <http://www.securiteam.com/securityreviews/5QP0L0AFFS.html>
449445- <http://www.securiteam.com/securityreviews/5OP0B006UQ.html>
450446- <http://www.securiteam.com/securityreviews/5IP0G1P8AM.html>
451447- <http://www.securiteam.com/securityreviews/6Q00L00BFI.html>
452448- <http://www.securiteam.com/securityreviews/5UP0B0A60Y.html>
453449
454450Blokada usług (DoS):
455451
456452- <http://www.securiteam.com/securityreviews/5AP0V0AA1W.html>
457453- <http://www.securiteam.com/securityreviews/6Z00T0K95O.html>
458454
459455Wstrzykiwanie SQL:
460456
461457- <http://www.securiteam.com/securityreviews/5DP0N1P76E.html>
462458- <http://www.securiteam.com/securityreviews/5UP010A6AA.html>
463459- <http://www.securiteam.com/securityreviews/5GP0E2K7FO.html>
464460
465461---
466462*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).*
467463Category: Code

Back to history