Jakie są najlepsze funkcje odkażania wejścia PHP?

161

Próbuję wymyślić funkcję, przez którą mogę przejść wszystkie moje ciągi, aby ją odkazić. Aby ciąg, który z niego wyszedł, był bezpieczny do wstawienia do bazy danych. Ale jest tak wiele funkcji filtrujących , że nie jestem pewien, których powinienem użyć / potrzebować.

Proszę, pomóż mi wypełnić puste miejsca:

function filterThis($string) {
    $string = mysql_real_escape_string($string);
    $string = htmlentities($string);
    etc...
    return $string;
}
Lauren
źródło
4
do wstawienia wystarczy zdezynfekować przed wstrzyknięciem SQL za pomocą mysql_real_escape_string. Kiedy używasz WYBRANYCH danych (w wyjściu html lub w formule / funkcji php), powinieneś zastosować htmlentities
davidosomething
Zobacz stackoverflow.com/questions/60174/…, aby uzyskać odpowiedź dotyczącą czyszczenia w celu wstawienia do bazy danych (podaje przykład PDO, o którym inni wspominali poniżej).
Pat

Odpowiedzi:

433

Zatrzymać!

Popełniasz błąd. Och, nie, wybrałeś odpowiednie funkcje PHP, aby Twoje dane były nieco bezpieczniejsze. W porządku. Twój błąd dotyczy kolejności operacji oraz sposobu i miejsca korzystania z tych funkcji.

Ważne jest, aby zrozumieć różnicę między oczyszczaniem i sprawdzaniem poprawności danych użytkownika, ucieczką danych do przechowywania i ucieczką do prezentacji.

Odkażanie i weryfikacja danych użytkownika

Kiedy użytkownicy przesyłają dane, musisz się upewnić, że dostarczyli coś, czego oczekujesz.

Odkażanie i filtrowanie

Na przykład, jeśli oczekujesz liczby, upewnij się, że przesłane dane to liczba . Możesz także rzutować dane użytkownika na inne typy. Wszystko, co zostało przesłane, jest początkowo traktowane jak ciąg, więc wymuszenie, aby znane dane numeryczne były liczbami całkowitymi lub zmiennoprzecinkowymi, czyni czyszczenie szybkim i bezbolesnym.

A co z dowolnymi polami tekstowymi i obszarami tekstowymi? Musisz się upewnić, że w tych dziedzinach nie ma nic nieoczekiwanego. Przede wszystkim musisz się upewnić, że pola, które nie powinny zawierać treści HTML, w rzeczywistości nie zawierają HTML. Istnieją dwa sposoby rozwiązania tego problemu.

Najpierw możesz spróbować eskapować dane wejściowe HTML za pomocą htmlspecialchars. Nie powinieneś używać htmlentitiesdo neutralizowania HTML, ponieważ wykona on również kodowanie znaków akcentowanych i innych znaków, które według niego również powinny być zakodowane.

Po drugie, możesz spróbować usunąć dowolny możliwy kod HTML. strip_tagsjest szybki i łatwy, ale też niechlujny. HTML Purifier wykonuje znacznie dokładniejszą pracę, zarówno usuwając cały HTML, jak i umożliwiając selektywną białą listę tagów i atrybutów.

Nowoczesne wersje PHP są dostarczane z rozszerzeniem filtra , które zapewnia kompleksowy sposób oczyszczania danych wprowadzanych przez użytkownika.

Uprawomocnienie

Upewnienie się, że przesłane dane są wolne od nieoczekiwanych treści, to tylko połowa pracy. Musisz także spróbować upewnić się, że przesłane dane zawierają wartości, z którymi możesz faktycznie pracować.

Jeśli spodziewasz się liczby od 1 do 10, musisz sprawdzić tę wartość. Jeśli używasz jednego z tych nowych, wymyślnych liczbowych danych wejściowych ery HTML5 z pokrętłem i krokami, upewnij się, że przesłane dane są zgodne z krokiem.

Jeśli te dane pochodzą z tego, co powinno być menu rozwijanym, upewnij się, że przesłana wartość jest taka, która pojawiła się w menu.

