Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Bezpieczeństwo serwisu - zbiór informacji (pytanie)
Forum PHP.pl > Forum > Bazy danych > MySQL
SeaDog
Witam wszystkich forumowiczów bardzo gorąco, ponieważ jest to mój pierwszy post na tym forum.
Następnym razem będę witał trochę mniej entuzjastycznie biggrin.gif

Co prawda, podobne pytanie zadałem na innym forum, ale już przez ponad miesiąc nie otrzymałem żadnej odpowiedzi,
więc postanowiłem w zmienionej wersji zadać je wam, drodzy PHP-owcy.

Jak większość ludzi zadających tutaj pytania ja również piszę swój serwis i mój problem polega na tym, że obawiam
się o jego podatność na ataki. Bardzo dużo czytałem na temat SQL Injection i innych typach ataków, noo... już ze dwa tygodnie.

Żeby zebrać jak najwięcej informacji w jednym miejscu i nie tworzyć podobnych tematów, pozwolę sobie zadać pytania w punktach.

Wiem, że należy filtrować wszystkie dane pochodzące od użytkownika (to jest priorytet).

1) Jeśli mam formularz oparty o zmienne sesyjne (w tym miejscu muszę zaznaczyć, że walidacja następuje na każdym polu
input i textarea), to czy dane pochodzące z każdego pola przed dostaniem się do zmiennej sesyjnej muszą zostać od razu
przefiltrowane, czy mogą zostać przefiltrowane przed samym dodaniem do bazy. Konkretnie chodzi mi o znaki: " ' \
załóżmy, że ktoś wpisał: McDonald's (czy apostrof w czystej postaci może być przechowywany w zmiennej przez trzy
etapowy formularz, czy trzeba go od razu ucinać znakiem ucieczki?

2) Wyłączyłem na serwerze opcję magic_quotes_gpc. Od tej chwili przy użyciu funkcji mysql_real_escape_string
do bazy danych żywcem dodają mi się znaki: " ' \ bez żadnej ucieczki.
Czy to jest prawidłowe działanie skryptu?
Na przykład, gdy dodam wpis: <?php echo date("Y-m-d"); ?> to ten tekst w czystej postaci dodawany jest do bazy.

3) Napisałem skrypt, który przy pomocy trzech zmiennych GET pobiera z bazy odpowiednie rekordy, pola.
Na początku tego skryptu następuje walidacja tych zmiennych:
if(preg_match('/^[\węĘóÓąĄśŚłŁźŹżŻńŃ\- ]$/D', $_GET['zmienna']) &&.... walidacja dwóch kolejnych zmiennych) {

// jeśli TRUE
// tutaj następuje pobranie danych z bazy

mysql_query("SELECT....

} else {

include("......."); }

Czy przy takim zabezpieczeniu (preg_match), j/w konieczne jest używanie funkcji mysql_real_escape_string
przed pobraniem danych? Przed mysql_query powinno być: $zmienna = mysql_real_escape_string($_GET['zmienna']);
czy można już to sobie darować?

4) Gdzieś wyczytałem, że trzeba filtrować tablice $_SERVER - konkretnie jakie zmienne?

5) Jeśli taki wpis: <?php echo date("Y-m-d"); ?> w bazie jest poprawny, bez żadnych backslash-y to przed wyświetleniem
go na stronie wystarczy sama funkcja htmlspecialchars($zmienna, ENT_QUOTES); czy polecacie coś jeszcze?

6) Ostatnie pytanie, na które znam odpowiedź ale jakoś mnie to trapi... czy trzeba filtrować zmienne pól:
checkbox, radio i select przekazywane metodą POST?


Wybaczcie, że tak obszernie to napisałem ale chciałem jak najjaśniej przekazać wam swoje myśli, problemy, zagwozdki...
Proszę o dokładne opisanie/wytłumaczenie każdego punktu, to rozwieje moje wątpliwości.

