Dlaczego nie powinienem używać funkcji mysql * w PHP?

chcesz poprawić ten post? podaj szczegółowe odpowiedzi na to pytanie, w tym cytaty i wyjaśnienie, dlaczego Twoja odpowiedź jest prawidłowa. Odpowiedzi bez wystarczającej ilości szczegółów mogą być edytowane lub usuwane.

Jakie są TECHNICZNE powody, dla których nie należy używać funkcji mysql_*? (np. mysql_query(), mysql_connect() lub mysql_real_escape_string())?

Dlaczego powinienem używać czegoś innego, nawet jeśli działa na mojej stronie?

Jeśli nie działają na mojej stronie, dlaczego dostaję błędy typu

Warning: mysql_connect (): No such file or directory

 2557
Author: Dharman, 2012-10-12

12 answers

Rozszerzenie MySQL:

  • nie jest w trakcie aktywnego rozwoju
  • jest oficjalnie przestarzałe od wersji PHP 5.5 (wydany w czerwcu 2013).
  • został usunięto całkowicie od wersji PHP 7.0 (wydany w grudniu 2015)
    • oznacza to, że od dnia 31 Dec 2018 nie istnieje w żadnej obsługiwanej wersji PHP. Jeśli używasz wersji PHP, która ją obsługuje, używasz wersji, która nie ma problemów z bezpieczeństwem naprawione.
  • Brak interfejsu OO
  • nie obsługuje:
    • nieblokujące, asynchroniczne zapytania
    • Prepared statements or parameterized queries
    • procedury przechowywane
    • Wiele Wypowiedzi
    • transakcje
    • Metoda uwierzytelniania" nowego " hasła (domyślnie włączona w MySQL 5.6; wymagana w 5.7)
  • każda nowa funkcjonalność w MySQL 5.1 lub nowszym

Ponieważ jest przestarzały, korzystanie z niego sprawia, że Twój kod jest mniej przyszłościowy.

Brak obsługi gotowych 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 usuwanie ich za pomocą oddzielnego wywołania funkcji.

Zobacz Porównanie rozszerzeń SQL.

 2133
Author: Quentin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-21 10:15:53

PHP oferuje trzy różne API do połączenia z MySQL. Są to mysql(usunięte z PHP 7), mysqli, oraz PDO rozszerzenia.

Funkcje mysql_* były kiedyś bardzo popularne, ale ich użycie nie jest już zachęcane. Zespół dokumentacji omawia sytuację bezpieczeństwa bazy danych, a częścią tego jest edukowanie użytkowników, aby odeszli od powszechnie używanego rozszerzenia ext / mysql (sprawdź php.wewnętrzne: deprecating ext / mysql).

I późniejszy zespół programistów PHP podjął decyzję o wygenerowaniu E_DEPRECATED błędy podczas łączenia się użytkowników z MySQL, czy poprzez mysql_connect(), mysql_pconnect() lub domyślną funkcjonalność połączenia wbudowaną w ext/mysql.

ext/mysql na oficjalnie przestarzałe od PHP 5.5 i został usunięte z PHP 7.

# Patrz Czerwony Pudełko?

Kiedy wejdziesz na którąkolwiek stronę podręcznika funkcji mysql_*, zobaczysz czerwone okienko, wyjaśniające, że nie powinna być już używana.

Dlaczego


Odejście od ext/mysql to nie tylko bezpieczeństwo, ale także dostęp do wszystkich funkcji bazy danych MySQL.

ext/mysql został zbudowany dla MySQL 3.23 i od tego czasu dostał tylko niewiele dodatków, zachowując głównie kompatybilność z tą starą wersją, co sprawia, że kod jest nieco trudniejszy aby utrzymać. Brakujące funkcje, które nie są obsługiwane przez ext/mysql obejmują: (z podręcznika PHP).

Powód, aby nie używać mysql_* funkcji :

  • brak aktywnego rozwoju
  • usunięty jako of PHP 7
  • brak interfejsu OO
  • Nie obsługuje nieblokujących, asynchronicznych zapytań]}
  • nie obsługuje przygotowanych poleceń ani parametryzowanych zapytań
  • nie obsługuje procedur składowanych
  • nie obsługuje wielu wyrażeń
  • nie obsługuje transakcji
  • nie obsługuje wszystkich funkcji w MySQL 5.1

Powyższy cytat pochodzi ze strony: odpowiedź

Brak obsługi gotowych instrukcji jest szczególnie ważny, ponieważ zapewniają one jaśniejszą, mniej podatną na błędy metodę ucieczki i cytowania danych zewnętrznych niż ręczne usuwanie ich za pomocą oddzielnego wywołania funkcji.

Zobacz porównanie rozszerzeń SQL.


Tłumiące Ostrzeżenia deprecacyjne

Podczas konwersji kodu na MySQLi/PDO, E_DEPRECATED błędy można tłumić ustawiając error_reporting W php.ini aby wykluczyć E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

Zauważ, że spowoduje to ukrycie innych ostrzeżeń o deprecjacji, które jednak mogą dotyczyć rzeczy innych niż MySQL. (z podręcznika PHP)

Artykuł PDO vs. MySQLi: z czego należy korzystać? przez Dejan Marjanović pomoże Ci wybrać.

A lepszym sposobem jest PDO, a teraz piszę prosty PDO tutorial.


Prosty i krótki poradnik PDO


P. pierwsze pytanie w mojej głowie brzmiało: Co to jest "PDO"?

A. "PDO – PHP Data Objects – jest warstwą dostępu do bazy danych zapewniającą jednolitą metodę dostępu do wielu baz danych."

alt text


Łączenie z MySQL

W PHP 5.5 i nowszych PHP 5.5 i nowszych PHP 5.5 i nowszych PHP 5.5 i nowszych PHP 5.5 i nowszych PHP 5.5 i nowszych PHP 5.5 i nowszych PHP 5.5 i nowszych PHP 5.5 i nowszych PHP 5.5 i nowszych]}
$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

Z PDO: wystarczy utworzyć nowy obiekt PDO. Konstruktor przyjmuje parametry do określenia źródła bazy danych PDO ' konstruktor najczęściej pobiera cztery parametry, które są DSN (nazwa źródła danych) i opcjonalnie username, password.

Tutaj myślę, że znasz wszystkie oprócz DSN; to jest nowość w PDO. A {[53] } jest w zasadzie ciągiem opcji, które mówią PDO jakiego sterownika użyć i szczegóły połączenia. Aby uzyskać więcej informacji, sprawdź PDO MySQL DSN .

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

Uwaga: można również użyć charset=UTF-8, ale czasami powoduje to błąd, więc lepiej jest użyć utf8.

Jeśli wystąpi jakiś błąd połączenia, rzuci PDOException obiekt, który może zostać złapany do obsługi Exception dalej.

Dobra lektura: połączenia i zarządzanie połączeniami ¶