A co z danymi wejściowymi, które spełniają inne potrzeby? Na przykład dane wejściowe powinny być sprawdzane za pomocą strtotimelub klasy DateTime . Podana data powinna mieścić się w oczekiwanych zakresach. A co z adresami e-mail? Wspomniane wcześniej rozszerzenie filtru może sprawdzić, czy adres jest poprawnie sformułowany, chociaż jestem fanem biblioteki is_email .

To samo dotyczy wszystkich innych kontrolek formularza. Masz przyciski opcji? Sprawdź na liście. Masz pola wyboru? Sprawdź na liście. Czy przesłać plik? Upewnij się, że plik jest odpowiedniego typu i traktuj jego nazwę jak niefiltrowane dane użytkownika.

Każda nowoczesna przeglądarka ma wbudowany pełny zestaw narzędzi programistycznych, co sprawia, że ​​manipulowanie formularzem jest dla każdego banalne. Twój kod powinien zakładać, że użytkownik całkowicie usunął wszystkie ograniczenia po stronie klienta dotyczące zawartości formularza !

Ucieczka danych do przechowywania

Po upewnieniu się, że dane są w oczekiwanym formacie i zawierają tylko oczekiwane wartości, musisz się martwić o zachowanie tych danych w pamięci.

Każdy mechanizm przechowywania danych ma określony sposób, aby upewnić się, że dane są odpowiednio chronione i zakodowane. Jeśli tworzysz SQL, to akceptowanym sposobem przekazywania danych w zapytaniach są przygotowane instrukcje z symbolami zastępczymi .

Jednym z lepszych sposobów pracy z większością baz danych SQL w PHP jest rozszerzenie PDO . Postępuje zgodnie z powszechnym schematem przygotowywania instrukcji , wiążąc zmienne z instrukcją , a następnie wysyłając instrukcję i zmienne do serwera . Jeśli wcześniej nie pracowałeś z PDO, oto całkiem niezły samouczek zorientowany na MySQL .

Niektóre bazy danych SQL mają własne rozszerzenia w PHP, w tym SQL Server , PostgreSQL i SQLite 3 . Każde z tych rozszerzeń ma przygotowaną obsługę instrukcji, która działa w ten sam sposób, co PDO. Czasami może być konieczne użycie tych rozszerzeń zamiast PDO do obsługi niestandardowych funkcji lub zachowania.

MySQL ma również własne rozszerzenia PHP. A właściwie dwóch. Chcesz używać tylko tego o nazwie mysqli . Stare rozszerzenie „mysql” zostało wycofane i nie jest bezpieczne ani rozsądne w użyciu we współczesnych czasach.

Osobiście nie jestem fanem mysqli. Sposób, w jaki wykonuje zmienne wiązanie przygotowanych instrukcji, jest nieelastyczny i może być trudny w użyciu. W razie wątpliwości użyj zamiast tego PDO.

Jeśli nie używasz bazy danych SQL do przechowywania danych, zapoznaj się z dokumentacją interfejsu bazy danych, którego używasz, aby określić, jak bezpiecznie przekazywać przez nią dane.

Jeśli to możliwe, upewnij się, że Twoja baza danych przechowuje dane w odpowiednim formacie. Przechowuj liczby w polach numerycznych. Przechowuj daty w polach dat. Przechowuj pieniądze w polu dziesiętnym, a nie zmiennoprzecinkowym. Przejrzyj dokumentację dostarczoną przez bazę danych, aby dowiedzieć się, jak prawidłowo przechowywać różne typy danych.

Ucieczka danych do prezentacji

Za każdym razem, gdy pokazujesz dane użytkownikom, musisz upewnić się, że dane są bezpieczne, chyba że wiesz, że nie należy ich uciekać.

Emitując HTML, prawie zawsze powinieneś przekazywać wszelkie dane, które zostały pierwotnie dostarczone przez użytkownika htmlspecialchars. W rzeczywistości jedyny raz, kiedy nie powinieneś tego robić, to kiedy wiesz, że użytkownik dostarczył HTML i wiesz, że został on już oczyszczony za pomocą białej listy.

Czasami trzeba wygenerować JavaScript za pomocą PHP. Javascript nie ma takich samych reguł ucieczki jak HTML! Bezpiecznym sposobem na dostarczenie wartości dostarczonych przez użytkownika do JavaScript za pośrednictwem PHP jest przejście json_encode.