Dziękuję i pozdrawiam.
darko
1. Najlepiej od razu przefiltrować
2. - 3. ---> PDO
4. ?
5. ---> PDO
6. można próbować kombinować z tym za pomocą livehttpheaders, więc najlepiej też sprawdzać wartości
Wybaczone.
Przeczytaj uważnie ten temat.
Noidea
A ja odpowiem nieco inaczej:

1. Powinieneś filtrować te dane tuż przed dodaniem do bazy. Nigdy nie wiadomo co jeszcze będziesz chciał z tymi danymi zrobić. Na przykład jeśli będziesz chciał wyświetlić taką zmienną na stronie, to dostaniesz Mc\'Donalds. strlen z takiej zmiennej zwróci 11 zamiast 10 itp.

2. tak

3. Nie jest konieczne, ale nie zaszkodzi. Spadek wydajnościowy jest praktycznie żaden, a przynajmniej, gdy postanowisz zmienić to wyrażenie, nie musisz się zastanawiać czy teraz trzeba już dodać mysql_real_escape_string, czy nie.

4. tak, wszystkie które pochodzą od użytkownika (z nagłówków http), czyli HTTP_USER_AGENT, HTTP_REFERER, HTTP_ACCEPT_LANGUGE, ... Prawdopodobnie potencjalnie niebezpieczne są wyłącznie te zaczynające się na HTTP_, ale tego nie jestem w 100% pewien

5. wystarczy

6. Trzeba. Nic nie stoi na przeszkodzie, żeby sobie w formularzu zmienić <select name="xxx"> na <input type="text" name="xxx"> i wysłać niebezpieczne dane, których nie uwzględniłeś w tagach <option>


No i również polecam PDO
SeaDog
Panowie, bardzo dziękuję za szybkie odpowiedzi.

Odnośnie waszych postów:

darko - punkt 1
napisałeś, żebym od razu filtrował zmienne. Jakiej funkcji powinienem użyć? mysql_real_escape_string jest przystosowany do bazy mysql.
Użyć addslashes? i na koniec mysql_real_escape_string? Formularz jest 3 etapowy, do tego dochodzą błędy użytkowników.
Formularz ma około 20 pól. 16 trzeba wypełnić obowiązkowo. Załóżmy, że pierwsze pole zostało poprawnie wypełnione.
Pierwsze pole ma wartość np. Mc Donald's.
Każde kolejne za drugim razem, bo użytkownik podał nieprawidłowe dane. W zmiennej od pierwszego pola narobi się sporo backslashy.
Backslashe będą poprzedzały backslache, efekt niepożądany.
Każde pole jest sprawdzane przez preg_match, jeśli jest błąd wysyłana jest zmienna sesyjna error itd.
Noidea napisał, żeby filtrować przed dodaniem do bazy. Chodzi mi konkretnie o to, czy zmienna sesyjna może przechowywać
cudzysłów podwójny, pojedynczy i slash, czy jest to bezpieczne.
Wygodniejszym by było, gdyby filtracja wszystkich pól następowała na samym końcu, tuż przed dodaniem wpisów do bazy.

Noidea - punkt 2
Zatem czysty tekst np: <?php echo date("Y-m-d"); ?> albo inny kod z cudzysłowem podwójnym lub pojedynczym nie wpływa
na zachowanie bazy danych? nie ma znaczenia, czy tagi html będą zamieniane przy użyciu funkcji htmlspecialchars czy nie?
Mam rozumieć, że chwila strachu jest tylko i wyłącznie w momencie wysyłania treści do bazy? tak?
Baza przyjmie każdy znak?

Noidea - punkt 5
Dodałem taki wpis do bazy. Czysty tekst, bez znaków ucieczki. Następnie próbowałem taki wpis wyświetlić na stronie.
Wyświetlał się sam tekst, data się nie wyświetliła, więc szczerze mówiąc nie wiem o co tu chodzi.
Mimo wszystko używam funkcji htmlspecialchars($zmienna, ENT_QUOTES); i w tym wypadku czuję się bezpieczniejszy smile.gif

Cytat
wybaczone

dzięki wielkie biggrin.gif

W wolnej chwili zapoznam się z tematem, który mi podał darko