Możesz również przekazać w kilku opcjach Sterownika jako tablicę do czwartego parametru. Polecam podanie parametru, który umieszcza PDO w trybie wyjątku. Ponieważ niektóre sterowniki PDO nie obsługują natywnych instrukcji prepare, PDO wykonuje emulację prepare. Umożliwia również ręczne włączenie tej emulacji. Aby użyć natywnych instrukcji przygotowanych po stronie serwera, należy jawnie ustawić false.

Drugim jest wyłączenie emulacji prepare, która jest domyślnie włączona w sterowniku MySQL, ale prepare emulacja powinna być wyłączona, aby bezpiecznie używać PDO.

Wyjaśnię później, dlaczego przygotowanie emulacji powinno być wyłączone. Aby znaleźć powód, sprawdź ten post.

Jest użyteczny tylko wtedy, gdy używasz starej wersji MySQL, której nie polecam.

Poniżej znajduje się przykład, jak możesz to zrobić:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password',
              array(PDO::ATTR_EMULATE_PREPARES => false,
              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

Czy możemy ustawić atrybuty po budowie PDO?

Tak , możemy również ustawić pewne atrybuty po budowie PDO metodą setAttribute:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Obsługa Błędów


Obsługa błędów jest znacznie łatwiejsza w PDO niż mysql_*.

Powszechną praktyką przy użyciu mysql_* jest:

//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die() to nie jest dobry sposób, aby poradzić sobie z błędem, ponieważ nie możemy obsłużyć rzeczy w die. To po prostu zakończyć skrypt nagle, a następnie echo błąd na ekranie, który zwykle nie chcesz, aby pokazać użytkownikom końcowym, i niech krwawe hakerzy odkryć swój schemat. Alternatywnie wartości zwracane funkcji mysql_* mogą być często używane w połączenie z mysql_error () w celu obsługi błędów.

PDO oferuje lepsze rozwiązanie: wyjątki. Wszystko co robimy z PDO powinno być zawinięte w try-catch blok. Możemy wymusić PDO w jeden z trzech trybów błędu, 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 jak mysql_*, gdzie musisz sprawdzić każdy wynik, a następnie spojrzeć na $db->errorInfo();, aby uzyskać błąd szczegóły.
  • PDO::ERRMODE_WARNING podnieść E_WARNING. (Ostrzeżenia o czasie wykonywania (błędy inne niż fatalne). Wykonanie skryptu nie jest wstrzymane.)
  • PDO::ERRMODE_EXCEPTION: wyrzuć wyjątki. Stanowi błąd zgłoszony przez PDO. Nie powinieneś wyrzucać PDOException z własnego kodu. Zobacz wyjątki aby uzyskać więcej informacji na temat WYJĄTKÓW w PHP. Zachowuje się bardzo podobnie do or die(mysql_error());, kiedy nie jest złapany. Ale w przeciwieństwie do or die(), {[62] } można złapać i obchodzić się z gracją, jeśli zdecydujesz się zrobić więc.

Dobra lektura :

Jak:

$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

And you can wrap it in try-catch, jak poniżej:

try {
    //Connect as appropriate as above
    $db->query('hi'); //Invalid query!
} 
catch (PDOException $ex) {
    echo "An Error occured!"; //User friendly message/message you want to show to user
    some_logging_function($ex->getMessage());
}

Nie musisz sobie radzić z try-catch natychmiast. Możesz go złapać w dowolnym momencie, ale zdecydowanie zalecam użycie try-catch. Może również sprawić, że więcej sensu, aby złapać go poza funkcją, która wywołuje PDO rzeczy:

function data_fun($db) {
    $stmt = $db->query("SELECT * FROM table");
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

//Then later
try {
    data_fun($db);
}
catch(PDOException $ex) {
    //Here you can handle error and show message/perform action you want.
}

Również, można obsługiwać przez or die() lub możemy powiedzieć, jak mysql_*, Ale To będzie bardzo zróżnicowane. Możesz ukryć niebezpieczne komunikaty o błędach w produkcji, obracając display_errors off i po prostu czytając dziennik błędów.

Teraz, po przeczytaniu wszystkich rzeczy powyżej, prawdopodobnie myślisz: co to do cholery jest, że kiedy chcę po prostu zacząć Pochylać proste SELECT, INSERT, UPDATE, albo DELETE Oświadczenia? Nie. Uwaga, zaczynamy:


Wybór Danych

PDO wybierz obraz

Więc to, co robisz wmysql_* jest:

<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());

$num_rows = mysql_num_rows($result);

while($row = mysql_fetch_assoc($result)) {
    echo $row['field1'];
}

Teraz w PDO możesz to zrobić tak:

<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

Lub

<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

//Use $results

Uwaga: jeśli używasz metody jak poniżej (query()), metoda ta zwraca obiekt PDOStatement. Więc jeśli chcesz pobrać wynik, użyj go jak powyżej.

<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

W danych PDO uzyskuje się je za pomocą ->fetch(), metody twojego Oświadczenia uchwyt. Przed wywołaniem fetch najlepszym rozwiązaniem byłoby poinformowanie PDO, w jaki sposób chcesz pobrać dane. W poniższej sekcji wyjaśniam to.

Tryby Pobierania

Zwróć uwagę na użycie PDO::FETCH_ASSOC w kodzie fetch() i fetchAll() powyżej. To mówi PDO, aby zwracała wiersze jako tablicę asocjacyjną z nazwami pól jako kluczami. Istnieje wiele innych trybów pobierania, które wyjaśnię jeden po drugim.

Przede wszystkim wyjaśniam jak wybrać fetch tryb:

 $stmt->fetch(PDO::FETCH_ASSOC)

W powyższym używam fetch(). Można również użyć:

Teraz przychodzę do tryb pobierania:

  • PDO::FETCH_ASSOC: zwraca tablicę indeksowaną przez nazwę kolumny jako zwróconą w zestawie wyników
  • PDO::FETCH_BOTH (domyślnie): zwraca tablicę zindeksowaną zarówno nazwą kolumny, jak i 0-indeksowanym numerem kolumny zwróconym w zestawie wyników

Jest jeszcze więcej możliwości! Przeczytaj o nich wszystkich w PDOStatement Pobierz dokumentację..

Liczba wierszy :

Zamiast używać mysql_num_rows aby uzyskać liczbę zwróconych wiersze, można uzyskać PDOStatement i zrobić rowCount(), jak:

<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

Pobieranie Ostatnio wstawionego ID

<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();

Wstawianie i aktualizowanie lub usuwanie stwierdzeń

Wstaw i zaktualizuj obraz PDO

To, co robimy w mysql_* funkcji jest:

<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

I w pdo to samo można zrobić przez:

<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;

W powyższym zapytaniu PDO::exec wykonaj polecenie SQL i zwróci liczbę wierszy, których to dotyczy.

Insert a delete zostanie omówione później.

Powyższa metoda jest przydatna tylko wtedy, gdy nie używasz zmiennej w zapytaniu. Ale kiedy trzeba użyć zmiennej w zapytaniu, nigdy nie próbuj jak powyżej i tam dla oświadczenie przygotowane lub oświadczenie parametryzowane jest.


Gotowe Oświadczenia

Q. Co to jest przygotowane oświadczenie i dlaczego ich potrzebuję?
A. a przygotowany instrukcja jest wstępnie skompilowaną instrukcją SQL, która może być wykonywana wiele razy, wysyłając tylko dane do serwera.

Typowy obieg pracy przy użyciu przygotowanego oświadczenia jest następujący (cytowany z Wikipedii trzy 3 punkty):

  1. Prepare: 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 bind zmienne (oznaczone ? poniżej):

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. DBMS analizuje, kompiluje i wykonuje optymalizację zapytań na szablonie instrukcji i przechowuje wynik bez jego wykonywania.

  3. Execute: w późniejszym czasie aplikacja dostarcza (lub wiąże) wartości dla parametrów, a DBMS wykonuje instrukcję (ewentualnie zwraca wynik). Aplikacja może wykonać polecenie tyle razy, ile chce, z różnymi wartości. W tym przykładzie może podać "chleb" dla pierwszego parametru i 1.00 dla drugiego parametru.

Możesz użyć przygotowanej instrukcji, włączając placeholdery do swojego SQL. Istnieją zasadniczo trzy bez symboli zastępczych (nie próbuj tego ze zmienną its powyżej), jeden z nienazwanymi symbolami zastępczymi i jeden z nazwanymi symbolami zastępczymi.

Q. więc teraz, jak nazywa się placeholdery i jak ich używać?
A. O Nazwie zastępcze. Używaj nazw opisowych poprzedzonych dwukropkiem, zamiast znaków zapytania. Nie dbamy o pozycję/kolejność wartości w nazwie posiadacza miejsca:

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

Możesz również powiązać używając tablicy execute:

<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

Kolejną miłą cechą 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:

class person {
    public $name;
    public $add;
    function __construct($a,$b) {
        $this->name = $a;
        $this->add = $b;
    }

}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);