I więcej

Walidacja danych ma znacznie więcej niuansów.

Na przykład kodowanie zestawu znaków może być ogromną pułapką . Twoja aplikacja powinna być zgodna z praktykami opisanymi w „ UTF-8 do końca ”. Istnieją hipotetyczne ataki, które mogą wystąpić, gdy traktujesz dane łańcuchowe jako niewłaściwy zestaw znaków.

Wcześniej wspomniałem o narzędziach do debugowania przeglądarki. Narzędzia te mogą być również używane do manipulowania danymi z plików cookie. Pliki cookie należy traktować jako niezaufane dane wejściowe użytkownika .

Sprawdzanie poprawności danych i ucieczka to tylko jeden aspekt bezpieczeństwa aplikacji internetowych. Powinieneś zapoznać się z metodologiami ataków na aplikacje internetowe , aby móc zbudować przed nimi ochronę.

Charles
źródło
Określając go, upewnij się, że znajduje się na liście obsługiwanych kodowań.
Charles
3
I w ogóle nie używaj htmlentities, zamień je na htmlspecialchars w celu zastąpienia tylko <>, nie każdego znaku do jego encji
Your Common Sense
6
Tylko pamiętaj, aby nie dzwonić htmlspecialcharsdwa razy, ponieważ mówi o tym w części „Kiedy użytkownicy przesyłają dane” oraz w części „Podczas wyświetlania danych”.
Savageman
2
Głosowano za. Najbardziej pomocna odpowiedź, jaką przeczytałem w wielu pytaniach i odpowiedziach dotyczących wtrysku SQL.
akinuri
Absolutnie dobra odpowiedź z wieloma wyjaśnieniami i linkami dla przyszłych użytkowników, aby odkryć więcej opcji. Dostałem też ode mnie jeden-up ...
James Walker,
32

Najskuteczniejszą metodą oczyszczania, która zapobiega iniekcji SQL, jest parametryzacja za pomocą PDO. Za pomocą zapytań parametrycznych zapytanie jest oddzielane od danych, co eliminuje zagrożenie iniekcją SQL pierwszego rzędu.

Jeśli chodzi o usuwanie HTML, strip_tagsjest to prawdopodobnie najlepszy pomysł na usunięcie HTML, ponieważ po prostu usunie wszystko. htmlentitiesrobi to, na co wygląda, więc to też działa. Jeśli chcesz przeanalizować, który HTML zezwolić (to znaczy chcesz zezwolić na niektóre tagi), powinieneś użyć dojrzałego istniejącego parsera, takiego jak HTML Purifier

Derek H
źródło
2
O rany, napisałem tę gigantyczną ścianę tekstu tylko dlatego, że nie widziałem nikogo wspominającego o HTML Purifier, a tutaj pokonałeś mnie o jakieś 40 minut. ;)
Charles
3
Czy nie powinieneś usuwać HTML tylko na wyjściu? IMO Nigdy nie powinieneś zmieniać danych wejściowych - nigdy nie wiesz, kiedy będziesz ich potrzebować
Joe Phillips
11

Dane wejściowe bazy danych - jak zapobiec iniekcji SQL

  1. Sprawdź, czy dane typu integer, na przykład, są poprawne, upewniając się, że faktycznie jest to liczba całkowita
    • W przypadku znaków niebędących ciągami należy upewnić się, że dane są rzeczywiście właściwego typu
    • W przypadku łańcuchów musisz upewnić się, że ciąg jest otoczony cudzysłowami w zapytaniu (oczywiście, w przeciwnym razie nawet by nie zadziałał)
  2. Wprowadź wartość do bazy danych, unikając iniekcji SQL (mysql_real_escape_string lub sparametryzowane zapytania)
  3. Podczas pobierania wartości z bazy danych należy unikać ataków typu Cross Site Scripting, upewniając się, że nie można wstawić kodu HTML do strony (htmlspecialchars)

Musisz wycofać dane wejściowe użytkownika przed wstawieniem lub zaktualizowaniem ich do bazy danych. Oto starszy sposób, aby to zrobić. Chciałbyś teraz używać zapytań parametrycznych (prawdopodobnie z klasy PDO).