Powiedzcie mi tak ogólnikowo, czym różni się to PDO od zwykłych zapytań mysql.
W sumie nie uśmiecha mi się teraz poprawiać całej strony. Zapytań co prawda, nie jest aż tak dużo, ale dodatkowa robota,
no i oczywiście poznawanie PDO. Korzystam z mysql, nie mam innej bazy na serwerze.
darko
Poprawne użycie PDO pozwoli Ci zapomnieć o konieczności filtrowania danych, preparowanie i bindowanie parametrów do zapytań jest wystarczającym zabezpieczeniem przed atakiem sql injection. Dodatkowo PDO domyślnie eskejpuje wszystkie potencjalnie groźne znaki, dlatego polecam Ci od razu stosowanie PDO, gdyż po prostu ułatwisz sobie pracę, nie wspominając o tym, że PDO jest standardem w kwestii obsługi bazy z poziomu kodu. Odnośnie Twojego pytania: jeśli nie korzystałbym z PDO, to filtrowałbym wszystkie dane od razu, nie tuż przed dodaniem do bazy, ale od razu jeszcze przed zapisaniem w sesji i dopiero tak przefiltrowane dane zapisywałbym w sesji. Odnośnie pytania o zalety PDO: http://www.google.pl/search?q=zalety+pdo&a...lient=firefox-a (przekonywać na siłę nikogo nie będę).
Noidea
Jeszcze nie do końca łapiesz o co chodzi w SQL Injection. Znaki takie jak: " ' \ nie są dla bazy danych niebezpieczne. Bez problemu mogą być przechowywane polach typu tekstowego. Problemem jest ingerencja w kod instrukcji SQL, która w 99% przypadków powoduje błąd składni. Przykładowo po wstawieniu Mc Donald's do zapytania:
  1. $zmienna = "Mc Donald's";
  2. $query = "SELECT * FROM tabela WHERE kolumna = '$zmienna' AND typ = 1";
  3.  
  4. echo $query;
  1. SELECT * FROM tabela WHERE kolumna = 'Mc Donald's' AND typ = 1

dostajesz zapytanie z trzema apostrofami. Zapytanie takie jest niepoprawne składniowo, bo interpreter nie wie gdzie tekst się powinien kończyć. Użytkownikowi nie wyświetlą się na stronie informacje o fastfoodzie, tylko pewnie jakiś błąd i będzie poirytowany.
Pozostały 1% przypadków to użytkownicy na tyle sprytni, że potrafią wstawić do zapytania takie dane z apostrofem, że zapytanie nadal będzie poprawne składniowo, np.: Mc Donald' OR 1 = 1 --
  1. SELECT * FROM tabela WHERE kolumna = 'Mc Donald' OR 1 = 1 -- ' AND typ = 1


Wstawienie znaku ucieczki (\) przed apostrofem jest tylko informacją dla interpretera zapytań SQL mówiącą "nie traktuj tego apostrofu jako końca tekstu". Zapytania wyglądają wtedy tak:
  1. SELECT * FROM tabela WHERE kolumna = 'Mc Donald\'s' AND typ = 1
  2. SELECT * FROM tabela WHERE kolumna = 'Mc Donald\' OR 1 = 1 -- ' AND typ = 1

Po zinterpretowaniu zapytania znak ucieczki spełnił już swoje zadanie i nie ma potrzeby wstawiania go do bazy danych. Dlatego w bazie widzisz "czyste" dane, takie jak te o które się martwiłeś:
Mc Donald's
<?php echo "ala ma kota"; ?>


Cytat
Noidea napisał, żeby filtrować przed dodaniem do bazy. Chodzi mi konkretnie o to, czy zmienna sesyjna może przechowywać
cudzysłów podwójny, pojedynczy i slash, czy jest to bezpieczne.

tak, zmienna sesyjna, jak każda inna zmienna może przechowywać cudzysłów, apostrof i slash:
  1. <?php
  2.  
  3. $var = "Mc Donald's";
  4. $_SESSION['var'] = "Mc Donald's";
  5.  
  6. $var = '<?php echo "ala ma kota"; ?>';
  7. $_SESSION['var'] = '<?php echo "ala ma kota"; ?>';
  8.  
  9. ?>