P. czym są nienazwane placeholdery i jak ich używać?
A. weźmy przykład:

<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();

I

$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));

W powyższym, można zobaczyć te ? zamiast nazwy jak w posiadacza nazwy miejsce. Teraz w pierwszym przykładzie przypisujemy zmienne do różnych elementów zastępczych ($stmt->bindValue(1, $name, PDO::PARAM_STR);). Następnie przypisujemy wartości do tych elementów zastępczych i wykonujemy instrukcję. W drugim przykładzie pierwszy element tablicy przechodzi do pierwszego ?, a drugi do drugiego ?.

Uwaga: w nienazwane placeholdery musimy zadbać o właściwą kolejność elementów w tablicy, które przekazujemy do metody PDOStatement::execute().


SELECT, INSERT, UPDATE, DELETE przygotowane queries

  1. SELECT:

    $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
    $stmt->execute(array(':name' => $name, ':id' => $id));
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
  2. INSERT:

    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
    $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
    
  3. DELETE:

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
    
  4. UPDATE:

    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
    $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();
    

Uwaga:

Jednakże PDO i / lub MySQLi nie są całkowicie bezpieczne. Sprawdź odpowiedź czy deklaracje PDO prepared są wystarczające, aby zapobiec SQL injection? przez ircmaxell . W 1996 roku, w wyniku zamachu stanu, w 1997 roku, doszło do zamachu stanu.]}

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));
 1308
Author: NullPoiиteя,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-13 01:03:03

Najpierw zacznijmy od standardowego komentarza, który dajemy każdemu:

Proszę, nie używaj funkcji mysql_* w nowym kodzie. Nie są już utrzymywane i są oficjalnie przestarzałe . Zobacz też red box? Dowiedz się o przygotowane Oświadczenia zamiast tego użyj PDOlub MySQLi - ten artykuł pomoże Ci zdecydować, który. Jeśli wybierzesz PDO, tutaj jest dobry tutorial .

Przejdźmy przez to, zdanie po zdaniu, i wyjaśnijmy:
  • Nie są już utrzymywane i są oficjalnie przestarzałe

    Oznacza to, że społeczność PHP stopniowo wycofuje wsparcie dla tych bardzo starych funkcji. Prawdopodobnie nie istnieją w przyszłej (najnowszej) wersji PHP! Dalsze korzystanie z tych funkcji może złamać twój kod w (nie tak) dalekiej przyszłości.

    Nowość! - ext / mysql jest teraz oficjalnie przestarzałe od PHP 5.5!

    Nowsze! ext / mysql został usunięty w PHP 7.

  • Zamiast tego powinieneś dowiedzieć się o przygotowanych wypowiedziach

    mysql_* rozszerzenie nie obsługuje prepared statements , co jest (między innymi) bardzo skutecznym środkiem przeciwdziałania SQL Injection. Naprawiono bardzo poważną lukę w aplikacjach zależnych od MySQL, która umożliwia atakującym uzyskanie dostęp do skryptu i wykonanie wszelkich możliwych zapytań w bazie danych.

    Aby uzyskać więcej informacji, zobacz Jak mogę zapobiec SQL injection w PHP?

  • Widzisz czerwone pudełko?

    Kiedy przejdziesz do dowolnej strony podręcznika funkcji mysql, zobaczysz czerwone pole wyjaśniające, że nie powinna być już używana.

  • Użyj PDO lub MySQLi

    Istnieją lepsze, bardziej wytrzymałe i dobrze zbudowane alternatywy, PDO-PHP Obiekt bazy danych, który oferuje kompletne podejście OOP do interakcji z bazami danych oraz MySQLi, co jest ulepszeniem specyficznym dla MySQL.
 306
Author: Madara's Ghost,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-25 17:58:46

Łatwość użycia

Analityczne i syntetyczne powody były już wymienione. Dla nowicjuszy istnieje bardziej znacząca zachęta do zaprzestania używania datowanych funkcji mysql_.

Współczesne interfejsy API baz danych są po prostu łatwiejsze w użyciu.

To głównie powiązane parametry , które mogą uprościć kod. I z doskonałe tutoriale (jak widać powyżej) przejście do PDO nie jest zbyt uciążliwe.

Przepisywanie a większa baza kodu na raz wymaga jednak czasu. Raison d ' être dla tej pośredniej alternatywy:

Równoważne funkcje pdo_ * w miejsce mysql_ *

Za pomocą pdo_mysql.php> możesz przełączyć się ze starych funkcji mysql_ z minimalnym wysiłkiem . Dodaje ona otoczki funkcji pdo_, które zastępują ich odpowiedniki mysql_.

  1. Po prostu include_once("pdo_mysql.php"); w każdym skrypcie wywołania, który musi współdziałać z bazą danych.

  2. Usuń mysql_ prefiks funkcji wszędzie i zastąp go 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()
    • i tak dalej...

  3. Twój kod będzie działał podobnie i nadal będzie wyglądał tak samo:

    include_once("pdo_mysql.php"); 
    
    pdo_connect("localhost", "usrABC", "pw1234567");
    pdo_select_db("test");
    
    $result = pdo_query("SELECT title, html FROM pages");  
    
    while ($row = pdo_fetch_assoc($result)) {
        print "$row[title] - $row[html]";
    }
    