$mysql['username'] = mysql_real_escape_string($clean['username']);
$sql = "SELECT * FROM userlist WHERE username = '{$mysql['username']}'";
$result = mysql_query($sql);

Dane wyjściowe z bazy danych - jak zapobiec XSS (Cross Site Scripting)

Używaj htmlspecialchars()tylko podczas wyprowadzania danych z bazy danych. To samo dotyczy HTML Purifier. Przykład:

$html['username'] = htmlspecialchars($clean['username'])

I wreszcie ... o co prosiłeś

Muszę zaznaczyć, że jeśli używasz obiektów PDO z parametrami zapytań (właściwy sposób to zrobić), to naprawdę nie ma łatwego sposobu, aby to łatwo osiągnąć. Ale jeśli używasz starego sposobu 'mysql', to jest to, czego potrzebujesz.

function filterThis($string) {
    return mysql_real_escape_string($string);
}
Joe Phillips
źródło
5

Moje 5 centów.

Nikt tutaj nie rozumie, jak mysql_real_escape_stringdziała. Ta funkcja niczego nie filtruje ani nie oczyszcza.
Nie możesz więc używać tej funkcji jako uniwersalnego filtra, który uchroni Cię przed wtryskiem.
Możesz go używać tylko wtedy, gdy rozumiesz, jak działa i gdzie ma to zastosowanie.

Mam odpowiedź na bardzo podobne pytanie, które już napisałem: Czy w PHP przy wysyłaniu stringów do bazy należy zadbać o niedozwolone znaki przy pomocy htmlspecialchars () czy też użyć wyrażenia regularnego?
Kliknij, aby uzyskać pełne wyjaśnienie dotyczące bezpieczeństwa po stronie bazy danych.

Jeśli chodzi o elementy htmlent - Charles ma rację, mówiąc ci o oddzieleniu tych funkcji.
Wyobraź sobie, że zamierzasz wstawić dane wygenerowane przez administratora, który może publikować HTML. Twoja funkcja zepsuje to.

Chociaż odradzałbym htmlentities. Ta funkcja stała się przestarzała dawno temu. Jeśli chcesz zamienić tylko <, >i "postacie w trosce o bezpieczeństwo HTML - korzystanie z funkcji, która została opracowana celowo do tego celu - AN htmlspecialchars () jeden.