Cytat
Zatem czysty tekst np: <?php echo date("Y-m-d"); ?> albo inny kod z cudzysłowem podwójnym lub pojedynczym nie wpływa
na zachowanie bazy danych?

nie wpływa

Cytat
nie ma znaczenia, czy tagi html będą zamieniane przy użyciu funkcji htmlspecialchars czy nie?

nie ma. Do zabezpieczania przed SQL Injection służą metody takie jak mysql_real_escape_string albo PDO z prepared statements. htmlspecialchars zabezpiecza przed innym rodzajem ataków i powinien być stosowany przed wyświetlaniem danych.

Cytat
Baza przyjmie każdy znak?

tak

Cytat
Dodałem taki wpis do bazy. Czysty tekst, bez znaków ucieczki. Następnie próbowałem taki wpis wyświetlić na stronie.
Wyświetlał się sam tekst, data się nie wyświetliła, więc szczerze mówiąc nie wiem o co tu chodzi.

chodzi o to samo co w tym przypadku:
http://ideone.com/NWYfL
Kod PHP nie jest interpretowany, tylko traktowany jak zwykły tekst. Dlatego nie wyświetla się aktualna data.
SeaDog
darko, Noidea, bardzo dziękuję za pomoc. Rozwialiście wszelkie moje wątpliwości, zaczynam filtrować dane od samego początku,
jeszcze przed dodaniem do sesji. Jeśli załapię PDO, przeskoczę na to rozszerzenie, jak nie to zostaję przy wyrażeniach regularnych
i standardowych zapytaniach mysql.
PLUSY dla was i na koniec mam jeszcze ostatnie pytanie, odnośnie czysto praktycznego wykonania.

Jest sobie formularz:
Użytkownik wprowadza wartość do pola "a", we wprowadzonym ciągu znajduje się cudzysłów. Teraz wykonuję filtrację za pomocą addslashes.
Przed ten cudzysłów wstawiany jest backslash i $_SESSION['a'] przyjmuje wartość: abcd\"efgh
Gdy skrypt się wykona następuje powrót do formularza i teraz input "a" przy pomocy stripslashes wyświetla abcd"efgh.
Input wyświetla $_SESSION['a'].

Ok, tu wszystko jest jasne. Użytkownik wypełnia pozostałe pola, następuje drugi etap wprowadzania danych itd.

Teraz moment dodania wpisów do bazy.

Pytanie:
jak bezpiecznie, bez backslasha dodać wartość $_SESSION['a'] do bazy?

Czy poprawnym będzie: stripslashes(mysql_real_escape_string($_SESSION['a'])); questionmark.gif

jak w tym przypadku użyję samego mysql_real_escape_string to do bazy zostanie dodany również backslash?
Noidea
Cytat
Czy poprawnym będzie: stripslashes(mysql_real_escape_string($_SESSION['a']));


Będzie działać i będzie bezpieczne. Ale "poprawnym" bym tego nie nazwał.
Skoro bezpieczeństwem skryptów zajmujesz się od niedawna i nadal boisz się cudzysłowu i apostrofu (w sensie chcesz te znaki escape'ować najwcześniej jak tylko się da) to nie ma problemu żebyś tak pisał.
Z biegiem czasu nabierzesz nieco ogłady i zacznie cie wnerwiać ciągłe pisanie stripslashes (ta funkcja przydaje się chyba tylko wtedy, gdy nie możemy wyłączyć magic_quotes). Wtedy wypracujesz sobie mniej pracochłonne podejście.

Cytat
jak w tym przypadku użyję samego mysql_real_escape_string to do bazy zostanie dodany również backslash?

tak, ale na przyszłość takie rzeczy sprawdzaj już sam wink.gif
SeaDog
Cytat
tak, ale na przyszłość takie rzeczy sprawdzaj już sam wink.gif

he he smile.gif jak pisałem poprzedniego posta to miałem już wyłączony serwer, więc podczepiłem też to pytanie smile.gif

Jeszcze raz dziękuję za pomoc.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.