Jakie są techniczne powody, dla których nie należy korzystać z mysql_*
funkcji? (na przykład mysql_query()
, mysql_connect()
i mysql_real_escape_string()
)?
Dlaczego powinienem używać czegoś innego, nawet jeśli działają na mojej stronie?
Jeśli nie działają w mojej witrynie, dlaczego dostaję takie błędy
Ostrzeżenie: mysql_connect (): Brak takiego pliku lub katalogu
Odpowiedzi:
Rozszerzenie MySQL:
Ponieważ jest przestarzałe, użycie go sprawia, że Twój kod jest mniej odporny na przyszłe problemy.
Brak wsparcia dla przygotowanych instrukcji jest szczególnie ważny, ponieważ zapewniają one bardziej przejrzystą, mniej podatną na błędy metodę ucieczki i cytowania danych zewnętrznych niż ręczne ucieczkowanie ich osobnym wywołaniem funkcji.
Zobacz porównanie rozszerzeń SQL .
źródło
PHP oferuje trzy różne interfejsy API do łączenia się z MySQL. Są to
mysql
(usunięte z PHP 7)mysqli
iPDO
rozszerzenia.Te
mysql_*
funkcje kiedyś bardzo popularne, ale ich stosowanie nie jest zalecane więcej. Zespół dokumentacji omawia sytuację w zakresie bezpieczeństwa bazy danych i częścią tego jest szkolenie użytkowników, jak odejść od powszechnie używanego rozszerzenia ext / mysql (sprawdź php.internals: wycofywanie ext / mysql ).A później zespół PHP developer podjął decyzję, aby generować
E_DEPRECATED
błędy podczas łączenia się użytkowników MySQL, czy przezmysql_connect()
,mysql_pconnect()
lub niejawny funkcjonalność połączenia wbudowanyext/mysql
.ext/mysql
został oficjalnie uznany za przestarzały od wersji PHP 5.5 i został usunięty od wersji PHP 7 .Widzisz czerwone pudełko?
Gdy przejdziesz do dowolnej
mysql_*
strony podręcznika funkcji, zobaczysz czerwone pole wyjaśniające, że nie należy jej już używać.Dlaczego
Odejście od
ext/mysql
dotyczy nie tylko bezpieczeństwa, ale także dostępu do wszystkich funkcji bazy danych MySQL.ext/mysql
został zbudowany dla MySQL 3.23 i od tego czasu otrzymał bardzo niewiele dodatków, przy jednoczesnym zachowaniu zgodności z tą starą wersją, co utrudnia utrzymanie kodu. Brakujące funkcje, które nie są obsługiwane przezext/mysql
: ( z podręcznika PHP ).Powód nieużywania
mysql_*
funkcji :Powyższy punkt cytowany z odpowiedzi Quentina
Brak wsparcia dla przygotowanych instrukcji jest szczególnie ważny, ponieważ zapewniają one bardziej przejrzystą, mniej podatną na błędy metodę ucieczki i cytowania danych zewnętrznych niż ręczne ucieczkowanie ich osobnym wywołaniem funkcji.
Zobacz porównanie rozszerzeń SQL .
Pomijanie ostrzeżeń o wycofaniu
Natomiast kod jest konwertowana do
MySQLi
/PDO
,E_DEPRECATED
błędy mogą być tłumione przez ustawienieerror_reporting
w php.ini wykluczaniaE_DEPRECATED:
Zauważ, że to ukryje także inne ostrzeżenia o wycofaniu , które jednak mogą dotyczyć rzeczy innych niż MySQL. ( z instrukcji PHP )
Artykuł PDO vs. MySQLi: Którego należy użyć? autor: Dejan Marjanovic pomoże ci wybrać.
I jest lepszy sposób
PDO
, a teraz piszę prostyPDO
samouczek.Prosty i krótki samouczek PDO
P: Pierwsze pytanie w mojej głowie brzmiało: co to jest „PDO”?
A. „ PDO - PHP Data Objects - to warstwa dostępu do bazy danych zapewniająca jednolitą metodę dostępu do wielu baz danych.”
Łączenie z MySQL
Z
mysql_*
funkcją lub możemy to powiedzieć po staremu (przestarzałe w PHP 5.5 i nowszych)Z
PDO
: Wszystko, co musisz zrobić, to utworzyć nowyPDO
obiekt. Konstruktor przyjmuje parametry określające źródło bazyPDO
„S konstruktor przeważnie czterech parametrów, które sąDSN
(nazwa źródła danych) i ewentualnieusername
,password
.Tutaj myślę, że znasz wszystko oprócz
DSN
; to jest nowy wPDO
. ADSN
to w zasadzie ciąg opcji określających,PDO
którego sterownika należy użyć, i szczegóły połączenia. Aby uzyskać dodatkowe informacje, sprawdź PDO MySQL DSN .Uwaga: możesz także użyć
charset=UTF-8
, ale czasami powoduje to błąd, więc lepiej jest użyćutf8
.Jeśli wystąpi jakikolwiek błąd połączenia, wyrzuci
PDOException
obiekt, który można złapać w celuException
dalszej obsługi .Dobra lektura : Połączenia i zarządzanie połączeniami ¶
Możesz również przekazać kilka opcji sterownika jako tablicę do czwartego parametru. Polecam przekazanie parametru, który przechodzi
PDO
w tryb wyjątku. Ponieważ niektórePDO
sterowniki nie obsługują natywnie przygotowanych instrukcji,PDO
wykonuje emulację przygotowywania. Pozwala także ręcznie włączyć tę emulację. Aby użyć natywnych instrukcji przygotowanych po stronie serwera, należy je jawnie ustawićfalse
.Drugim jest wyłączenie przygotowania emulacji, która jest
MySQL
domyślnie włączona w sterowniku, ale przygotowanie emulacji powinno być wyłączone, aby można byłoPDO
bezpiecznie korzystać .Wyjaśnię później, dlaczego należy przygotować emulację. Aby znaleźć powód, sprawdź ten post .
Można go używać tylko wtedy, gdy używasz starej wersji,
MySQL
której nie zaleciłem.Poniżej znajduje się przykład tego, jak możesz to zrobić:
Czy możemy ustawić atrybuty po budowie PDO?
Tak , możemy również ustawić niektóre atrybuty po konstrukcji PDO za pomocą
setAttribute
metody:Obsługa błędów
Obsługa błędów jest znacznie łatwiejsza
PDO
niżmysql_*
.Powszechną praktyką podczas używania
mysql_*
jest:OR die()
nie jest dobrym sposobem na poradzenie sobie z błędem, ponieważ nie możemy sobie poradzićdie
. To po prostu nagle zakończy skrypt, a następnie powtórzy błąd na ekranie, którego zwykle NIE chcesz pokazywać użytkownikom końcowym, i pozwoli cholernym hakerom odkryć twój schemat. Alternatywnie, zwracane wartościmysql_*
funkcji mogą być często używane w połączeniu z mysql_error () do obsługi błędów.PDO
oferuje lepsze rozwiązanie: wyjątki. Wszystko robimy zPDO
powinny być zapakowane wtry
-catch
bloku. Możemy wymusićPDO
przejście do jednego z trzech trybów błędów, ustawiając atrybut trybu błędu. Poniżej przedstawiono trzy tryby obsługi błędów.PDO::ERRMODE_SILENT
. Po prostu ustawia kody błędów i działa prawie tak samo, jakmysql_*
tam, gdzie trzeba sprawdzić każdy wynik, a następnie spojrzeć,$db->errorInfo();
aby uzyskać szczegółowe informacje o błędzie.PDO::ERRMODE_WARNING
PodbicieE_WARNING
. (Ostrzeżenia w czasie wykonywania (błędy niekrytyczne). Wykonanie skryptu nie jest zatrzymane.)PDO::ERRMODE_EXCEPTION
: Zgłaszaj wyjątki. Reprezentuje błąd zgłoszony przez ChNP. Nie powinieneś wyrzucaćPDOException
własnego kodu. Zobacz Wyjątki, aby uzyskać więcej informacji o wyjątkach w PHP. Zachowuje się bardzo podobnieor die(mysql_error());
, gdy nie zostanie złapany. Ale w przeciwieństwie do tegoor die()
,PDOException
można je złapać i traktować z wdziękiem, jeśli zdecydujesz się to zrobić.Dobra lektura :
Lubić:
I możesz to owinąć
try
-catch
jak poniżej:Nie musisz sobie z tym poradzić
try
- wcatch
tej chwili. Możesz go złapać w dowolnym momencie, ale zdecydowanie zalecamy użycietry
-catch
. Bardziej sensowne może być złapanie go poza funkcją wywołującą tePDO
rzeczy:Możesz sobie poradzić
or die()
lub możemy to powiedziećmysql_*
, ale będzie to bardzo zróżnicowane. Możesz ukryć niebezpieczne komunikaty o błędach podczas produkcji, obracającdisplay_errors off
i po prostu czytając dziennik błędów.Teraz, po przeczytaniu wszystkich rzeczy powyżej, prawdopodobnie myśląc: co do cholery jest, że kiedy tylko chcesz rozpocząć pochylony proste
SELECT
,INSERT
,UPDATE
lubDELETE
oświadczenia? Nie martw się, zaczynamy:Wybieranie danych
Więc to, co robisz,
mysql_*
to:Teraz
PDO
możesz to zrobić w następujący sposób:Lub
Uwaga : Jeśli używasz metody jak poniżej (
query()
), ta metoda zwracaPDOStatement
obiekt. Więc jeśli chcesz pobrać wynik, użyj go jak powyżej.W PDO Data jest uzyskiwany za
->fetch()
pomocą metody obsługi wyciągu. Przed wywołaniem funkcji pobierania najlepszym rozwiązaniem byłoby określenie PDO, w jaki sposób dane mają być pobierane. W poniższej sekcji wyjaśniam to.Tryby pobierania
Zwróć uwagę na użycie
PDO::FETCH_ASSOC
wfetch()
ifetchAll()
kodu powyżej. MówiPDO
to o zwróceniu wierszy jako tablicy asocjacyjnej z nazwami pól jako kluczami. Istnieje również wiele innych trybów pobierania, które wyjaśnię jeden po drugim.Przede wszystkim wyjaśniam, jak wybrać tryb pobierania:
W powyższym używam
fetch()
. Możesz także użyć:PDOStatement::fetchAll()
- Zwraca tablicę zawierającą wszystkie wiersze zestawu wynikówPDOStatement::fetchColumn()
- Zwraca pojedynczą kolumnę z następnego wiersza zestawu wynikówPDOStatement::fetchObject()
- Pobiera następny wiersz i zwraca go jako obiekt.PDOStatement::setFetchMode()
- Ustaw domyślny tryb pobierania dla tej instrukcjiTeraz przechodzę do trybu pobierania:
PDO::FETCH_ASSOC
: zwraca tablicę indeksowaną według nazwy kolumny, tak jak zwrócono w zestawie wynikówPDO::FETCH_BOTH
(domyślnie): zwraca tablicę zindeksowaną zarówno przez nazwę kolumny, jak i numer kolumny o indeksie 0, jak zwrócono w zestawie wynikówJest jeszcze więcej możliwości! Przeczytaj o nich wszystkich w
PDOStatement
dokumentacji Fetch. .Uzyskiwanie liczby wierszy :
Zamiast używać
mysql_num_rows
do uzyskania liczby zwróconych wierszy, możesz uzyskaćPDOStatement
i zrobićrowCount()
, na przykład:Uzyskiwanie ostatniego wstawionego identyfikatora
Wstawianie i aktualizowanie lub usuwanie instrukcji
To, co robimy w
mysql_*
funkcji, to:I w pdo, to samo można zrobić poprzez:
W powyższym zapytaniu
PDO::exec
uruchom instrukcję SQL i zwróci liczbę dotkniętych wierszy.Wstaw i usuń zostaną omówione później.
Powyższa metoda jest przydatna tylko wtedy, gdy nie używasz zmiennej w zapytaniu. Ale kiedy potrzebujesz użyć zmiennej w zapytaniu, nigdy nie próbuj tak jak wyżej, a tam jest gotowa instrukcja lub instrukcja sparametryzowana .
Przygotowane oświadczenia
P: Co to jest przygotowane oświadczenie i dlaczego ich potrzebuję?
Odp .: Przygotowana instrukcja to wstępnie skompilowana instrukcja SQL, którą można wykonać wiele razy, wysyłając tylko dane do serwera.
Typowy przepływ pracy przy użyciu przygotowanej instrukcji jest następujący ( cytowany z Wikipedii trzy 3 punkty ):
Przygotuj : Szablon instrukcji jest tworzony przez aplikację i wysyłany do systemu zarządzania bazą danych (DBMS). Niektóre wartości pozostają nieokreślone, nazywane parametrami, symbolami zastępczymi lub zmiennymi powiązania (oznaczone
?
poniżej):INSERT INTO PRODUCT (name, price) VALUES (?, ?)
DBMS analizuje, kompiluje i wykonuje optymalizację zapytań na szablonie instrukcji i zapisuje wynik bez jego wykonania.
1.00
dla drugiego parametru.Możesz użyć przygotowanej instrukcji, umieszczając symbole zastępcze w kodzie SQL. Zasadniczo są trzy bez symboli zastępczych (nie próbuj tego ze zmienną powyżej jednego), jeden z nienazwanymi symbolami zastępczymi i jeden z nazwanymi symbolami zastępczymi.
P: Więc jakie są teraz nazwane symbole zastępcze i jak ich używać?
A. Nazwane symbole zastępcze. Używaj opisowych nazw poprzedzonych dwukropkiem zamiast znaków zapytania. Nie dbamy o pozycję / porządek wartości w nazwie zastępczej:
bindParam(parameter,variable,data_type,length,driver_options)
Możesz również powiązać za pomocą tablicy wykonawczej:
Inną ciekawą funkcją dla
OOP
znajomych jest to, że nazwane symbole zastępcze mają możliwość wstawiania obiektów bezpośrednio do bazy danych, zakładając, że właściwości pasują do nazwanych pól. Na przykład:P: Więc jakie są teraz nienazwane symbole zastępcze i jak ich używać?
A. Weźmy przykład:
i
Powyżej możesz zobaczyć te
?
zamiast nazwy, jak w miejscu na nazwę. Teraz w pierwszym przykładzie przypisujemy zmienne do różnych symboli zastępczych ($stmt->bindValue(1, $name, PDO::PARAM_STR);
). Następnie przypisujemy wartości do tych symboli zastępczych i wykonujemy instrukcję. W drugim przykładzie pierwszy element tablicy przechodzi do pierwszego,?
a drugi do drugiego?
.UWAGA : W nienazwanych symbolach zastępczych musimy zadbać o prawidłową kolejność elementów w tablicy, którą przekazujemy do
PDOStatement::execute()
metody.SELECT
,INSERT
,UPDATE
,DELETE
Przygotowany zapytaniaSELECT
:INSERT
:DELETE
:UPDATE
:UWAGA:
Jednak
PDO
i / lubMySQLi
nie są całkowicie bezpieczne. Sprawdź odpowiedź Czy instrukcje przygotowane przez PDO są wystarczające, aby zapobiec wstrzyknięciu SQL? przez ircmaxell . Cytuję też część jego odpowiedzi:źródło
IN (...) construct
.function throwEx() { throw new Exception("You did selected not existng db"); } mysql_select_db("nonexistdb") or throwEx();
dla wyjątków. Działa w przypadku zgłaszania wyjątków.Doesn't support non-blocking, asynchronous queries
jako powód, aby nie używać mysql_ - powinieneś również podać ten powód, aby nie używać PDO, ponieważ PDO też tego nie obsługuje. (ale MySQLi to obsługuje)Najpierw zacznijmy od standardowego komentarza, który dajemy wszystkim:
Przejdźmy przez to zdanie po zdaniu i wyjaśnimy:
Nie są już utrzymywane i oficjalnie przestarzałe
Oznacza to, że społeczność PHP stopniowo rezygnuje z obsługi tych bardzo starych funkcji. Prawdopodobnie nie będą istnieć w przyszłej (najnowszej) wersji PHP! Dalsze korzystanie z tych funkcji może uszkodzić kod w (nie tak odległej) przyszłości.
NOWY! - ext / mysql jest teraz oficjalnie przestarzałe od PHP 5.5!
Nowszy! ext / mysql został usunięty w PHP 7 .
Zamiast tego powinieneś nauczyć się przygotowanych wypowiedzi
mysql_*
rozszerzenie nie obsługuje przygotowanych instrukcji , co jest (między innymi) bardzo skutecznym środkiem zaradczym przeciwko SQL Injection . Naprawiono bardzo poważną lukę w aplikacjach zależnych od MySQL, która pozwala atakującym uzyskać dostęp do skryptu i wykonać dowolne zapytanie w bazie danych.Aby uzyskać więcej informacji, zobacz Jak zapobiec iniekcji SQL w PHP?
Widzisz czerwone pudełko?
Gdy przejdziesz do dowolnej
mysql
strony podręcznika funkcji, zobaczysz czerwone pole wyjaśniające, że nie należy jej już używać.Użyj PDO lub MySQLi
Istnieją lepsze, bardziej niezawodne i dobrze zbudowane alternatywy, PDO - PHP Database Object , który oferuje pełne podejście OOP do interakcji z bazami danych, oraz MySQLi , który jest poprawą specyficzną dla MySQL.
źródło
IN (...) construct
.Łatwość użycia
Powody analityczne i syntetyczne zostały już wspomniane. Dla nowicjuszy jest bardziej znacząca zachęta do zaprzestania korzystania z datowanych funkcji mysql_.
Współczesne interfejsy API baz danych są po prostu łatwiejsze w użyciu.
W większości są to związane parametry, które mogą uprościć kod. A dzięki doskonałym samouczkom (jak widać powyżej) przejście na PDO nie jest zbyt uciążliwe.
Jednak przepisywanie większej bazy kodu jednocześnie wymaga czasu. Raison d'être dla tej pośredniej alternatywy:
Równoważne funkcje pdo_ * zamiast
mysql_ *Używając < pdo_mysql.php > możesz przełączyć się ze starych funkcji mysql_ przy minimalnym wysiłku . Dodaje
pdo_
opakowania funkcji, które zastępują ichmysql_
odpowiedniki.Po prostu w każdym skrypcie wywołania, który musi wchodzić w interakcje z bazą danych.
include_once(
"pdo_mysql.php"
);
Usuń
prefiks funkcji wszędzie i zastąp gomysql_
pdo_
.mysql_
connect()
staje siępdo_
connect()
mysql_
query()
staje siępdo_
query()
mysql_
num_rows()
staje siępdo_
num_rows()
mysql_
insert_id()
staje siępdo_
insert_id()
mysql_
fetch_array()
staje siępdo_
fetch_array()
mysql_
fetch_assoc()
staje siępdo_
fetch_assoc()
mysql_
real_escape_string()
staje siępdo_
real_escape_string()
Twój kod będzie działał podobnie i nadal będzie wyglądał tak samo:
Zrobione.
Twój kod używa PDO.
Teraz nadszedł czas, aby z niego skorzystać .
Związane parametry mogą być łatwe w użyciu
Potrzebujesz tylko mniej nieporęcznego API.
pdo_query()
dodaje bardzo łatwą obsługę powiązanych parametrów. Konwersja starego kodu jest prosta:Przenieś zmienne z ciągu SQL.
pdo_query()
.?
jako symbole zastępcze tam, gdzie wcześniej były zmienne.'
pojedynczych cudzysłowów, które poprzednio zawierały wartości / zmienne łańcuchowe.Korzyść staje się bardziej oczywista w przypadku dłuższego kodu.
Często zmienne łańcuchowe są nie tylko interpolowane w SQL, ale łączone z ucieczkowymi wywołaniami pomiędzy nimi.
Po
?
zastosowaniu symboli zastępczych nie musisz się tym przejmować:Pamiętaj, że pdo_ * nadal pozwala na albo .
Po prostu nie zmieniaj zmiennej i nie wiąż jej w tym samym zapytaniu.
:named
listy zastępcze później.Co ważniejsze, możesz bezpiecznie przekazać zmienne $ _REQUEST [] za dowolnym zapytaniem. Gdy przesłane
<form>
pola odpowiadają strukturze bazy danych, jest ona jeszcze krótsza:Tyle prostoty. Wróćmy jednak do kilku porad dotyczących przepisywania i technicznych powodów, dla których możesz chcieć się pozbyć
i uciec.mysql_
Napraw lub usuń dowolną
sanitize()
funkcję oldschoolPo przekonwertowaniu wszystkich
połączeń namysql_
pdo_query
powiązane parametry usuń wszystkie zbędnepdo_real_escape_string
połączenia.W szczególności powinieneś naprawić dowolne
sanitize
lubclean
lubfilterThis
lubclean_data
funkcje reklamowane w datowanych samouczkach w takiej lub innej formie:Najbardziej rażącym błędem tutaj jest brak dokumentacji. Co ważniejsze, kolejność filtrowania była dokładnie w niewłaściwej kolejności.
Prawidłowa kolejność byłaby: deprecatedly
stripslashes
jako wezwanie najgłębszej, potemtrim
, potemstrip_tags
,htmlentities
za kontekście wyjścia i tylko wreszcie_escape_string
jako jego stosowania powinien bezpośrednio poprzedzać SQL intersparsing.Ale jako pierwszy krok po prostu pozbądź się
_real_escape_string
połączenia.Być może będziesz musiał zatrzymać resztę
sanitize()
funkcji na razie, jeśli przepływ bazy danych i aplikacji oczekuje ciągów bezpiecznych w kontekście HTML. Dodaj komentarz, że odtąd stosuje się tylko znaki HTML.Obsługa ciągów / wartości jest delegowana do PDO i jej sparametryzowanych instrukcji.
Jeśli wspomniano o
stripslashes()
Twojej funkcji odkażania, może to wskazywać na nadzór wyższego poziomu.To było zwykle po to, by cofnąć obrażenia (podwójne ucieczki) od przestarzałych
magic_quotes
. Które jednak najlepiej jest ustawić centralnie , a nie ciąg po ciągu.Użyj jednej z metod odwracania przestrzeni użytkownika . Następnie usuń funkcję
stripslashes()
wsanitize
.Czym różnią się przygotowane oświadczenia
Gdy wmieszasz zmienne łańcuchowe do zapytań SQL, nie jest to tylko bardziej skomplikowane. To także dodatkowy wysiłek, aby MySQL ponownie segregował kod i dane.
Zastrzyki SQL są po prostu gdy dane spadają do kontekstu kodu . Serwer bazy danych nie może później wykryć, gdzie PHP pierwotnie skleił zmienne między klauzulami zapytania.
Za pomocą powiązanych parametrów oddzielasz kod SQL i wartości kontekstu SQL w kodzie PHP. Ale nie pojawia się ponownie za kulisami (z wyjątkiem PDO :: EMULATE_PREPARES). Twoja baza danych odbiera niezróżnicowane polecenia SQL i wartości zmiennych 1: 1.
Chociaż ta odpowiedź podkreśla, że powinieneś dbać o czytelność zalet upuszczania
. Czasami występuje również przewaga wydajności (powtarzane WSTAWKI z tylko różnymi wartościami) dzięki temu widocznemu i technicznemu rozdzieleniu danych / kodów.mysql_
Uważaj, że wiązanie parametrów nadal nie jest magicznym kompleksowym rozwiązaniem dla wszystkich zastrzyków SQL. Obsługuje najczęściej używane dane / wartości. Nie można jednak dodać do białej listy nazw kolumn / identyfikatorów tabel, pomocy w dynamicznej konstrukcji klauzul ani po prostu list wartości tablic.
Zastosowanie hybrydowego PDO
Te
pdo_*
funkcje otoki tworzą przyjazny dla kodu interfejs API stop-gap. (Jest to właściwie to, coMYSQLI
mogłoby być, gdyby nie przesunięcie podpisu funkcji idiosynkratycznej). W większości przypadków ujawniają również prawdziwą chronioną nazwę pochodzenia.Przepisywanie nie musi kończyć się na użyciu nowych nazw funkcji pdo_. Możesz po kolei przejść każde pdo_query () do zwykłego wywołania $ pdo-> prepar () -> execute ().
Jednak najlepiej zacząć od uproszczenia. Na przykład typowe pobieranie wyników:
Można zastąpić tylko iteracją Foreach:
Lub jeszcze lepiej bezpośrednie i pełne wyszukiwanie tablicy:
W większości przypadków otrzymasz bardziej pomocne ostrzeżenia niż PDO lub mysql_, które zwykle dostarczają po nieudanych zapytaniach.
Inne opcje
Mamy nadzieję, że zwizualizowałem kilka praktycznych powodów i wartą uwagi ścieżkę do upuszczenia
.mysql_
Właśnie zmieniam na pdonie do końca to.
pdo_query()
jest także tylko nakładką na to.O ile nie wprowadzisz również powiązania parametrów lub nie możesz użyć czegoś innego z ładniejszego interfejsu API, jest to bezcelowy przełącznik. Mam nadzieję, że obraz ten jest na tyle prosty, że nie zniechęca nowych przybyszów. (Edukacja zwykle działa lepiej niż prohibicja).
Chociaż kwalifikuje się do kategorii najprostszej rzeczy, która mogłaby ewentualnie działać, jest także bardzo eksperymentalnym kodem. Właśnie napisałem to w weekend. Istnieje jednak mnóstwo alternatyw. Wystarczy google dla abstrakcji bazy danych PHP i przeglądać trochę. Zawsze było i będzie wiele doskonałych bibliotek do takich zadań.
Jeśli chcesz jeszcze bardziej uprościć interakcję z bazą danych, warto spróbować twórców map , takich jak Paris / Idiorm . Tak jak nikt już nie używa nijakiego DOM w JavaScript, nie musisz obecnie opiekować się interfejsem surowej bazy danych.
źródło
pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
funkcję - tj .:pdo_query("INSERT INTO users VALUES (?, ?, ?), $_POST); $_POST = array( 'username' => 'lawl', 'password' => '123', 'is_admin' => 'true');
pdo_real_escape_string()
<- Czy to nawet prawdziwa funkcja, nie mogę znaleźć dla niej dokumentacji? Proszę zamieścić źródło tego.Te
mysql_
funkcje:źródło
mysqli_
mysql_*
funkcja to powłoka na funkcje mysqlnd nowsze wersje PHP. Więc nawet jeśli stara biblioteka klienta nie jest już utrzymywana, mysqlnd jest utrzymywany :)Mówiąc o przyczynach technicznych , jest tylko kilka, wyjątkowo specyficznych i rzadko używanych. Najprawdopodobniej nigdy nie użyjesz ich w swoim życiu.
Może jestem zbyt nieświadomy, ale nigdy nie miałem okazji korzystać z takich rzeczy
Jeśli ich potrzebujesz - to bez wątpienia techniczne powody, aby odejść od rozszerzenia mysql w kierunku czegoś bardziej stylowego i nowocześnie wyglądającego.
Niemniej jednak istnieją również problemy pozatechniczne, które mogą sprawić, że twoje wrażenia będą nieco trudniejsze
Ten ostatni problem stanowi problem.
Ale moim zdaniem proponowane rozwiązanie też nie jest lepsze.
Wydaje mi się zbyt idealistycznym snem, że wszyscy ci użytkownicy PHP nauczą się, jak poprawnie obsługiwać zapytania SQL. Najprawdopodobniej po prostu zmieniliby mechanicznie mysql_ * na mysqli_ *, pozostawiając to samo podejście . Zwłaszcza, że mysqli sprawia, że korzystanie z przygotowanych wyciągów jest niesamowicie bolesne i kłopotliwe.
Nie wspominając, że natywnie przygotowane oświadczenia nie wystarczą do ochrony przed wstrzyknięciami SQL i ani mysqli, ani PDO nie oferują rozwiązania.
Zamiast walczyć z tym uczciwym rozszerzeniem, wolałbym zwalczać złe praktyki i edukować ludzi we właściwy sposób.
Ponadto istnieje kilka fałszywych lub nieistotnych przyczyn, takich jak
mysql_query("CALL my_proc");
od wieków)Ten ostatni jest interesującym punktem. Chociaż mysql ext nie obsługuje natywnie przygotowanych instrukcji, nie są one wymagane ze względu na bezpieczeństwo. Możemy łatwo sfałszować przygotowane wyciągi, używając ręcznie obsługiwanych symboli zastępczych (podobnie jak PDO):
voila , wszystko jest sparametryzowane i bezpieczne.
Ale dobrze, jeśli nie podoba ci się czerwone pole w instrukcji, pojawia się problem z wyborem: mysqli lub PDO?
Odpowiedź byłaby następująca:
Jeśli, jak większość ludzi PHP, używasz surowych wywołań API bezpośrednio w kodzie aplikacji (co jest zasadniczo niewłaściwą praktyką) - PDO jest jedynym wyborem , ponieważ to rozszerzenie udaje nie tylko API, ale raczej pół-DAL, wciąż niekompletny, ale oferuje wiele ważnych funkcji, a dwie z nich wyraźnie odróżniają ChNP od mysqli:
Tak więc, jeśli jesteś przeciętnym użytkownikiem PHP i chcesz zaoszczędzić mnóstwo kłopotów z użyciem natywnie przygotowanych instrukcji, PDO - znowu - jest jedynym wyborem.
Jednak ChNP nie jest też srebrną kulą i ma swoje trudności.
Tak więc napisałem rozwiązania wszystkich typowych pułapek i skomplikowanych przypadków na wiki tagu PDO
Niemniej jednak wszystkim, którzy mówią o rozszerzeniach, zawsze brakuje 2 ważnych faktów na temat Mysqli i PDO:
Przygotowane oświadczenie nie jest srebrną kulą . Istnieją identyfikatory dynamiczne, których nie można powiązać przy użyciu przygotowanych instrukcji. Istnieją dynamiczne zapytania o nieznanej liczbie parametrów, co sprawia, że tworzenie zapytań jest trudnym zadaniem.
W kodzie aplikacji nie powinny pojawić się ani funkcje mysqli_ *, ani PDO. Pomiędzy nimi a kodem aplikacji
powinna znajdować się warstwa abstrakcji , która wykona całą brudną robotę związania, zapętlenia, obsługi błędów itp. Wewnątrz, dzięki czemu kod aplikacji stanie się SUCHY i czysty. Zwłaszcza w skomplikowanych przypadkach, takich jak dynamiczne budowanie zapytań.
Tak więc samo przejście na PDO lub mysqli nie wystarczy. Należy użyć ORM, konstruktora zapytań lub dowolnej klasy abstrakcji bazy danych zamiast wywoływać surowe funkcje API w kodzie.
Przeciwnie - jeśli masz warstwę abstrakcji między kodem aplikacji a interfejsem API mysql - nie ma znaczenia, który silnik jest używany. Możesz używać mysql ext, dopóki nie stanie się przestarzały, a następnie łatwo przepisać klasę abstrakcji na inny silnik, zachowując cały kod aplikacji.
Oto kilka przykładów opartych na mojej klasie safemysql, które pokazują, jak powinna wyglądać taka klasa abstrakcji:
Porównaj tę pojedynczą linię z ilością kodu, której będziesz potrzebować w PDO .
Następnie porównaj z szaloną ilością kodu, której będziesz potrzebować z surowymi instrukcjami przygotowanymi przez Mysqli. Należy pamiętać, że obsługa błędów, profilowanie, rejestrowanie zapytań jest już wbudowane i uruchomione.
Porównaj go ze zwykłymi wstawkami PDO, gdy każda nazwa pola jest powtarzana sześć do dziesięciu razy - we wszystkich tych licznych nazwanych symbolach zastępczych, powiązaniach i definicjach zapytań.
Inny przykład:
Trudno znaleźć przykład PDO do obsługi tak praktycznego przypadku.
I będzie zbyt pracowity i najprawdopodobniej niebezpieczny.
Więc jeszcze raz - nie tylko surowy sterownik powinien być twoją troską, ale klasa abstrakcyjna, przydatna nie tylko dla niemądrych przykładów z podręcznika dla początkujących, ale do rozwiązania rzeczywistych problemów.
źródło
mysql_*
sprawia, że luki w zabezpieczeniach są bardzo łatwe do znalezienia. Ponieważ PHP jest używane przez wielu początkujących użytkowników,mysql_*
jest aktywnie szkodliwe w praktyce, nawet jeśli teoretycznie można go używać bez żadnych problemów.everything is parameterized and safe
- może to być parametryzowane, ale czynność nie używa prawdziwych przygotowanych sprawozdań.Not under active development
tylko w przypadku tego „0,01%”? Jeśli zbudujesz coś z tą funkcją zatrzymania, zaktualizujesz wersję mysql w ciągu roku i skończysz z niedziałającym systemem, jestem pewien, że nagle jest tak dużo ludzi w tym „0,01%”. Powiedziałbym todeprecated
inot under active development
są blisko spokrewnieni. Można powiedzieć, że „nie ma [uzasadnionego] powodu”, ale faktem jest, że kiedy oferuje się wybór między opcjami,no active development
jest prawie tak samo zły, jakdeprecated
powiedziałbym?Istnieje wiele powodów, ale być może najważniejszym jest to, że funkcje te zachęcają do niepewnych praktyk programowania, ponieważ nie obsługują przygotowanych instrukcji. Przygotowane instrukcje pomagają zapobiegać atakom typu SQL injection.
Korzystając z
mysql_*
funkcji, musisz pamiętać, aby przez parametry uruchamiać parametry podane przez użytkownikamysql_real_escape_string()
. Jeśli zapomnisz tylko w jednym miejscu lub jeśli zdarzy ci się uciec tylko części danych, baza danych może zostać zaatakowana.Korzystanie z przygotowanych instrukcji w
PDO
lubmysqli
sprawi, że tego rodzaju błędy programistyczne będą trudniejsze do wykonania.źródło
Ponieważ (między innymi) znacznie trudniej jest zapewnić, że dane wejściowe zostaną zdezynfekowane. Jeśli używasz sparametryzowanych zapytań, podobnie jak w przypadku PDO lub mysqli, możesz całkowicie uniknąć ryzyka.
Na przykład ktoś może użyć
"enhzflep); drop table users"
jako nazwy użytkownika. Stare funkcje pozwalają na wykonywanie wielu instrukcji na zapytanie, więc coś w rodzaju tego paskudnego robala może usunąć całą tabelę.Gdyby użyć PDO mysqli, nazwa użytkownika byłaby ostatecznie
"enhzflep); drop table users"
.Zobacz bobby-tables.com .
źródło
The old functions will allow executing of multiple statements per query
- nie, nie będą. Tego rodzaju wstrzykiwanie nie jest możliwe w przypadku ext / mysql - jedynym sposobem takiego wstrzykiwania jest możliwe w PHP i MySQL, gdy używa się MySQLi imysqli_multi_query()
funkcji. Rodzaj zastrzyku, który jest możliwy przy użyciu ext / mysql i ciągów bezkształtnych, to takie rzeczy, jak' OR '1' = '1
wyodrębnianie danych z bazy danych, która nie miała być dostępna. W niektórych sytuacjach możliwe jest wstrzykiwanie pod-zapytań, jednak nadal nie jest możliwe zmodyfikowanie bazy danych w ten sposób.Ta odpowiedź została napisana, aby pokazać, jak banalne jest omijanie źle napisanego kodu sprawdzania poprawności użytkownika PHP, jak (i za pomocą czego) te ataki działają oraz jak zastąpić stare funkcje MySQL bezpieczną przygotowaną instrukcją - i, w zasadzie, dlaczego użytkownicy StackOverflow (prawdopodobnie z dużą liczbą powtórzeń) szczekają na nowych użytkowników, zadając pytania w celu ulepszenia kodu.
Po pierwsze, proszę utworzyć tę testową bazę danych mysql (nazwałem mój prep):
Po wykonaniu tej czynności możemy przejść do naszego kodu PHP.
Załóżmy, że następujący skrypt to proces weryfikacji dla administratora w witrynie (uproszczony, ale działa, jeśli skopiujesz go i użyjesz do testowania):
Na pierwszy rzut oka wydaje się wystarczająca.
Użytkownik musi wprowadzić login i hasło, prawda?
Genialny, nie wpisuj:
i prześlij to.
Dane wyjściowe są następujące:
Wspaniały! Działając zgodnie z oczekiwaniami, spróbujmy teraz rzeczywistej nazwy użytkownika i hasła:
Niesamowity! Cześć piątki, kod poprawnie zweryfikował administratora. Jest idealny!
Cóż, nie za bardzo. Powiedzmy, że użytkownik jest sprytną małą osobą. Powiedzmy, że ta osoba to ja.
Wpisz następujące dane:
Wyjście to:
Gratulacje, właśnie pozwoliłeś mi wpisać twoją super-chronioną sekcję tylko dla administratorów, a ja wpisałem fałszywą nazwę użytkownika i fałszywe hasło. Poważnie, jeśli mi nie wierzysz, utwórz bazę danych z podanym przeze mnie kodem i uruchom ten kod PHP - który na pierwszy rzut oka NAPRAWDĘ wydaje się raczej ładnie weryfikować nazwę użytkownika i hasło.
Tak więc, w odpowiedzi, DLACZEGO JESTEŚ WKRĘTY.
Zobaczmy więc, co poszło nie tak i dlaczego właśnie dostałem się do twojej jaskini superadministratora. Zgadłem i założyłem, że nie byłeś ostrożny ze swoimi danymi wejściowymi i po prostu przekazałem je bezpośrednio do bazy danych. Skonstruowałem dane wejściowe w taki sposób, aby ZMIENIĆ zapytanie, które faktycznie uruchomiłeś. Więc co to miało być i czym się to stało?
To jest zapytanie, ale kiedy zamieniamy zmienne na rzeczywiste dane wejściowe, których użyliśmy, otrzymujemy:
Zobacz, jak skonstruowałem moje „hasło”, aby najpierw zamknęło pojedynczy cytat wokół hasła, a następnie wprowadzić zupełnie nowe porównanie? Następnie dla bezpieczeństwa dodałem kolejny „ciąg”, aby pojedynczy cytat został zamknięty zgodnie z oczekiwaniami w kodzie, który pierwotnie mieliśmy.
Nie chodzi tu jednak o ludzi krzyczących na ciebie, chodzi o pokazanie, jak zwiększyć bezpieczeństwo kodu.
Okej, więc co poszło nie tak i jak możemy to naprawić?
Jest to klasyczny atak typu SQL injection. Jeden z najprostszych pod tym względem. W skali wektorów ataku jest to maluch atakujący czołg - i wygrywający.
Jak więc chronimy waszą świętą sekcję administracyjną i sprawiamy, że jest ładna i bezpieczna? Pierwszą rzeczą do zrobienia będzie zaprzestanie używania tych naprawdę starych i przestarzałych
mysql_*
funkcji. Wiem, że obejrzałeś samouczek, który znalazłeś w Internecie i działa, ale jest stary, jest przestarzały i w ciągu kilku minut właśnie go przekroczyłem, nie tracąc nawet potu.Teraz masz lepsze opcje korzystania z mysqli_ lub PDO . Osobiście jestem wielkim fanem PDO, więc w dalszej części tej odpowiedzi będę używać PDO. Są plusy i minusy, ale osobiście uważam, że pro znacznie przewyższają minusy. Jest przenośny w wielu silnikach baz danych - bez względu na to, czy korzystasz z MySQL, Oracle czy po prostu coś cholernego - po prostu zmieniając ciąg połączenia, ma wszystkie fantazyjne funkcje, których chcemy używać, i jest ładny i czysty. Lubię czyste.
Teraz spójrzmy jeszcze raz na ten kod, tym razem napisany przy użyciu obiektu PDO:
Główne różnice polegają na tym, że nie ma już żadnych
mysql_*
funkcji. Wszystko to odbywa się za pośrednictwem obiektu PDO, po drugie, używa przygotowanej instrukcji. A teraz, o co pytasz? Jest to sposób na poinformowanie bazy danych przed uruchomieniem zapytania, jakie to zapytanie zostanie uruchomione. W tym przypadku mówimy do bazy danych: „Cześć, zamierzam uruchomić instrukcję select, szukając identyfikatora, identyfikatora użytkownika i przekazania od użytkowników tabeli, w których identyfikator użytkownika jest zmienną, a hasło jest również zmienną.”.Następnie w instrukcji wykonania przekazujemy do bazy danych tablicę ze wszystkimi zmiennymi, których teraz oczekuje.
Rezultaty są fantastyczne. Spróbujmy jeszcze raz te kombinacje nazwy użytkownika i hasła:
Użytkownik nie został zweryfikowany. Niesamowite.
Co powiesz na:
Och, właśnie się trochę podnieciłem, zadziałało: czek mija. Mamy zweryfikowanego administratora!
Teraz spróbujmy danych, które wprowadziłby sprytny facet, aby ominąć nasz mały system weryfikacji:
Tym razem otrzymujemy:
Właśnie dlatego jesteś krzyczany, gdy piszesz pytania - to dlatego, że ludzie widzą, że Twój kod można ominąć nawet bez próby. Proszę, skorzystaj z tego pytania i odpowiedzi, aby ulepszyć swój kod, aby był bardziej bezpieczny i korzystać z aktualnych funkcji.
Na koniec nie oznacza to, że jest to kod PERFECT. Jest wiele innych rzeczy, które możesz zrobić, aby to poprawić, na przykład użyj zaszyfrowanych haseł, upewnij się, że podczas przechowywania wrażliwych informacji w bazie danych nie przechowujesz ich jako zwykłego tekstu, masz wiele poziomów weryfikacji - ale tak naprawdę, jeśli po prostu zmieniasz swój stary podatny na wstrzykiwanie kod na ten, będziesz DOBRZE na drodze do pisania dobrego kodu - a fakt, że zaszedłeś tak daleko i wciąż czytasz, daje mi poczucie nadziei, że nie tylko zaimplementujesz ten typ kodu podczas pisania stron internetowych i aplikacji, ale możesz wyjść i zbadać inne rzeczy, o których właśnie wspomniałem - i więcej. Napisz najlepszy możliwy kod, a nie najbardziej podstawowy, który ledwo funkcjonuje.
źródło
mysql_*
sam w sobie nie jest niebezpieczny, ale promuje niepewny kod poprzez złe samouczki i brak odpowiedniej instrukcji przygotowującej API.Rozszerzenie MySQL jest najstarszym z trzech i było oryginalnym sposobem, w jaki programiści używali komunikacji z MySQL. To rozszerzenie jest obecnie przestarzałe na korzyść dwóch pozostałych alternatyw z powodu ulepszeń wprowadzonych w nowszych wersjach zarówno PHP, jak i MySQL.
MySQLi to „ulepszone” rozszerzenie do pracy z bazami danych MySQL. Korzysta z funkcji, które są dostępne w nowszych wersjach serwera MySQL, udostępnia deweloperowi zarówno interfejs zorientowany na funkcje, jak i zorientowany obiektowo oraz robi kilka innych fajnych rzeczy.
PDO oferuje interfejs API, który konsoliduje większość funkcji, które wcześniej były rozproszone w głównych rozszerzeniach dostępu do bazy danych, tj. MySQL, PostgreSQL, SQLite, MSSQL itp. Interfejs udostępnia obiekty wysokiego poziomu dla programisty do pracy z połączeniami z bazami danych, zapytaniami i zestawy wyników i sterowniki niskiego poziomu wykonują komunikację i obsługę zasobów z serwerem bazy danych. Dużo dyskusji i pracy dotyczy PDO i jest uważana za odpowiednią metodę pracy z bazami danych w nowoczesnym, profesjonalnym kodzie.
źródło
Uważam, że powyższe odpowiedzi są bardzo długie, więc podsumowując:
Źródło: Przegląd MySQLi
Jak wyjaśniono w powyższych odpowiedziach, alternatywą dla mysql są mysqli i PDO (PHP Data Objects).
Zarówno MySQLi, jak i PDO zostały wprowadzone w PHP 5.0, natomiast MySQL został wprowadzony przed PHP 3.0. Należy zauważyć, że MySQL jest zawarty w PHP5.x, ale jest przestarzały w późniejszych wersjach.
źródło
Możliwe jest zdefiniowanie prawie wszystkich
mysql_*
funkcji za pomocą mysqli lub PDO. Wystarczy dołączyć je do starej aplikacji PHP, a będzie działać na PHP7. Moje rozwiązanie tutaj .źródło
Funkcje, które są podobne do tego
mysql_connect()
,mysql_query()
są poprzednimi wersjami PHP tj. (PHP 4) i teraz nie są używane.Są one zastępowane przez
mysqli_connect()
,mysqli_query()
podobnie jak w najnowszym PHP5.To jest przyczyna błędu.
źródło
MySQL jest przestarzałe w PHP 5.5.0 i usunięte w PHP 7.0.0. W przypadku dużej i starej aplikacji trudno jest wyszukać i zastąpić każdą funkcję.
Możemy korzystać z funkcji MySQL, tworząc funkcję otoki dla każdego uruchomionego kodu. Kliknij tutaj
źródło
Funkcje mysql_ * były przestarzałe (od PHP 5.5 ), ponieważ opracowano lepsze funkcje i struktury kodu. Fakt, że funkcja została wycofana, oznacza, że nie będzie się więcej starać o jej poprawienie pod względem wydajności i bezpieczeństwa, co oznacza, że jest mniej odporna na przyszłość .
Jeśli potrzebujesz więcej powodów:
źródło