Twój zdrowy rozsądek
źródło
1
mysql_real_escape_stringwymyka potrzebne znaki w ciągu. Nie jest to ścisłe filtrowanie ani oczyszczanie, ale umieszczanie łańcucha w cudzysłowie też nie jest (i wszyscy to robią, prawie nigdy nie widziałem pytania na ten temat). Więc nic nie jest oczyszczane, kiedy piszemy SQL? Oczywiście nie. To, co zapobiega iniekcji SQL, to użycie mysql_real_escape_string. Także otaczające cudzysłowy, ale wszyscy to robią, a jeśli przetestujesz to, co robisz, z tym pominięciem otrzymasz błąd składni SQL. Naprawiono niebezpieczną część mysql_real_escape_string.
Savageman
@Savageman przepraszam kolego, nic nie rozumiesz. Nie rozumiesz, jak działa mysql_real_escape_string. Te „potrzebne znaki” SĄ cudzysłowami. Ani ta funkcja, ani same cytaty niczego nie oczyszczają. Te dwie rzeczy działają tylko razem . Uczynienie ciągu zapytania tylko poprawnym składniowo, a nie „zabezpieczonym przed wstrzyknięciem”. A za jaki błąd składni dostałbym tylko WHERE id = 1? ;)
Your Common Sense
Spróbuj WHERE my_field = two words(bez cudzysłowów), aby uzyskać błąd składni. Twój przykład jest zły, ponieważ nie wymaga cudzysłowów ani znaków ucieczki, a jedynie sprawdzenie liczbowe. Nie powiedziałem też, że cytaty są bezużyteczne. Powiedziałem, że wszyscy ich używają, więc to nie jest źródło problemów z iniekcją SQL.
Savageman
1
@Savageman, więc powiedziałem: Możesz go używać tylko wtedy, gdy rozumiesz, jak to działa i gdzie ma zastosowanie. Właśnie przyznałeś, że mysql_real_escape_string nie wszędzie ma zastosowanie. Co do everyone use themciebie, możesz sprawdzić kody tutaj na SO. Wiele osób nie używa cytatów z liczbami. Domyśl. I proszę, pamiętaj, że nie omawiam tutaj tego, co powiedziałeś, a czego nie. Wyjaśniam tylko podstawowe zasady bezpieczeństwa baz danych. Lepiej się uczyć zamiast pustych argumentów. Nikt tu nie wspomniał o cytatach ani o castingu, ale m_r_e_s tylko tak, jakby to była magia. O czym mówię
Your Common Sense
1
jeden w górę, a także @Charles. Jako nowicjusz, interakcja z bazą danych ... zapewniająca bezpieczeństwo wprowadzania i wyświetlania znaków specjalnych, problemów z wtryskiem, była bardzo stromą krzywą uczenia się. Czytanie twojego posta i jego (jak również innych twoich odpowiedzi PHP na inne pytania, bardzo mi pomogło. Tx dla całego twojego wkładu.
James Walker
2

Do wstawienia do bazy danych wystarczy mysql_real_escape_string(lub użyj zapytań parametrycznych). Zwykle nie chcesz zmieniać danych przed ich zapisaniem, co by się stało, gdybyś użył htmlentities. Doprowadziłoby to do zniekształconego bałaganu później, po htmlentitiesponownym uruchomieniu go, aby wyświetlić go gdzieś na stronie internetowej.

Użyj, htmlentitiesgdy wyświetlasz dane na jakiejś stronie internetowej.

Nieco powiązane, jeśli wysyłasz przesłane dane gdzieś w e-mailu, na przykład w formularzu kontaktowym, pamiętaj, aby usunąć znaki nowego wiersza z wszelkich danych, które zostaną użyte w nagłówku (takich jak Od: imię i nazwisko oraz adres e-mail, poddział itp. )

$input = preg_replace('/\s+/', ' ', $input);

Jeśli tego nie zrobisz, to tylko kwestia czasu, zanim roboty spamujące znajdą twoją formę i wykorzystają ją, nauczyłem się na własnej skórze.

Obrabować
źródło
2

To zależy od rodzaju używanych danych. Ogólnie najlepiej byłoby użyć, mysqli_real_escape_stringale na przykład wiesz, że nie będzie treści HTML, użycie strip_tags zapewni dodatkowe bezpieczeństwo.

Możesz także usunąć znaki, o których wiesz, że nie powinny być dozwolone.

Aaron Harun
źródło
1

Zawsze zalecam użycie małego pakietu walidacyjnego, takiego jak GUMP: https://github.com/Wixel/GUMP

Zbuduj wszystkie podstawowe funkcje wokół takiej biblioteki i prawie nie da się zapomnieć o urządzeniach sanitarnych. „mysql_real_escape_string” nie jest najlepszą alternatywą dla dobrego filtrowania (jak wyjaśniono w „Your Common Sense”) - a jeśli zapomnisz użyć go tylko raz, cały system będzie można zaatakować przez zastrzyki i inne nieprzyjemne ataki.

Simon Schneider
źródło
1

Dla wszystkich, którzy tutaj rozmawiają i polegają na mysql_real_escape_string, powinniście zauważyć, że ta funkcja była przestarzała w PHP5 i nie istnieje już w PHP7.

IMHO najlepszym sposobem wykonania tego zadania jest użycie sparametryzowanych zapytań poprzez wykorzystanie PDO do interakcji z bazą danych. Sprawdź to: https://phpdelusions.net/pdo_examples/select

Zawsze używaj filtrów do przetwarzania danych wejściowych użytkownika. Zobacz http://php.net/manual/es/function.filter-input.php

Kuntur
źródło
To właściwie nie odpowiada na pytanie. Rozważ zmodyfikowanie swojej odpowiedzi, tak aby zawierała rozwiązanie.
kris
Mam nadzieję że ci się spodoba!
Kuntur
Ja robię. Niezła odpowiedź!
kris
Proponuję odnotować, że w PHP 7 mysqli_real_escape_string()jest dostępne.
Chris,
Cześć Chris, przedstawione tutaj rozwiązania odwoływały się do mysql_real_escape_string. Zauważyłem, kto czytał od teraz, że nie istnieje już w PHP7 i zaproponowałem alternatywę wykorzystującą PDO (i filtry), a nie mysqli. Możesz dodać notatkę wyjaśniającą rozwiązanie, korzystając z sugestii. Pozdrawiam
Kuntur
0

Używasz mysql_real_escape_string () w kodzie podobnym do poniższego.

$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
  mysql_real_escape_string($user),
  mysql_real_escape_string($password)
);