Et voilà.
Twój kod to używając PDO.
Teraz nadszedł czas, aby rzeczywiście wykorzystać to.

Powiązane parametry mogą być łatwe w użyciu

Po prostu potrzebujesz mniej nieporęcznego API.

pdo_query() dodaje bardzo łatwą obsługę związanych parametrów. Konwersja starego kodu jest prosta:

Przenieś swoje zmienne Z ciągu SQL.

  • dodaj je jako parametry funkcji rozdzielane przecinkami do pdo_query().
  • umieść znaki zapytania ? jako symbole zastępcze, gdzie zmienne były wcześniej.
  • pozbądź się ' pojedynczych cudzysłowów, które wcześniej zawierały wartości/zmienne łańcuchowe.

Zaleta staje się bardziej oczywista dla dłuższego kodu.

Często zmienne łańcuchowe są nie tylko interpolowane do SQL, ale konkatenowane z wywołaniami ucieczki pomiędzy nimi.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
   pdo_real_escape_string($title) . "' AND user <> '" .
   pdo_real_escape_string($root) . "' ORDER BY date")

Z ? placeholderami zastosowanymi nie musisz się tym przejmować:

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

Pamiętaj, że pdo_ * nadal pozwala albo .
Po prostu nie escape zmiennej i bind go w tym samym zapytaniu.

  • funkcja zastępcza jest dostarczana przez prawdziwe PDO za nim.
  • w ten sposób również dozwolone :named listy zastępcze później.

Co ważniejsze możesz przekazać zmienne $_REQUEST[] bezpiecznie za każdym zapytaniem. Po przesłaniu<form> pól dokładnie dopasowuje się do struktury bazy danych, jest jeszcze krótszy:

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
Tyle prostoty. Ale wróćmy jeszcze do przepisywania porad i technicznych powody, dla których możesz chcieć pozbyć się mysql_ i ucieczka.

Napraw lub usuń Oldschool sanitize() function

Gdy już przekonwertujesz wszystkie mysql_ wywołania pdo_query z powiązanymi paramami, usuwają wszystkie zbędne wywołania pdo_real_escape_string.

W szczególności powinieneś naprawić dowolne funkcje sanitize lub clean lub filterThis lub clean_data reklamowane przez datowane tutoriale w jednej lub drugiej formie:

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

Najbardziej rażącym błędem jest brak dokumentacji. Co ważniejsze, kolejność filtrowania była dokładnie w złej kolejności.

  • Prawidłową kolejnością byłoby: {[59] } jako najskrytsze wywołanie, potem trim, potem strip_tags, htmlentities dla kontekstu wyjściowego i tylko na końcu _escape_string jako jego aplikacja powinna bezpośrednio poprzedzać przeplatanie się SQL.

  • Ale jako pierwszy krok po prostu pozbądź się _real_escape_string sprawdzam.

  • Być może będziesz musiał zachować resztę funkcji sanitize() na razie, jeśli twoja baza danych i przepływ aplikacji oczekują ciągów HTML-context-safe. Dodaj komentarz, że od tej pory stosuje się tylko HTML.

  • Obsługa łańcuchów / wartości jest przekazywana do PDO i jego sparametryzowanych instrukcji.

  • Jeśli była wzmianka o stripslashes() w twojej funkcji dezynfekcji, może to wskazywać na wyższy poziom nadzoru.

    Historyczna notka na magic_quotes. ta funkcja jest słusznie przestarzała. Jest to jednak często błędnie przedstawiane jako nieudana funkcja zabezpieczeń. Ale magic_quotes są tak samo nieudaną funkcją bezpieczeństwa, jak piłki tenisowe nieudane jako źródło pożywienia. To po prostu nie było ich celem.

    Oryginalna implementacja w PHP2/FI wprowadziła ją wprost z " cudzysłowy będą automatycznie unikane, co ułatwi przekazywanie danych formularza bezpośrednio do zapytań msql ". W szczególności był przypadkowo bezpieczny w użyciu z mSQL, ponieważ obsługiwał tylko ASCII.
    Następnie PHP3 / Zend ponownie wprowadził magic_quotes dla MySQL i błędnie go udokumentował. Ale pierwotnie była to tylko wygoda funkcja , nie jest przeznaczona dla bezpieczeństwa.

Jak przygotowane wypowiedzi różnią się

Kiedy mieszasz zmienne łańcuchowe do zapytań SQL, nie tylko staje się to bardziej skomplikowane dla Ciebie do naśladowania. Jest to również dodatkowy wysiłek dla MySQL, aby ponownie segregować kod i dane.

Zastrzyki SQL są po prostu wtedy, gdy dane spływają do kodu kontekstu. Serwer bazy danych nie może później wykryć, gdzie PHP pierwotnie przyklejał zmienne klauzule zapytania.

Z parametrami związanymi oddzielasz kod SQL i wartości SQL-context w kodzie PHP. Ale to nie zostanie tasowane ponownie za kulisami (z wyjątkiem PDO:: EMULATE_PREPARES). Twoja baza danych otrzymuje niezarejestrowane polecenia SQL i wartości zmiennych 1:1.

Podczas gdy ta odpowiedź podkreśla, że należy dbać o zalety czytelności z upuszczania mysql_. Okazjonalnie występuje też przewaga wydajnościowa (powtarzające się wstawki z tylko różne wartości) ze względu na to widoczne i techniczne oddzielenie danych/kodu.

Uważaj, że Wiązanie parametrów nadal nie jest magicznym, jednorazowym rozwiązaniem przeciwko wszystkim iniekcjom SQL. Obsługuje najczęściej używane Dane / wartości. Ale nie może dodawać do białej listy nazw kolumn / identyfikatorów tabeli, pomóc w dynamicznej budowie klauzul lub po prostu listy wartości tablicy.

Hybrydowe stosowanie PDO

Te pdo_* funkcje owijania tworzą przyjazne dla kodowania API stop-gap. (Jest ładna wiele, co MYSQLI mogłoby być, gdyby nie przesunięcie podpisu funkcji idiosynkratycznej). W większości przypadków ujawniają również prawdziwe PDO.
Przepisywanie nie musi kończyć się na użyciu nowych nazw funkcji pdo_. Można po kolei przejść każde wywołanie pdo_query () do zwykłego wywołania $pdo->prepare ()->execute ().

Najlepiej jednak zacząć od uproszczenia. Na przykład wspólny wynik pobierania:

$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {

Można zastąpić tylko foreach iteracja:

foreach ($result as $row) {

Albo jeszcze lepiej bezpośrednie i pełne pobieranie tablicy:

$result->fetchAll();

W większości przypadków otrzymasz więcej pomocnych ostrzeżeń niż PDO lub mysql_, które zazwyczaj dostarczają po nieudanych zapytaniach.

Inne opcje

Więc mam nadzieję, że to wizualizowało niektóre } praktyczne powody i wartą ścieżkę do spadku mysql_.

Samo przejście na pdo nie do końca to wyciąć. pdo_query() jest również tylko frontend na nim.

Unless you również wprowadzić Wiązanie parametrów lub można wykorzystać coś innego z ładniejszego API, to bezsensowny przełącznik. Mam nadzieję, że jest to na tyle proste, aby nie zniechęcać nowych. (Edukacja zwykle działa lepiej niż prohibicja.)

Chociaż kwalifikuje się do kategorii najprostszych rzeczy, które mogłyby działać, jest to również bardzo eksperymentalny kod. Napisałem to w weekend. Istnieje jednak mnóstwo alternatyw. Po prostu google dla bazy PHP abstrakcja i przejrzyj 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ć maperów, takich jak Paris/Idiorm. Tak jak nikt już nie używa mdłego DOM w JavaScript, nie musisz niańczyć surowego interfejsu bazy danych w dzisiejszych czasach.

 222
Author: mario,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:03:09

Funkcje mysql_:

  1. są nieaktualne-nie są już utrzymywane
  2. nie pozwalaj na łatwe przenoszenie się do innego zaplecza bazy danych
  3. nie wspieraj przygotowanych wypowiedzi, stąd
  4. zachęcanie programistów do używania konkatenacji do budowania zapytań, co prowadzi do luk w zabezpieczeniach SQL injection]}
 152
Author: Alnitak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-26 16:00:27

Mówiąc otechnicznych powodów, jest tylko kilka, niezwykle 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 użyć takich rzeczy jak

  • nieblokujące, asynchroniczne zapytania
  • procedury składowane zwracające wiele wyników
  • szyfrowanie (SSL)
  • Kompresja

Jeśli ich potrzebujesz - są to bez wątpienia techniczne powody, aby przenieść z dala od rozszerzenia mysql w kierunku czegoś bardziej stylowego i nowoczesnego.

Niemniej jednak, istnieją również problemy nietechniczne, które mogą sprawić, że twoje doświadczenie będzie nieco trudniejsze

  • dalsze korzystanie z tych funkcji w nowoczesnych wersjach PHP podniesie przestarzałe powiadomienia na poziomie. Po prostu można je wyłączyć.
  • w odległej przyszłości mogą być ewentualnie usunięte z domyślnej kompilacji PHP. Nic wielkiego, bo mydsql ext zostanie przeniesiony do PECL i każdy hoster z przyjemnością skompiluje z nim PHP, ponieważ nie chce stracić klientów, których witryny działały przez dziesięciolecia.
  • silny opór społeczności Stackoverflow. Za każdym razem, gdy wspomnisz o tych uczciwych funkcjach, powiedziano ci, że są one pod ścisłym tabu.
  • będąc przeciętnym użytkownikiem PHP, najprawdopodobniej twój pomysł korzystania z tych funkcji jest podatny na błędy i zły. Tylko z powodu tych wszystkich licznych samouczków i podręczników, które uczą cię w niewłaściwy sposób. Nie same funkcje-muszę to podkreślić-ale sposób ich użycia.
Ta ostatnia kwestia jest problemem.
Ale moim zdaniem proponowane rozwiązanie również nie jest lepsze.
Wydaje mi się, zbyt idealistyczne marzenie, że wszyscy ci użytkownicy PHP nauczą się prawidłowo obsługiwać zapytania SQL na raz. Najprawdopodobniej po prostu zmieniliby mysql_* na mysqli_* mechanicznie, pozostawiając podejście to samo. Tym bardziej, że mysqli wykonuje gotowe polecenia niewiarygodnie bolesne i kłopotliwe.
Nie wspominając już o tym, że natywne przygotowane instrukcjenie wystarczą do ochrony przed iniekcjami SQL, a ani mysqli, ani PDO nie oferują rozwiązania. Więc zamiast walczyć z tym uczciwym rozszerzeniem, wolałbym walczyć ze złymi praktykami i edukować ludzi we właściwy sposób.

Istnieje również kilka fałszywych lub nieistotnych powodów, takich jak

  • nie obsługuje procedur składowanych (używaliśmy mysql_query("CALL my_proc"); dla wieku)
  • nie obsługuje transakcji (tak samo jak powyżej)
  • nie obsługuje wielu wyrażeń (kto ich potrzebuje?)
  • nie pod aktywnym rozwojem (i co z tego? czy to w jakiś praktyczny sposób wpływa na ciebie?)
  • brak interfejsu OO (stworzenie go to kwestia kilku godzin)
  • nie obsługuje gotowych instrukcji ani Parametryzowanych zapytań