Jak mówi dokumentacja, jego celem jest unikanie znaków specjalnych w ciągu przekazanym jako argument, biorąc pod uwagę bieżący zestaw znaków połączenia, aby można było bezpiecznie umieścić go w mysql_query () . Dokumentacja dodaje również:

Jeśli mają być wstawione dane binarne, należy użyć tej funkcji.

htmlentities () służy do konwersji niektórych znaków w encjach, gdy wyprowadzasz ciąg znaków w treści HTML.

kiamlaluno
źródło
0

To jest 1 sposób, w jaki obecnie ćwiczę,

  1. Implant csrf i token kuszenia soli wraz z żądaniem, które ma być wykonane przez użytkownika, i zweryfikuj je wszystkie razem z żądaniem. Zobacz tutaj
  2. upewnij się, że nie polegasz zbytnio na plikach cookie po stronie klienta i upewnij się, że ćwiczysz używanie sesji po stronie serwera
  3. podczas analizowania danych upewnij się, że akceptujesz tylko typ danych i metodę przesyłania (takie jak POST i GET)
  4. Upewnij się, że używasz SSL dla swojej aplikacji internetowej / aplikacji
  5. Upewnij się również, że wygenerowałeś żądanie sesji podstawy czasu, aby celowo ograniczyć żądanie spamu.
  6. Kiedy dane są parsowane na serwer, upewnij się, że żądanie zostało zatwierdzone w żądanej metodzie danych, takiej jak json, html itp., A następnie kontynuuj
  7. usuń wszystkie niedozwolone atrybuty z wejścia za pomocą typu ucieczki ... takiego jak realescapestring.
  8. po tym sprawdź onlyclean format typu danych, który chcesz od użytkownika.
    Przykład:
    - E-mail: sprawdź, czy dane wejściowe są w prawidłowym formacie e-mail
    - tekst / ciąg: sprawdź, czy dane wejściowe są tylko w formacie tekstowym (ciąg)
    - liczba: sprawdź, czy dozwolony jest tylko format liczb.
    - itp. Pelase odwołaj się do biblioteki do sprawdzania poprawności danych wejściowych php z portalu php
    - Po sprawdzeniu poprawności, kontynuuj używając przygotowanej instrukcji SQL / PDO.
    - Po zakończeniu pamiętaj o wyjściu i zakończeniu połączenia.
    - Nie zapomnij wyczyścić wartości wyjściowej po zakończeniu.

Uważam, że to wystarczy na podstawowe sekcje. Powinno to zapobiec wszystkim poważnym atakom ze strony hakerów.

Ze względów bezpieczeństwa po stronie serwera możesz chcieć ustawić w swoim apache / htaccess ograniczenie dostępu i zapobieganie robotom, a także zapobieganie routingowi. Oprócz bezpieczeństwa systemu po stronie serwera jest wiele do zrobienia.

Możesz się nauczyć i otrzymać kopię sec z poziomu htaccess apache sec (typowe rpaktyki)

Ahmad Anuar
źródło
0
function sanitize($string,$dbmin,$dbmax){
$string = preg_replace('#[^a-z0-9]#i', '', $string); //useful for strict cleanse, alphanumeric here
$string = mysqli_real_escape_string($con, $string); //get ready for db
if(strlen($string) > $dbmax || strlen($string) < $dbmin){
    echo "reject_this"; exit();
    }
return $string;
}
stkmedia
źródło
0

a co z tym

$string = htmlspecialchars(strip_tags($_POST['example']));

albo to

$string = htmlentities($_POST['example'], ENT_QUOTES, 'UTF-8');
jerryurenaa
źródło