[5]}Ostatni jest interesującym punktem. Chociaż mysql ext nie obsługuje native przygotowane oświadczenia, nie są wymagane dla bezpieczeństwa. Możemy łatwo sfałszować przygotowane oświadczenia za pomocą ręcznie obsługiwanych elementów zastępczych (tak jak robi to PDO):
function paraQuery()
{
    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = mysql_real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = mysql_query($query);
    if (!$result)
    {
        throw new Exception(mysql_error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

voila , wszystko jest parametryzowane i bezpieczne.

Ale OK, jeśli nie podoba Ci się czerwone pole w instrukcji, pojawia się problem z wyborem: mysqli czy PDO?

Cóż, odpowiedź będzie następująca:

  • Jeśli rozumiesz konieczność korzystania z warstwy abstrakcji bazy danych i szukasz API aby go utworzyć, mysqli jest bardzo dobrym wyborem, ponieważ rzeczywiście obsługuje wiele funkcji specyficznych dla mysql.
  • Jeśli, podobnie jak większość ludzi z PHP, używasz surowych wywołań API poprawnie w kodzie aplikacji (co jest zasadniczo złą praktyką) - PDO jest jedynym wyborem, ponieważ to rozszerzenie udaje, że nie jest tylko API, ale raczej pół-DAL, nadal niekompletne, ale oferuje wiele ważnych funkcji, z dwóch z nich sprawia, że PDO jest krytycznie mysqli:

    • W przeciwieństwie do mysqli, PDO może wiązać symbole zastępcze przez wartość, co sprawia, że dynamicznie budowane zapytania są możliwe bez kilku ekranów dość niechlujnego kodu.
    • W przeciwieństwie do mysqli, PDO zawsze może zwrócić wynik zapytania w prostej zwykłej tablicy, podczas gdy mysqli może to zrobić tylko w instalacjach mysqlnd.

Jeśli więc jesteś przeciętnym użytkownikiem PHP i chcesz oszczędzić sobie mnóstwa bólów głowy podczas korzystania z natywnych gotowych instrukcji, PDO-znowu-jest jedynym wybór.
Jednak PDO nie jest również srebrną kulą i ma swoje trudności.
Napisałem więc rozwiązania dla wszystkich typowych pułapek i skomplikowanych przypadków w PDO tag wiki

Niemniej jednak, wszyscy mówiący o rozszerzeniach zawsze brakuje 2 ważne fakty o Mysqli i PDO:

  1. Przygotowane oświadczenie nie jest srebrną kulą. Istnieją identyfikatory dynamiczne, których nie można powiązać za pomocą gotowych instrukcji. Są dynamiczne zapytania o nieznanej liczbie parametrów, co sprawia, że budowanie zapytań jest trudnym zadaniem.

  2. W kodzie aplikacji nie powinny występować ani funkcje mysqli_*, ani PDO.
    Powinna istnieć warstwa abstrakcji pomiędzy nimi a kodem aplikacji, która wykona całą brudną robotę wiązania, zapętlania, obsługi błędów itp. wewnątrz, dzięki czemu kod aplikacji jest suchy i czysty. Szczególnie w przypadku złożonych przypadków, takich jak zapytania dynamiczne budynek.

Więc samo przejście na PDO lub mysqli nie wystarczy. Trzeba użyć ORM, lub konstruktora zapytań, lub jakiejkolwiek klasy abstrakcji bazy danych zamiast wywoływać surowe funkcje API w swoim kodzie.
I przeciwnie - jeśli masz warstwę abstrakcyjną między kodem aplikacji a API mysql - nie ma znaczenia, który silnik jest używany. Możesz używać MySQL ext, dopóki nie zostanie wycofany, a następnie łatwo przepisać klasę abstrakcji na inny silnik, mając cały kod aplikacji nienaruszony.

Oto kilka przykładów opartych na mojej klasie safemysql aby pokazać, jak taka klasa abstrakcji powinna być:

$city_ids = array(1,2,3);
$cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

Porównaj ten pojedynczy wiersz z ilością kodu, którego potrzebujesz z PDO .
Następnie porównaj z szaloną ilością kodu będziesz potrzebował surowych instrukcji przygotowanych przez Mysqli. Zauważ, że obsługa błędów, profilowanie, logowanie zapytań już wbudowane i bieganie.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

Porównaj to ze zwykłymi wstawkami PDO, gdy każda nazwa pola jest powtarzana sześć do dziesięciu razy - we wszystkich tych licznych nazwanych miejscach zastępczych, powiązaniach i definicjach zapytań.

Inny przykład:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

Trudno znaleźć przykład dla PDO, aby poradzić sobie z takim praktycznym przypadkiem.
I to będzie zbyt słowne i najprawdopodobniej niebezpieczne.

Więc jeszcze raz - to nie tylko surowy sterownik powinien być twoim zmartwieniem, ale Klasa abstrakcji, przydatna nie tylko dla głupich przykłady z podręcznika dla początkujących, ale do rozwiązania wszelkich rzeczywistych problemów.

 110
Author: Your Common Sense,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-25 17:32:07

Powodów jest wiele, ale być może najważniejszym z nich jest to, że funkcje te zachęcają do niepewnych praktyk programistycznych, ponieważ nie obsługują gotowych instrukcji. Przygotowane instrukcje pomagają zapobiegać atakom SQL injection.

Podczas korzystania z funkcji mysql_* Należy pamiętać o uruchamianiu parametrów dostarczonych przez użytkownika przez mysql_real_escape_string(). Jeśli zapomnisz w jednym miejscu lub zdarzy ci się uciec tylko z części danych wejściowych, twoja baza danych może zostać zaatakowana.

Używając przygotowanego wyrażenia w PDO lub mysqli sprawią, że tego rodzaju błędy programistyczne będą trudniejsze do wykonania.

 99
Author: Trott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-12 13:41:23

Ponieważ (między innymi) znacznie trudniej jest zapewnić dezynfekcję danych wejściowych. Jeśli używasz parametryzowanych zapytań, tak jak w przypadku PDO lub mysqli, możesz całkowicie uniknąć ryzyka.

Jako przykład ktoś mógłby użyć "enhzflep); drop table users" jako nazwy użytkownika. Stare funkcje umożliwią wykonywanie wielu poleceń na zapytanie, więc coś takiego może usunąć całą tabelę.

Jeśli ktoś miałby użyć PDO mysqli, nazwa użytkownika skończyłaby się "enhzflep); drop table users".

Zobacz bobby-tables.com .

 78
Author: enhzflep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-25 18:25:56

Ta odpowiedź jest napisana, aby pokazać, jak trywialne jest ominięcie źle napisanego kodu weryfikacyjnego PHP, jak (i za pomocą czego) te ataki działają i jak zastąpić stare funkcje MySQL bezpiecznym przygotowanym oświadczeniem - i zasadniczo, dlaczego użytkownicy StackOverflow (prawdopodobnie z dużą ilością reputacji) szczekają na nowych użytkowników zadając pytania, aby poprawić swój kod.

Po pierwsze, zapraszam do stworzenia tej testowej bazy danych mysql (nazwałem mój prep):

mysql> create table users(
    -> id int(2) primary key auto_increment,
    -> userid tinytext,
    -> pass tinytext);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into users values(null, 'Fluffeh', 'mypass');
Query OK, 1 row affected (0.04 sec)

mysql> create user 'prepared'@'localhost' identified by 'example';
Query OK, 0 rows affected (0.01 sec)

mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
Query OK, 0 rows affected (0.00 sec)

Z tym gotowe, możemy przejść do naszego kodu PHP.

Załóżmy, że następujący skrypt jest procesem weryfikacji dla administratora na stronie internetowej (uproszczony, ale działa, jeśli skopiujesz i użyjesz go do testowania):

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }

    $database='prep';
    $link=mysql_connect('localhost', 'prepared', 'example');
    mysql_select_db($database) or die( "Unable to select database");

    $sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
    //echo $sql."<br><br>";
    $result=mysql_query($sql);
    $isAdmin=false;
    while ($row = mysql_fetch_assoc($result)) {
        echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
        $isAdmin=true;
        // We have correctly matched the Username and Password
        // Lets give this person full access
    }
    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }
    mysql_close($link);

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>
Na pierwszy rzut oka wydaje się wystarczająco legalne.

Użytkownik musi podać login i hasło, prawda?

Brilliant, not enter in the following:

user: bob
pass: somePass

I prześlij go.

Wyjście jest następujące:

You could not be verified. Please try again...
Super! Praca zgodnie z oczekiwaniami, teraz pozwala wypróbuj rzeczywistą nazwę użytkownika i hasło:
user: Fluffeh
pass: mypass
Niesamowite! Hi-fives all round, the code correctly verified an admin. Jest idealny! Nie bardzo. Powiedzmy, że użytkownik jest sprytnym małym człowiekiem. Powiedzmy, że tą osobą jestem ja.

Wpisz:

user: bob
pass: n' or 1=1 or 'm=m

A wyjście to:

The check passed. We have a verified admin!

Gratulacje, właśnie pozwoliłeś mi wejść do Twojej super-chronionej sekcji adminów, z wpisaniem fałszywej nazwy użytkownika i fałszywego hasła. Poważnie, jeśli nie uwierz mi, Utwórz bazę danych za pomocą kodu, który podałem i uruchom ten kod PHP-który na pierwszy rzut oka naprawdę ładnie weryfikuje nazwę użytkownika i hasło.

Więc, w odpowiedzi, właśnie dlatego na Ciebie krzyczą. Przyjrzyjmy się więc, co poszło nie tak i dlaczego właśnie dostałem się do Twojej Super-admin-only-bat-cave. Zgadłem i założyłem, że nie jesteś ostrożny ze swoimi wejściami i po prostu przekazałem je bezpośrednio do bazy danych. Zbudowałem wejście w sposób tht zmieniłoby zapytanie, które faktycznie uruchomiłeś. Więc co to miało być i czym się skończyło?
select id, userid, pass from users where userid='$user' and pass='$pass'

To jest zapytanie, ale kiedy zamienimy zmienne na rzeczywiste dane wejściowe, które użyliśmy, otrzymujemy następujące:

select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'

Widzisz, jak skonstruowałem moje "hasło", aby najpierw zamknąć pojedynczy cytat wokół hasła, a następnie wprowadzić zupełnie nowe porównanie? Wtedy dla bezpieczeństwa dodałem kolejny "ciąg", aby pojedynczy cytat zamknąć zgodnie z oczekiwaniami w kodzie, który pierwotnie mieliśmy.

Nie chodzi jednak o ludzi krzyczących na ciebie teraz, chodzi o pokazanie ci, jak uczynić Twój kod bezpieczniejszym.

Co poszło nie tak i jak to naprawić?

Jest to klasyczny atak SQL injection. Jedno z najprostszych. W skali wektorów ataku jest to maluch atakujący czołg - i wygrywający.

Jak więc chronić swoją świętą sekcję admina i uczynić ją ładną i zabezpieczona? Pierwszą rzeczą do zrobienia będzie zaprzestanie używania tych naprawdę starych i przestarzałych funkcji mysql_*. Wiem, że śledziłeś tutorial, który znalazłeś w Internecie i działa, ale jest stary, przestarzały i w ciągu kilku minut po prostu złamałem go bez wytarcia potu.

Teraz masz lepsze opcje użycia mysqli_ lub PDO . Osobiście jestem wielkim fanem PDO, więc będę używał PDO w dalszej części tej odpowiedzi. Są pro i con, ale osobiście uważam, że pro znacznie przewyższają con. jest przenośny w wielu silnikach bazodanowych-czy używasz MySQL lub Oracle lub po prostu o cholernym czegokolwiek-po prostu zmieniając ciąg połączenia, ma wszystkie fantazyjne funkcje, których chcemy używać i jest ładny i czysty. Lubię czyste.

Spójrzmy jeszcze raz na ten kod, tym razem napisany przy użyciu obiektu PDO:

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }
    $isAdmin=false;

    $database='prep';
    $pdo=new PDO ('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $sql="select id, userid, pass from users where userid=:user and pass=:password";
    $myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
    if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
    {
        while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
        {
            echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
            $isAdmin=true;
            // We have correctly matched the Username and Password
            // Lets give this person full access
        }
    }

    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

Główne różnice polegają na tym, że nie ma już mysql_* funkcji. It ' s all done poprzez obiekt PDO, po drugie, korzysta z przygotowanego Oświadczenia. O co pytasz? Jest to sposób, aby powiedzieć bazie danych przed uruchomieniem zapytania, co zapytanie jest, że mamy zamiar uruchomić. W tym przypadku, mówimy do bazy danych: "Cześć, mam zamiar uruchomić polecenie select żądając id, userid i pass z tabeli users, gdzie userid jest zmienną, a pass jest również zmienną.".

Następnie w instrukcji execute przekazujemy do bazy danych tablicę ze wszystkimi zmiennymi tego teraz oczekuje.

Wyniki są fantastyczne. Spróbujmy ponownie tych kombinacji nazwy użytkownika i hasła sprzed:

user: bob
pass: somePass
Użytkownik nie został zweryfikowany. Super.

A może:

user: Fluffeh
pass: mypass
Och, trochę się podnieciłem, zadziałało: czek minął. Mamy zweryfikowanego admina!

Teraz spróbujmy danych, które sprytny facet wprowadzi, aby spróbować ominąć nasz mały system weryfikacji: {]}

user: bob
pass: n' or 1=1 or 'm=m

Tym razem dostajemy po:

You could not be verified. Please try again...

Dlatego jesteś krzyczący podczas wysyłania pytań-to dlatego, że ludzie widzą, że Twój kod można ominąć nawet próbując. Proszę, użyj tego pytania i odpowiedz, aby poprawić swój kod, aby uczynić go bardziej bezpiecznym i korzystać z funkcji, które są aktualne.

Wreszcie, nie oznacza to, że jest to idealny kod. Istnieje wiele innych rzeczy, które można zrobić, aby go poprawić, użyj hashed haseł na przykład, upewnij się, że podczas przechowywania sensetive informacje w bazie danych, nie przechowywać go w postaci zwykłego tekstu, mają wiele poziomów weryfikacji - ale naprawdę, jeśli po prostu zmienić swój stary kod iniekcji podatne na to, będzie dobrze po drodze do pisania dobrego kodu - i fakt, że masz już tak daleko i nadal czyta daje mi poczucie nadziei, że nie tylko wdrożyć tego typu Kod podczas pisania stron internetowych i aplikacji, ale że może wyjść i badania tych innych rzeczy, które właśnie wymienione-i więcej. Napisz najlepszy kod, jaki możesz, a nie najbardziej podstawowy kod, który ledwo działa.

 68
Author: Fluffeh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-18 12:50:54

Rozszerzenie MySQL jest najstarszym z trzech i było oryginalnym sposobem, który Programiści używali do komunikacji z MySQL. To rozszerzenie jest obecnie przestarzałe na rzecz innych dwóch alternatywy z powodu ulepszeń wprowadzonych w nowszych wydaniach zarówno PHP, jak i MySQL.

  • MySQLi jest 'ulepszonym' rozszerzeniem do pracy z bazami danych MySQL. Korzysta z funkcji dostępnych w nowszych wersjach serwera MySQL, wyświetla zarówno zorientowany funkcyjnie, jak i obiektowo interfejs dla programisty, A A robi kilka innych sprytnych rzeczy.

  • PDO oferuje API, które konsoliduje większość funkcji, które były wcześniej rozpowszechnione w głównych rozszerzeniach dostępu do baz danych, tj. MySQL, PostgreSQL, SQLite, MSSQL, itp. Interfejs udostępnia obiekty wysokiego poziomu, dzięki czemu programista może pracować z połączeniami baz danych, zapytaniami i zestawami wyników, a sterowniki niskiego poziomu wykonują komunikację i obsługa zasobów za pomocą serwera bazy danych. Wiele dyskusji i pracy toczy się w PDO i jest uważana za odpowiednią metodę pracy z bazami danych w nowoczesnym, profesjonalnym kodzie.

 35
Author: Alexander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-02 07:20:14

Powyższe odpowiedzi uważam za bardzo długie, więc podsumowując:

Rozszerzenie mysqli ma numer korzyści, najważniejsze ulepszenia nad rozszerzenie mysql to:

  • Interfejs obiektowy
  • Wsparcie dla przygotowanych wypowiedzi
  • Obsługa wielu wyrażeń
  • obsługa transakcji
  • rozszerzone możliwości debugowania
  • obsługa serwera wbudowanego

Źródło: MySQLi przegląd


Jak wyjaśniono w powyższych odpowiedziach, alternatywami dla mysql są mysqli i PDO (PHP Data Objects).

  • API obsługuje instrukcje przygotowane po stronie serwera: obsługiwane przez MYSQLi i PDO
  • API obsługuje instrukcje przygotowane po stronie klienta: obsługiwane tylko przez PDO
  • API obsługuje procedury składowane: zarówno MySQLi jak i PDO
  • API obsługuje wiele poleceń i wszystkie funkcje MySQL 4.1+ - obsługiwane przez MySQLi, a głównie przez CHNP

Zarówno MySQLi, jak i PDO zostały wprowadzone w PHP 5.0, podczas gdy MySQL został wprowadzony przed PHP 3.0. Warto zauważyć, że MySQL jest zawarty w PHP5.x choć przestarzałe w późniejszych wersjach.

 23
Author: Ani Menon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-25 19:09:28

Możliwe jest definiowanie prawie wszystkich funkcji mysql_* za pomocą mysqli lub PDO. Wystarczy umieścić je na starej aplikacji PHP, a będzie działać na PHP7. Moje rozwiązanie TUTAJ .

<?php

define('MYSQL_LINK', 'dbl');
$GLOBALS[MYSQL_LINK] = null;

function mysql_link($link=null) {
    return ($link === null) ? $GLOBALS[MYSQL_LINK] : $link;
}

function mysql_connect($host, $user, $pass) {
    $GLOBALS[MYSQL_LINK] = mysqli_connect($host, $user, $pass);
    return $GLOBALS[MYSQL_LINK];
}

function mysql_pconnect($host, $user, $pass) {
    return mysql_connect($host, $user, $pass);
}

function mysql_select_db($db, $link=null) {
    $link = mysql_link($link);
    return mysqli_select_db($link, $db);
}

function mysql_close($link=null) {
    $link = mysql_link($link);
    return mysqli_close($link);
}

function mysql_error($link=null) {
    $link = mysql_link($link);
    return mysqli_error($link);
}

function mysql_errno($link=null) {
    $link = mysql_link($link);
    return mysqli_errno($link);
}

function mysql_ping($link=null) {
    $link = mysql_link($link);
    return mysqli_ping($link);
}

function mysql_stat($link=null) {
    $link = mysql_link($link);
    return mysqli_stat($link);
}

function mysql_affected_rows($link=null) {
    $link = mysql_link($link);
    return mysqli_affected_rows($link);
}

function mysql_client_encoding($link=null) {
    $link = mysql_link($link);
    return mysqli_character_set_name($link);
}

function mysql_thread_id($link=null) {
    $link = mysql_link($link);
    return mysqli_thread_id($link);
}

function mysql_escape_string($string) {
    return mysql_real_escape_string($string);
}

function mysql_real_escape_string($string, $link=null) {
    $link = mysql_link($link);
    return mysqli_real_escape_string($link, $string);
}

function mysql_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql);
}

function mysql_unbuffered_query($sql, $link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, $sql, MYSQLI_USE_RESULT);
}

function mysql_set_charset($charset, $link=null){
    $link = mysql_link($link);
    return mysqli_set_charset($link, $charset);
}

function mysql_get_host_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_host_info($link);
}

function mysql_get_proto_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_proto_info($link);
}
function mysql_get_server_info($link=null) {
    $link = mysql_link($link);
    return mysqli_get_server_info($link);
}

function mysql_info($link=null) {
    $link = mysql_link($link);
    return mysqli_info($link);
}

function mysql_get_client_info() {
    $link = mysql_link();
    return mysqli_get_client_info($link);
}

function mysql_create_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "CREATE DATABASE `$db`");
}

function mysql_drop_db($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "DROP DATABASE `$db`");
}

function mysql_list_dbs($link=null) {
    $link = mysql_link($link);
    return mysqli_query($link, "SHOW DATABASES");
}

function mysql_list_fields($db, $table, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    $table = str_replace('`', '', mysqli_real_escape_string($link, $table));
    return mysqli_query($link, "SHOW COLUMNS FROM `$db`.`$table`");
}

function mysql_list_tables($db, $link=null) {
    $link = mysql_link($link);
    $db = str_replace('`', '', mysqli_real_escape_string($link, $db));
    return mysqli_query($link, "SHOW TABLES FROM `$db`");
}

function mysql_db_query($db, $sql, $link=null) {
    $link = mysql_link($link);
    mysqli_select_db($link, $db);
    return mysqli_query($link, $sql);
}

function mysql_fetch_row($qlink) {
    return mysqli_fetch_row($qlink);
}

function mysql_fetch_assoc($qlink) {
    return mysqli_fetch_assoc($qlink);
}

function mysql_fetch_array($qlink, $result=MYSQLI_BOTH) {
    return mysqli_fetch_array($qlink, $result);
}

function mysql_fetch_lengths($qlink) {
    return mysqli_fetch_lengths($qlink);
}

function mysql_insert_id($qlink) {
    return mysqli_insert_id($qlink);
}

function mysql_num_rows($qlink) {
    return mysqli_num_rows($qlink);
}

function mysql_num_fields($qlink) {
    return mysqli_num_fields($qlink);
}

function mysql_data_seek($qlink, $row) {
    return mysqli_data_seek($qlink, $row);
}

function mysql_field_seek($qlink, $offset) {
    return mysqli_field_seek($qlink, $offset);
}

function mysql_fetch_object($qlink, $class="stdClass", array $params=null) {
    return ($params === null)
        ? mysqli_fetch_object($qlink, $class)
        : mysqli_fetch_object($qlink, $class, $params);
}

function mysql_db_name($qlink, $row, $field='Database') {
    mysqli_data_seek($qlink, $row);
    $db = mysqli_fetch_assoc($qlink);
    return $db[$field];
}

function mysql_fetch_field($qlink, $offset=null) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    return mysqli_fetch_field($qlink);
}

function mysql_result($qlink, $offset, $field=0) {
    if ($offset !== null)
        mysqli_field_seek($qlink, $offset);
    $row = mysqli_fetch_array($qlink);
    return (!is_array($row) || !isset($row[$field]))
        ? false
        : $row[$field];
}

function mysql_field_len($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->length : false;
}

function mysql_field_name($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgname) ? $field->name : $field->orgname;
}

function mysql_field_table($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    if (!is_object($field))
        return false;
    return empty($field->orgtable) ? $field->table : $field->orgtable;
}

function mysql_field_type($qlink, $offset) {
    $field = mysqli_fetch_field_direct($qlink, $offset);
    return is_object($field) ? $field->type : false;
}

function mysql_free_result($qlink) {
    try {
        mysqli_free_result($qlink);
    } catch (Exception $e) {
        return false;
    }
    return true;
}
 8
Author: Pavel Tzonkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-24 07:02:53