Jak mogę zapobiec SQL injection w PHP?

odpowiedzi na to pytanie są wysiłkiem społeczności. Edytuj istniejące odpowiedzi, aby poprawić ten post. Obecnie nie przyjmuje nowych odpowiedzi ani interakcji.

Jeśli wejście użytkownika jest wstawiane bez modyfikacji do SQL zapytanie, wtedy aplikacja staje się podatna na SQL injection , jak w poniższym przykładzie:

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

To dlatego, że użytkownik może wprowadzić coś w rodzaju value'); DROP TABLE table;--, a zapytanie staje się:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

Co można zrobić, aby temu zapobiec?

Author: Andrew G. Johnson, 2008-09-12

28 answers

Używaj gotowych instrukcji i parametryzowanych zapytań.{[23] } są to polecenia SQL, które są wysyłane i przetwarzane przez serwer bazy danych oddzielnie od dowolnych parametrów. W ten sposób atakujący nie może wstrzyknąć złośliwego SQL.

Zasadniczo masz dwie opcje, aby to osiągnąć:

  1. Using PDO (for any supported database driver):

    $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
    
    $stmt->execute([ 'name' => $name ]);
    
    foreach ($stmt as $row) {
        // Do something with $row
    }
    
  2. Using MySQLi (for MySQL):

    $stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ?');
    $stmt->bind_param('s', $name); // 's' specifies the variable type => 'string'
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        // Do something with $row
    }
    

Jeśli łączysz się z bazą danych inną niż MySQL, istnieje druga opcja specyficzna dla sterownika, do której możesz się odwołać (na przykład pg_prepare() i pg_execute() dla PostgreSQL). PDO jest uniwersalną opcją.


Poprawne ustawianie połączenia

Zauważ, że podczas korzystania z PDO aby uzyskać dostęp do bazy danych MySQL prawdziwe przygotowane instrukcje są nie używane domyślnie. Aby to naprawić musisz wyłączyć emulację przygotowanych Oświadczenia. Przykład tworzenia połączenia za pomocą PDO to:

$dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'password');

$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

W powyższym przykładzie tryb błędu nie jest bezwzględnie konieczny, ale zaleca się dodanie go . W ten sposób skrypt nie zatrzyma się na Fatal Error, gdy coś pójdzie nie tak. I daje deweloperowi szansę na catch wszelkie błędy, które są thrown jako PDOExceptions.

Czym jest obowiązkowe , jest jednak pierwsza setAttribute() linia, która mówi PDO, aby wyłączyć emulowane przygotowane instrukcje i używać prawdziwe przygotowane wypowiedzi. Zapewnia to, że instrukcja i wartości nie są przetwarzane przez PHP przed wysłaniem jej na serwer MySQL(dając potencjalnemu atakującemu szansę na wstrzyknięcie złośliwego SQL).

Chociaż możesz ustawić charset w opcjach konstruktora, ważne jest, aby pamiętać, że 'starsze' wersje PHP (przed 5.3.6) po cichu ignorowały parametr charset w DSN.


Wyjaśnienie

Polecenie SQL, które przekazujesz prepare jest przetwarzany i kompilowany przez serwer bazy danych. Poprzez podanie parametrów (? lub nazwanego parametru, takiego jak :name w powyższym przykładzie) informujesz silnik bazy danych, gdzie chcesz filtrować. Po wywołaniu execute, Instrukcja prepared jest połączona z podanymi wartościami parametrów.

Ważne jest to, że wartości parametrów są łączone z kompilowaną instrukcją, a nie łańcuchem SQL. SQL injection działa poprzez oszukanie skryptu w tym złośliwe ciągów podczas tworzenia SQL do wysłania do bazy danych. Wysyłając rzeczywisty SQL oddzielnie od parametrów, ograniczasz ryzyko, że skończysz z czymś, czego nie zamierzałeś.

Wszelkie parametry, które wysyłasz podczas używania instrukcji prepared, będą traktowane jako ciągi znaków (chociaż silnik bazy danych może dokonać pewnej optymalizacji, więc parametry mogą skończyć się również jako liczby, oczywiście). W powyższym przykładzie, jeśli zmienna $name zawiera 'Sarah'; DELETE FROM employees, wynikiem będzie po prostu wyszukiwanie string "'Sarah'; DELETE FROM employees", a nie skończysz z pustą tabelą .

Kolejną zaletą użycia instrukcji prepared jest to, że jeśli wykonasz tę samą instrukcję wiele razy w tej samej sesji, zostanie ona przetworzona i skompilowana tylko raz, co da ci pewien wzrost prędkości.

Oh, a skoro pytałeś jak to zrobić dla wkładki, oto przykład (używając PDO):

$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');

$preparedStatement->execute([ 'column' => $unsafeValue ]);

Czy przygotowane instrukcje mogą być używane do dynamicznych zapytań?

Podczas gdy możesz nadal używać przygotowane instrukcje dla parametrów zapytania, struktura samego dynamicznego zapytania nie może być parametryzowana, a niektóre funkcje zapytania nie mogą być parametryzowane.

Dla tych konkretnych scenariuszy najlepiej jest użyć filtra białej listy, który ogranicza możliwe wartości.

// Value whitelist
// $dir can only be 'DESC', otherwise it will be 'ASC'
if (empty($dir) || $dir !== 'DESC') {
   $dir = 'ASC';
}
 9176
Author: PeeHaa,
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
2020-06-06 15:28:28

Deprecated Warning: Przykładowy kod tej odpowiedzi (podobnie jak przykładowy kod pytania) wykorzystuje rozszerzenie PHP MySQL, które zostało przestarzałe w PHP 5.5.0 i całkowicie usunięte w PHP 7.0.0.

Ostrzeżenie Bezpieczeństwa : Ta odpowiedź nie jest zgodna z najlepszymi praktykami bezpieczeństwa. Ucieczka jest niewystarczająca, aby zapobiec iniekcji SQL , zamiast tego użyj gotowych instrukcji . Stosuj strategię przedstawioną poniżej na własne ryzyko. (Również mysql_real_escape_string() został usunięty w PHP 7.)

Jeśli używasz najnowszej wersji PHP, przedstawiona poniżej opcja mysql_real_escape_string nie będzie już dostępna (choć mysqli::escape_string jest współczesnym odpowiednikiem). Obecnie opcja mysql_real_escape_string ma sens tylko dla kodu starszego w starej wersji PHP.


Masz dwie opcje-unikanie znaków specjalnych w unsafe_variable lub użycie parametryzowanego zapytania. Oba ochronią Cię przed SQL injection. Parametryzowane zapytanie jest uważane za lepszą praktykę, ale będzie wymaga zmiany na nowsze rozszerzenie MySQL w PHP, zanim będzie można go użyć.

Najpierw zakryjemy dolny ciąg uderzeniowy.

//Connect

$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);

mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

//Disconnect

Zobacz też:mysql_real_escape_string funkcja.

Aby użyć parametryzowanego zapytania, musisz użyć MySQLizamiast MySQL. Aby przepisać twój przykład, potrzebujemy czegoś takiego jak poniżej.

<?php
    $mysqli = new mysqli("server", "username", "password", "database_name");

    // TODO - Check that connection was successful.

    $unsafe_variable = $_POST["user-input"];

    $stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");

    // TODO check that $stmt creation succeeded

    // "s" means the database expects a string
    $stmt->bind_param("s", $unsafe_variable);

    $stmt->execute();

    $stmt->close();

    $mysqli->close();
?>

Kluczowa funkcja, na której będziesz chciał przeczytać, będzie mysqli::prepare.

Również, jak sugerowali inni, może okazać się przydatne / łatwiejsze, aby zwiększyć warstwę abstrakcji z czymś takim jak PDO.

Zwróć uwagę, że sprawa, o którą pytałeś, jest dość prosta i że bardziej złożone sprawy mogą wymagać bardziej złożonych podejść. W szczególności:

  • jeśli chcesz zmienić strukturę SQL na podstawie danych wejściowych użytkownika, parametryzowane zapytania nie pomogą, a wymagane ucieczki nie są objęte mysql_real_escape_string. W takim przypadku lepiej byłoby przekazać dane wejściowe użytkownika przez białą listę, aby upewnić się, że dozwolone są tylko "bezpieczne" wartości.
  • jeśli używasz liczb całkowitych z danych wejściowych użytkownika w warunku i zastosujesz podejście mysql_real_escape_string, będziesz cierpieć z powodu problemu opisanego przez wielomian w komentarzach poniżej. Ten przypadek jest trudniejszy, ponieważ liczby całkowite nie byłyby otoczone cudzysłowami, więc można sobie z tym poradzić, sprawdzając, czy dane wejściowe użytkownika zawierają tylko cyfry.
  • są prawdopodobnie inne przypadki, o których Nie wiem. Możesz znaleźć ten {[21] } jest przydatnym źródłem informacji na temat niektórych bardziej subtelnych problemów, które możesz napotkać.
 1683
Author: Matt Sheppard,
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-11-21 11:13:20

Każda odpowiedź tutaj obejmuje tylko część problemu. W rzeczywistości istnieją cztery różne części zapytań, które możemy dynamicznie dodać do SQL:-

  • a string
  • a liczba
  • identyfikator
  • słowo kluczowe składnia

A przygotowane Oświadczenia obejmują tylko dwa z nich.

Ale czasami musimy uczynić nasze zapytanie jeszcze bardziej dynamicznym, dodając również operatory lub identyfikatory. Będziemy więc potrzebować różnych technik ochrony.

W ogólnie rzecz biorąc, takie podejście do ochrony opiera się na białej liście.

W tym przypadku każdy parametr dynamiczny powinien być zakodowany na twardo w skrypcie i wybrany z tego zestawu. Na przykład, aby wykonać dynamiczne zamawianie:

$orders  = array("name", "price", "qty"); // Field names
$key = array_search($_GET['sort'], $orders)); // if we have such a name
$orderby = $orders[$key]; // If not, first one will be set automatically. 
$query = "SELECT * FROM `table` ORDER BY $orderby"; // Value is safe

Aby ułatwić ten proces napisałem funkcję helpera whitelist, która wykonuje wszystkie zadania w jednej linii:

$orderby = white_list($_GET['orderby'], "name", ["name","price","qty"], "Invalid field name");
$query  = "SELECT * FROM `table` ORDER BY `$orderby`"; // sound and safe

Istnieje inny sposób zabezpieczenia identyfikatorów-ucieczki, ale raczej trzymam się białej listy jako bardziej solidnego i wyraźnego podejścia. A jednak tak długo jak masz zacytowany identyfikator, możesz uciec od znaku cytowania, aby był bezpieczny. Na przykład, domyślnie dla mysql musisz podwoić znak cudzysłowu, aby go uwolnić . Dla innych innych DBMS reguły ucieczki byłyby inne.

Nadal występuje problem ze słowami kluczowymi składni SQL (takimi jak AND, DESC i takie), ale white-listing wydaje się jedynym podejściem w tym przypadku.

Tak więc ogólne zalecenie może być sformułowane jako

  • każda zmienna, która reprezentuje literał danych SQL (lub mówiąc prościej - ciąg SQL lub liczba) musi być dodany przez prepared polecenie. Bez Wyjątków.
  • każda inna część zapytania, taka jak słowo kluczowe SQL, tabela lub nazwa pola lub operator - musi być filtrowana przez białą listę.

Update

Chociaż istnieje ogólne porozumienie co do najlepszych praktyk dotyczących ochrony SQL injection, nadal istnieje wiele złych praktyk. i niektóre z nich zbyt głęboko zakorzenione w umysłach użytkowników PHP. Na przykład, na tej stronie znajduje się (choć niewidoczna dla większości odwiedzających) ponad 80 usuniętych odpowiedzi - wszystkie usunięte przez społeczność z powodu złej jakości lub promowania Złych i przestarzałych praktyk. Co gorsza, niektóre złe odpowiedzi nie są usuwane, ale raczej prosperują.

Na przykład tam(1) są(2) still(3) Wiele(4) odpowiedzi (5) , w tym druga najbardziej upvoted odpowiedź sugerowanie ręcznego uciekania ciągów-przestarzałe podejście, które okazało się niepewne.

Lub jest nieco lepsza odpowiedź, która sugeruje po prostu inną metodę formatowania łańcuchów , a nawet chwali się nią jako ostatecznym panaceum. Choć oczywiście nie jest. Ta metoda nie jest lepsza niż zwykłe formatowanie łańcuchów, ale zachowuje wszystkie swoje wady: ma zastosowanie tylko do łańcuchów i, jak każde inne formatowanie ręczne, jest zasadniczo opcjonalna, niezobowiązująca, podatna na ludzki błąd wszelkiego rodzaju.

Myślę, że wszystko to z powodu jednego bardzo starego przesądu, wspieranego przez takie autorytety jak OWASP czy podręcznik PHP , który głosi równość pomiędzy tym, co "ucieka", a ochroną przed zastrzykami SQL.

Niezależnie od tego, co mówi podręcznik PHP od wieków, *_escape_string w żaden sposób nie czyni danych bezpiecznymi i nigdy nie było to zamierzone. Poza tym, że jest bezużyteczny dla jakiejkolwiek części SQL innej niż string, ręczne uciekanie jest złe, ponieważ jest ręczne jako przeciwieństwo zautomatyzowanego.

A OWASP jeszcze pogarsza sytuację, podkreślając unikanie danych wejściowych użytkownika , co jest kompletnym nonsensem: nie powinno być takich słów w kontekście ochrony iniekcji. Każda zmienna jest potencjalnie niebezpieczna-bez względu na źródło! Innymi słowy - każda zmienna musi być odpowiednio sformatowana, aby móc ją umieścić w zapytaniu - bez względu na źródło. Liczy się cel podróży. Moment, w którym deweloper zaczyna oddzielić owcę od kozy (myśląc, czy dana zmienna jest "Bezpieczna", czy nie) stawiają pierwszy krok w kierunku katastrofy. Nie wspominając już o tym, że nawet sformułowanie sugeruje masową ucieczkę w punkcie wejścia, przypominającą bardzo magiczną funkcję cytatów - już pogardzaną, przestarzałą i usuniętą.

Tak więc, w przeciwieństwie do wszelkich "uciekających", prepared statements jest miarą, która rzeczywiście chroni przed SQL injection (jeśli ma to zastosowanie).

 1098
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
2020-06-20 09:12:55

Polecam użycie PDO (PHP Data Objects) do uruchamiania sparametryzowanych zapytań SQL.

Nie tylko chroni to przed SQL injection, ale także przyspiesza zapytania.

I używając PDO zamiast mysql_, mysqli_, i pgsql_ funkcje, sprawiasz, że Twoja aplikacja jest trochę bardziej abstrakcyjna z bazy danych, w rzadkim przypadku, gdy musisz zmienić dostawców baz danych.

 861
Author: Kibbee,
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-15 12:48:22

Użyj PDO i przygotowanych zapytań.

($conn jest obiektem PDO)

$stmt = $conn->prepare("INSERT INTO tbl VALUES(:id, :name)");
$stmt->bindValue(':id', $id);
$stmt->bindValue(':name', $name);
$stmt->execute();
 630
Author: Imran,
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-11 17:02:40

Jak widzisz, ludzie sugerują, abyś korzystał co najwyżej z gotowych wypowiedzi. To nie jest złe, ale gdy twoje zapytanie zostanie wykonane tylko raz na proces, będzie niewielka kara za wydajność.

Miałem do czynienia z tym problemem, ale myślę, że rozwiązałem go w bardzo wyrafinowany sposób-sposób, w jaki hakerzy unikają cudzysłowów. Użyłem tego w połączeniu z emulowanymi przygotowanymi oświadczeniami. Używam go, aby zapobiec wszystkim rodzajom możliwego wtrysku SQL ataki.

Moje podejście:

  • Jeśli oczekujesz, że input będzie liczbą całkowitą, upewnij się, że jest naprawdę. liczba całkowita. W języku typu zmiennego, takim jak PHP, jest to Bardzo Ważne. Możesz użyć na przykład tego bardzo prostego, ale potężnego rozwiązania: sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • Jeśli oczekujesz czegoś innego od liczby całkowitej hex to . Jeśli go wyprostujesz, doskonale wymkniesz się od wszystkich danych wejściowych. W C / C++ istnieje funkcja o nazwie mysql_hex_string(), W PHP możesz użyć bin2hex().

    Nie martw się o to, że łańcuch znaków z klauzulą ucieczki będzie miał 2x oryginalnej długości, ponieważ nawet jeśli użyjesz mysql_real_escape_string, PHP musi przydzielić tę samą pojemność ((2*input_length)+1), która jest taka sama.

  • Ta metoda hex jest często używana podczas przesyłania danych binarnych, ale nie widzę powodu, dlaczego nie używać jej na wszystkich danych, aby zapobiec atakom SQL injection. Należy pamiętać, że należy przedłożyć dane za pomocą 0x lub użyć funkcji MySQL UNHEX zamiast tego.

Więc, na przykład, zapytanie:

SELECT password FROM users WHERE name = 'root';

Stanie się:

SELECT password FROM users WHERE name = 0x726f6f74;

Lub

SELECT password FROM users WHERE name = UNHEX('726f6f74');

Hex jest idealną ucieczką. Nie można wstrzykiwać.

Różnica między funkcją UNHEX a prefiksem 0x

Była jakaś dyskusja w komentarzach, więc w końcu chcę to wyjaśnić. Te dwa podejścia są bardzo podobne, ale są nieco inne pod pewnymi względami: {]}

Prefiks 0x może być używany tylko dla kolumn danych, takich jak char, varchar, text, block, binary, itd.
Ponadto jego użycie jest trochę skomplikowane, jeśli masz zamiar wstawić pusty ciąg znaków. Musisz go całkowicie zastąpić '', albo dostaniesz błąd.

UNHEX() Działa na dowolnej kolumnie ; nie musisz się martwić o pusty łańcuch.


Metody Hex są często używane jako ataki

Zauważ, że ta metoda hex jest często używana jako atak SQL injection, gdzie liczby całkowite są jak ciągi znaków i uciekają tylko z mysql_real_escape_string. Następnie można uniknąć stosowania cudzysłowów.

Na przykład, jeśli zrobisz coś takiego:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

Atak może cię wstrzyknąć bardzo z łatwością . Zwróć uwagę na następujący kod zwracany ze skryptu:

SELECT ... WHERE id = -1 UNION ALL SELECT table_name FROM information_schema.tables;

I teraz po prostu wyodrębnij strukturę tabeli:

SELECT ... WHERE id = -1 UNION ALL SELECT column_name FROM information_schema.column WHERE table_name = __0x61727469636c65__;

A następnie po prostu wybierz dane, które chcesz. Czy to nie fajne?

Ale jeśli koder miejsca wstrzyknięcia byłby hexem, nie byłoby możliwe wstrzyknięcie, ponieważ zapytanie wygląda tak:

SELECT ... WHERE id = UNHEX('2d312075...3635');
 564
Author: Zaffy,
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
2020-06-12 03:31:08

Deprecated Warning: Przykładowy kod tej odpowiedzi (podobnie jak przykładowy kod pytania) wykorzystuje rozszerzenie PHP MySQL, które zostało przestarzałe w PHP 5.5.0 i całkowicie usunięte w PHP 7.0.0.

Ostrzeżenie Bezpieczeństwa : ta odpowiedź nie jest zgodna z najlepszymi praktykami bezpieczeństwa. Ucieczka jest niewystarczająca, aby zapobiec iniekcji SQL , zamiast tego użyj prepared statements . Stosuj strategię przedstawioną poniżej na własne ryzyko. (Również mysql_real_escape_string() został usunięty w PHP 7.)

Ważne

Najlepszym sposobem zapobiegania SQL Injection jest użycie gotowych instrukcji zamiast ucieczki, Jak pokazuje zaakceptowana odpowiedź.

Istnieją biblioteki takie jak Aura.Sql i EasyDB, które umożliwiają programistom łatwiejsze korzystanie z gotowych instrukcji. Aby dowiedzieć się więcej o tym, dlaczego preparowane wyrażenia są lepsze przy zatrzymywaniu SQL injection, zapoznaj się z this mysql_real_escape_string() bypass i ostatnio Naprawiono luki Unicode SQL Injection w WordPressie .

Injection prevention - mysql_real_escape_string()

PHP posiada specjalnie przygotowaną funkcję zapobiegającą tym atakom. Wystarczy użyć funkcji mysql_real_escape_string.

mysql_real_escape_string pobiera ciąg znaków, który będzie używany w zapytaniu MySQL i zwraca ten sam ciąg z wszystkimi próbami SQL injection bezpiecznie uciekającymi. Zasadniczo zastąpi te kłopotliwe cytaty(') użytkownik może wprowadzić za pomocą zamiennika MySQL-safe, unikalny cytat \'.

Uwaga: musisz być podłączony do bazy danych, aby korzystać z tej funkcji!

/ / Połącz się z MySQL

$name_bad = "' OR 1'"; 

$name_bad = mysql_real_escape_string($name_bad);

$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";


$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 

$name_evil = mysql_real_escape_string($name_evil);

$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;

Więcej szczegółów znajdziesz w MySQL-SQL Injection Prevention.

 504
Author: rahularyansharma,
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-05-09 03:05:39

Mógłbyś zrobić coś takiego:

$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
To nie rozwiąże każdego problemu, ale to bardzo dobry krok. Pominąłem oczywiste elementy, takie jak sprawdzenie istnienia zmiennej, jej formatu (cyfry, litery itp.).
 471
Author: Kiran Maniya,
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-06-18 10:23:01

Cokolwiek zrobisz, upewnij się, że Twoje dane wejściowe nie zostały już zmiażdżone przez magic_quotes lub inne bzdury o dobrych intencjach, a jeśli to konieczne, przeprowadź je przez stripslashes lub cokolwiek, aby je zdezynfekować.

 385
Author: Rob,
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 14:40:48

Deprecated Warning: Przykładowy kod tej odpowiedzi (podobnie jak przykładowy kod pytania) wykorzystuje rozszerzenie PHP MySQL, które zostało przestarzałe w PHP 5.5.0 i całkowicie usunięte w PHP 7.0.0.

Ostrzeżenie Bezpieczeństwa : ta odpowiedź nie jest zgodna z najlepszymi praktykami bezpieczeństwa. Ucieczka jest niewystarczająca, aby zapobiec iniekcji SQL , zamiast tego użyj prepared statements . Stosuj strategię przedstawioną poniżej na własne ryzyko. (Również mysql_real_escape_string() został usunięty w PHP 7.)

Sparametryzowane kwerendy i walidacja danych wejściowych to najlepszy sposób. Istnieje wiele scenariuszy, w których może wystąpić SQL injection, mimo że użyto mysql_real_escape_string().

W tym celu należy skontaktować się z firmą SQL injection.]}
$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

Lub

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

W obu przypadkach nie można użyć ' do ochrony enkapsulacji.

Źródło: nieoczekiwany SQL Injection (gdy Escape nie wystarczy)

 367
Author: Cedric,
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-05-09 02:59:38

Moim zdaniem najlepszym sposobem, aby ogólnie zapobiec wtrysku SQL w aplikacji PHP (lub jakiejkolwiek aplikacji internetowej, o to chodzi) jest myślenie o architekturze aplikacji. Jeśli jedynym sposobem ochrony przed SQL injection jest pamiętanie o użyciu specjalnej metody lub funkcji, która robi dobrą rzecz za każdym razem, gdy rozmawiasz z bazą danych, robisz to źle. W ten sposób, to tylko kwestia czasu, aż zapomnisz poprawnie sformatować zapytanie w pewnym momencie w Twoim kod.

Przyjęcie wzorca MVC i frameworka podobnego do CakePHP lub CodeIgniter jest prawdopodobnie właściwym rozwiązaniem: typowe zadania, takie jak tworzenie bezpiecznych zapytań do bazy danych, zostały rozwiązane i centralnie zaimplementowane w takich frameworkach. Pomagają uporządkować aplikację internetową w rozsądny sposób i sprawiają, że myślisz bardziej o ładowaniu i zapisywaniu obiektów niż o bezpiecznym konstruowaniu pojedynczych zapytań SQL.

 315
Author: Johannes Fahrenkrug,
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
2014-07-16 02:05:53

I favor procedury przechowywane (MySQL ma obsługę procedur składowanych od 5.0) z punktu widzenia bezpieczeństwa-zalety to -

  1. większość baz danych (w tym MySQL) umożliwia ograniczenie dostępu użytkownika do wykonywania procedur składowanych. Drobnoziarnista Kontrola dostępu jest przydatna, aby zapobiec eskalacji ataków na uprawnienia. Zapobiega to możliwości uruchamiania programu SQL bezpośrednio z bazy danych przez skompromitowane aplikacje.
  2. oni streszczenie surowe zapytanie SQL z aplikacji, więc mniej informacji o strukturze bazy danych jest dostępna dla aplikacji. Utrudnia to ludziom zrozumienie podstawowej struktury bazy danych i zaprojektowanie odpowiednich ataków.
  3. akceptują tylko parametry, więc zalety parametryzowanych zapytań są tam. Oczywiście-IMO nadal musisz wyczyścić swoje dane wejściowe-zwłaszcza jeśli używasz dynamicznego SQL wewnątrz procedury składowanej.

Wady are -

  1. one (procedury przechowywane) są trudne do utrzymania i mają tendencję do bardzo szybkiego mnożenia. To sprawia, że zarządzanie nimi jest problemem.
  2. nie nadają się do dynamicznych zapytań - jeśli są zbudowane tak, aby akceptować dynamiczny kod jako parametry, to wiele zalet jest negowanych.
 304
Author: Nikhil,
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 14:43:05

Istnieje wiele sposobów zapobiegania iniekcjom SQL i innym hackom SQL. Można go łatwo znaleźć w Internecie (Wyszukiwarka Google). Oczywiście PDO jest jednym z dobrych rozwiązań. ale chciałbym zaproponować Ci kilka dobrych linków do SQL injection.

Co to jest SQL injection i jak zapobiec

Instrukcja PHP dla SQL injection

Microsoft Wyjaśnienie SQL injection i zapobieganie w PHP

I inne podobne zapobieganie SQL injection z MySQL i PHP.

Teraz, Dlaczego musisz zapobiec zapytaniu z SQL injection?

Chciałbym dać Ci znać: dlaczego staramy się zapobiegać SQL injection z krótkim przykładem poniżej:

Zapytanie o dopasowanie uwierzytelniania loginu:

$query="select * from users where email='".$_POST['email']."' and password='".$_POST['password']."' ";

Teraz, jeśli ktoś (haker) postawi

$_POST['email']= [email protected]' OR '1=1

I hasło cokolwiek....

Zapytanie zostanie przetworzone na system Tylko do:

$query="select * from users where email='[email protected]' OR '1=1';

Druga część zostanie odrzucona. Więc, co się stanie? Nieautoryzowany użytkownik (haker) będzie mógł zalogować się jako administrator bez posiadania swojego hasła. Teraz może zrobić wszystko, co może zrobić administrator / osoba e-mail. Widzisz, to bardzo niebezpieczne, jeśli nie można zapobiec SQL injection.

 304
Author: Manish Shrivastava,
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
2020-02-22 17:39:17

Myślę, że jeśli ktoś chce używać PHP i MySQL lub jakiegoś innego serwera bazodanowego:

  1. Think about learning PDO – PHP Data Objects) - jest to warstwa dostępu do bazy danych zapewniająca jednolitą metodę dostępu do wielu baz danych.
  2. pomyśl o nauce MySQLi
  3. Użyj natywnych funkcji PHP takich jak: strip_tags, mysql_real_escape_string lub jeśli zmienna numeryczna, po prostu (int)$foo. Więcej o typach zmiennych w PHP przeczytasz tutaj . Jeśli używasz bibliotek takich jak PDO lub MySQLi, Zawsze używaj PDO:: quote () i mysqli_real_escape_string () .

Przykłady bibliotek:

---- CHNP

----- No placeholders-ripe for SQL injection! it ' s bad

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values ($name, $addr, $city)");

----- Unnamed placeholders

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) values (?, ?, ?);

----- nazwane placeholdery

$request = $pdoConnection->("INSERT INTO parents (name, addr, city) value (:name, :addr, :city)");

--- MySQLi

$request = $mysqliConnection->prepare('
       SELECT * FROM trainers
       WHERE name = ?
       AND email = ?
       AND last_login > ?');

    $query->bind_param('first_param', 'second_param', $mail, time() - 3600);
    $query->execute();

P. S :

PDO wygrywa tę bitwę z łatwością. Ze wsparciem dla dwunastu różnych sterowników bazy danych i nazwanych parametrów, możemy zignorować mała utrata wydajności i przyzwyczaić się do jego API. Z zabezpieczenia z punktu widzenia, oba są bezpieczne, o ile deweloper z nich korzysta sposób ich użycia

Ale podczas gdy zarówno PDO jak i MySQLi są dość szybkie, MySQLi wykonuje nieznacznie szybciej w benchmarkach - ~2,5% dla nieprzetworzone wypowiedzi, a ~6,5% dla przygotowanych.

I proszę przetestować każde zapytanie do bazy danych - to lepszy sposób, aby zapobiec wtrysku.

 269
Author: RDK,
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
2014-07-16 02:27:44

Jeśli to możliwe, podaj typy swoich parametrów. Ale działa tylko na prostych typach, takich jak int, bool i float.

$unsafe_variable = $_POST['user_id'];

$safe_variable = (int)$unsafe_variable ;

mysqli_query($conn, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
 259
Author: devOp,
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-05-01 16:51:42

Jeśli chcesz skorzystać z silników pamięci podręcznej, takich jak Redis lub Memcached, może DALMP mógłby być wyborem. Używa czystego MySQLi . Sprawdź to: DALMP Database Abstraction Layer for MySQL using PHP.

Możesz również "przygotować" swoje argumenty przed przygotowaniem zapytania, aby móc budować dynamiczne zapytania i na końcu mieć w pełni przygotowane Zapytanie o instrukcje. DALMP database Abstraction Layer for MySQL using PHP.

 233
Author: Peter Mortensen,
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 14:45:42

Dla tych, którzy nie są pewni, jak korzystać z PDO (pochodzących z funkcji mysql_), zrobiłem bardzo, bardzo prosty wrapper PDO , który jest pojedynczym plikiem. Istnieje, aby pokazać, jak łatwo jest zrobić wszystkie typowe rzeczy, które aplikacje muszą być zrobione. Współpracuje z PostgreSQL, MySQL i SQLite.

Zasadniczo, przeczytaj go podczas czytania instrukcji , aby zobaczyć, jak umieścić funkcje PDO w prawdziwym życiu, aby ułatwić przechowywanie i pobieranie wartości w formacie ty chcę.

Chcę jedną kolumnę

$count = DB::column('SELECT COUNT(*) FROM `user`);

I want an array (key => value) results (tzn. for making a selectbox)

$pairs = DB::pairs('SELECT `id`, `username` FROM `user`);

I want a single row result

$user = DB::row('SELECT * FROM `user` WHERE `id` = ?', array($user_id));

I want an array of results

$banned_users = DB::fetch('SELECT * FROM `user` WHERE `banned` = ?', array(TRUE));
 224
Author: Xeoncross,
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 14:47:00

Używając tej funkcji PHP mysql_escape_string() możesz w szybki sposób uzyskać dobrą prewencję.

Na przykład:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."'

mysql_escape_string - zawiera ciąg znaków do użycia w mysql_query

Aby uzyskać więcej informacji, możesz dodać na końcu ...

wHERE 1=1   or  LIMIT 1

Wreszcie masz:

SELECT * FROM users WHERE name = '".mysql_escape_string($name_from_html_form)."' LIMIT 1
 223
Author: Nicolas Finelli,
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 14:46:07

Kilka wskazówek dotyczących interpretacji znaków specjalnych w poleceniach SQL.

Nie używaj MySQL. To rozszerzenie jest przestarzałe. Zamiast tego użyj MySQLi lub PDO .

MySQLi

Do ręcznego usuwania znaków specjalnych w łańcuchu można użyć funkcji mysqli_real_escape_string. Funkcja nie będzie działać poprawnie, chyba że poprawny zestaw znaków jest ustawiony za pomocą mysqli_set_charset.

Przykład:

$mysqli = new mysqli('host', 'user', 'password', 'database');
$mysqli->set_charset('charset');

$string = $mysqli->real_escape_string($string);
$mysqli->query("INSERT INTO table (column) VALUES ('$string')");

Do automatycznego wycinania wartości z przygotowanymi instrukcjami, użyj mysqli_prepare i mysqli_stmt_bind_param gdzie należy podać typy odpowiednich zmiennych bind dla odpowiedniej konwersji:

Przykład:

$stmt = $mysqli->prepare("INSERT INTO table (column1, column2) VALUES (?,?)");

$stmt->bind_param("is", $integer, $string);

$stmt->execute();

Bez względu na to, czy używasz gotowych instrukcji czy mysqli_real_escape_string, zawsze musisz znać rodzaj danych wejściowych, z którymi pracujesz.

Więc jeśli używasz przygotowanego instrukcja, należy określić typy zmiennych dla funkcji mysqli_stmt_bind_param.

A użycie {[3] } służy, jak sama nazwa mówi, do unikania znaków specjalnych w łańcuchu, więc nie uczyni to liczb całkowitych bezpiecznymi. Celem tej funkcji jest zapobieganie łamaniu łańcuchów w instrukcjach SQL i uszkodzeniom bazy danych, które może ona spowodować. mysqli_real_escape_string jest użyteczną funkcją, gdy jest używana prawidłowo, zwłaszcza w połączeniu z sprintf.

Przykład:

$string = "x' OR name LIKE '%John%";
$integer = '5 OR id != 0';

$query = sprintf( "SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string($string), $integer);

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 5

$integer = '99999999999999999999';
$query = sprintf("SELECT id, email, pass, name FROM members WHERE email ='%s' AND id = %d", $mysqli->real_escape_string($string), $integer);

echo $query;
// SELECT id, email, pass, name FROM members WHERE email ='x\' OR name LIKE \'%John%' AND id = 2147483647
 221
Author: Danijel,
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-12-12 06:36:05

Prostą alternatywę dla tego problemu można rozwiązać poprzez przyznanie odpowiednich uprawnień w samej bazie danych. Na przykład: jeśli używasz bazy danych MySQL, wprowadź do bazy danych za pomocą terminala lub interfejsu użytkownika i wykonaj następujące polecenie:

 GRANT SELECT, INSERT, DELETE ON database TO username@'localhost' IDENTIFIED BY 'password';

Spowoduje to, że użytkownik zostanie ograniczony tylko do określonego zapytania. Usuń uprawnienia do usuwania, aby dane nigdy nie zostały usunięte z zapytania wywołanego ze strony PHP. Drugą rzeczą do zrobienia jest aby usunąć uprawnienia tak, że MySQL odświeża uprawnienia i aktualizacje.

FLUSH PRIVILEGES; 

Więcej informacji o flush .

Aby zobaczyć aktualne uprawnienia użytkownika uruchom następujące zapytanie.

select * from mysql.user where User='username';

Dowiedz się więcej o GRANT .

 184
Author: Apurv Nerlekar,
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-19 13:25:25

Jeśli chodzi o wiele przydatnych odpowiedzi, mam nadzieję dodać trochę wartości do tego wątku.

SQL injection jest atakiem, który można wykonać poprzez wejścia użytkownika (wejścia, które wypełniał użytkownik, a następnie używane wewnątrz zapytań). Wzorce SQL injection są poprawną składnią zapytań, podczas gdy możemy ją nazwać: złe zapytania ze złych powodów i zakładamy, że może być zła osoba, która próbuje uzyskać tajne informacje (omijając kontrolę dostępu), które wpływają na trzy zasady bezpieczeństwa (poufność, uczciwość i dostępność).

Teraz naszym celem jest zapobieganie zagrożeniom bezpieczeństwa, takim jak ataki SQL injection, pytanie (Jak zapobiec atakowi SQL injection za pomocą PHP), być bardziej realistyczne, filtrowanie danych lub czyszczenie danych wejściowych jest przypadek, gdy za pomocą danych wejściowych użytkownika wewnątrz takiego zapytania, za pomocą PHP lub innego języka programowania nie jest przypadek, lub jak zalecane przez więcej osób do korzystania z nowoczesnych technologii, takich jak prepared statement lub innych narzędzi, które obecnie obsługują SQL Injection prevention, uważasz, że te narzędzia nie są już dostępne? Jak zabezpieczyć swoją aplikację?

Moje podejście do SQL injection to: czyszczenie danych wejściowych użytkownika przed wysłaniem ich do bazy danych (przed użyciem ich wewnątrz dowolnego zapytania).

Filtrowanie danych (konwersja niebezpiecznych danych na bezpieczne Dane)

Weź pod uwagę, że PDOi MySQLi Nie są dostępne. Jak zabezpieczyć swoją aplikację? Zmuszasz mnie do ich użycia? A co z inne języki poza PHP? Wolę podać ogólne pomysły, ponieważ można je wykorzystać do szerszych granic, a nie tylko do określonego języka.

  1. użytkownik SQL (ograniczenie uprawnień użytkownika) : najczęstsze operacje SQL to (SELECT, UPDATE, INSERT), to po co nadawać uprawnienia aktualizacji użytkownikowi, który tego nie wymaga? Na przykład, login i strony wyszukiwania używają tylko SELECT, to po co używać użytkowników DB na tych stronach z wysokimi uprawnieniami?

Zasada: nie twórz jeden użytkownik bazy danych dla wszystkich uprawnień. Dla wszystkich operacji SQL, można utworzyć schemat jak (deluser, selectuser, updateuser) jako nazwy użytkowników dla łatwego użycia.

Zobacz zasada najmniejszego przywileju .

  1. Filtrowanie danych: przed zbudowaniem zapytania należy je zweryfikować i przefiltrować. Dla programistów ważne jest zdefiniowanie pewnych właściwości dla każdej zmiennej wprowadzanej przez użytkownika: typ danych, wzór danych i długość danych . Pole, które jest liczba pomiędzy (x I y) musi być dokładnie sprawdzona przy użyciu dokładnej reguły, a dla pola, które jest ciągiem znaków (tekst): wzorzec ma miejsce, na przykład nazwa użytkownika musi zawierać tylko niektóre znaki, powiedzmy [a-zA-Z0-9_ -.]. Długość waha się między (x I n) gdzie x i n (liczby całkowite, x reguła: tworzenie dokładnych filtrów i zasad walidacji to dla mnie najlepsze praktyki.

  2. Skorzystaj z innych narzędzi: tutaj również zgadzam się z Tobą, że przygotowane oświadczenie (parametryzowane zapytanie) i procedur przechowywanych. Wadą jest to, że te sposoby wymagają zaawansowanych umiejętności, które nie istnieją dla większości użytkowników. Podstawową ideą jest tutaj rozróżnienie między zapytaniem SQL a danymi, które są używane w środku. Oba podejścia mogą być używane nawet z niebezpiecznymi danymi, ponieważ dane wprowadzane przez użytkownika nie dodają niczego do oryginalnego zapytania, np. (any lub x=x).

Aby uzyskać więcej informacji, przeczytaj OWASP SQL Injection Prevention Cheat Sheet.

Teraz, jeśli jesteś zaawansowanym użytkownikiem, zacznij używać tej obrony, jak chcesz, ale dla początkujących, jeśli nie mogą szybko zaimplementować procedury składowanej i przygotować oświadczenie, lepiej filtrować dane wejściowe, jak tylko mogą.

Wreszcie, rozważmy, że użytkownik wysyła ten tekst poniżej, zamiast wpisywać swoją nazwę Użytkownika:

[1] UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root'

Dane wejściowe można sprawdzić wcześnie, bez żadnych przygotowanych instrukcji i procedur składowanych, ale aby zachować bezpieczeństwo, Korzystanie z nich rozpoczyna się po filtrowaniu danych użytkownika i walidacji.

Ostatnim punktem jest wykrywanie nieoczekiwanych zachowań, które wymagają większego wysiłku i złożoności; nie jest to zalecane dla normalnych aplikacji internetowych.

Nieoczekiwane zachowanie w powyższym wpisie użytkownika to SELECT, UNION, IF, SUBSTRING, BENCHMARK, SHA i root. Po wykryciu tych słów można uniknąć wprowadzania.

UPDATE 1:

Użytkownik skomentował ten post, OK! Oto co OWASP.ORG :

Obrona Podstawowa:

Option #1: Use of Prepared Statements (Parameterized Queries)
Opcja # 2: Użycie procedur składowanych
Opcja # 3: unikanie wszystkich danych wejściowych dostarczonych przez użytkownika

Dodatkowe zabezpieczenia:

Również Egzekwować: Najmniejszy Przywilej
Wykonaj Również: Walidacja Wprowadzania Białej Listy

Jak zapewne wiesz, twierdzenie o artykule powinno być poparte ważnym argumentem, przynajmniej jednym odniesieniem! W przeciwnym razie uważa się to za atak i złe oświadczenie!

Update 2:

Z podręcznika PHP, PHP: Prepared Statements-Manual :

Escaping i SQL injection

Powiązane zmienne będą automatycznie przetwarzane przez serwer. Na serwer wstawia ich wartości w odpowiednich miejscach do szablon oświadczenia przed wykonaniem. Podpowiedź należy podać do serwera dla typu zmiennej powiązanej, aby stworzyć odpowiednią nawrócenie. Zobacz też funkcja mysqli_stmt_bind_param () więcej informacje.

Automatyczna Ucieczka wartości wewnątrz serwera jest czasami uważany za funkcję zabezpieczającą przed iniekcją SQL. To samo stopień bezpieczeństwa można osiągnąć za pomocą Nie przygotowanych oświadczeń, jeżeli wartości wejściowe są usuwane poprawnie.

Update 3:

Stworzyłem przypadki testowe, aby wiedzieć, jak PDO i MySQLi wysyłają zapytanie do serwera MySQL przy użyciu przygotowanego oświadczenie:

CHNP:

$user = "''1''"; // Malicious keyword
$sql = 'SELECT * FROM awa_user WHERE userame =:username';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $user));

Query Log:

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

MySQLi:

$stmt = $mysqli->prepare("SELECT * FROM awa_user WHERE username =?")) {
$stmt->bind_param("s", $user);
$user = "''1''";
$stmt->execute();

Query Log:

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

Jest jasne, że przygotowane oświadczenie również ucieka od danych, nic więcej.

Jak również wspomniano w powyższym oświadczeniu,

Automatyczna Ucieczka wartości wewnątrz serwera jest czasami uważana za funkcję zabezpieczającą, aby zapobiec wtrysku SQL. Ten sam stopień zabezpieczenia można uzyskać za pomocą niepreparowanych instrukcji, jeśli wartości wejściowe są odpowiednio zabezpieczone

Dlatego dowodzi to, że walidacja danych, taka jak intval(), jest dobrym pomysłem dla wartości całkowitych przed wysłaniem dowolnego zapytania. Ponadto zapobieganie złośliwym danym użytkownika przed wysłaniem zapytania jest poprawnym i poprawnym podejściem .

Proszę zobaczyć to pytanie, aby uzyskać więcej szczegółów: PDO wysyła surowe zapytanie do MySQL, podczas gdy Mysqli wysyła przygotowane zapytanie, oba wytwarzają to samo wynik

Bibliografia:

  1. SQL Injection Cheat Sheet
  2. SQL Injection
  3. bezpieczeństwo informacji
  4. Zasady Bezpieczeństwa
  5. walidacja danych
 177
Author: 18 revs, 8 users 53%user1646111,
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-15 13:31:54

Ostrzeżenie Bezpieczeństwa : Ta odpowiedź nie jest zgodna z najlepszymi praktykami bezpieczeństwa. Ucieczka jest niewystarczająca, aby zapobiec iniekcji SQL , zamiast tego użyj prepared statements . Stosuj strategię przedstawioną poniżej na własne ryzyko. (Również, mysql_real_escape_string() został usunięty w PHP 7.)

Deprecated Warning: rozszerzenie mysql jest obecnie przestarzałe. zalecamy użycie rozszerzenia PDO

Używam trzech różnych sposobów, aby zapobiegaj podatności mojej aplikacji internetowej na SQL injection.

  1. użycie mysql_real_escape_string(), która jest wstępnie zdefiniowaną funkcją w PHP , a ten kod dodaje ukośniki do następujących znaków: \x00, \n, \r, \, ', " i \x1a. Przekaż wartości wejściowe jako parametry, aby zminimalizować szansę na SQL injection.
  2. najbardziej zaawansowanym sposobem jest użycie PDO.
Mam nadzieję, że to ci pomoże.

Rozważmy następujące zapytanie:

$iId = mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

Mysql_real_escape_string() nie będzie tutaj chronił. Jeśli używasz pojedynczych cudzysłowów (' ') wokół zmiennych w zapytaniu, chroni Cię przed tym. Poniżej znajduje się rozwiązanie tego problemu:

$iId = (int) mysql_real_escape_string("1 OR 1=1"); $sSql = "SELECT * FROM table WHERE id = $iId";

To pytanie zawiera kilka dobrych odpowiedzi na ten temat.

Sugeruję, używanie PDO jest najlepszą opcją.

Edit:

mysql_real_escape_string() jest przestarzały od PHP 5.5.0. Użyj mysqli lub PDO.

Alternatywą dla mysql_real_escape_string() jest

string mysqli_real_escape_string ( mysqli $link , string $escapestr )

Przykład:

$iId = $mysqli->real_escape_string("1 OR 1=1");
$mysqli->query("SELECT * FROM table WHERE id = $iId");
 176
Author: Soumalya Banerjee,
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-05-09 03:00:18

Prostym sposobem byłoby użycie frameworka PHP takiego jak CodeIgniter lub Laravel, które mają wbudowane funkcje takie jak filtrowanie i active-record, dzięki czemu nie musisz się martwić o te niuanse.

 170
Author: Deepak Thomas,
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 14:50:57

Ostrzeżenie: podejście opisane w tej odpowiedzi dotyczy tylko bardzo konkretnych scenariuszy i nie jest bezpieczne, ponieważ ataki SQL injection nie polegają tylko na możliwości wstrzyknięcia X=Y.

Jeśli atakujący próbują włamać się do formularza za pomocą zmiennej PHP $_GET lub za pomocą ciągu zapytań URL, będziesz w stanie je złapać, jeśli nie są bezpieczne.

RewriteCond %{QUERY_STRING} ([0-9]+)=([0-9]+)
RewriteRule ^(.*) ^/track.php

Ponieważ 1=1, 2=2, 1=2, 2=1, 1+1=2, itd... są najczęstszymi pytaniami do bazy danych SQL z napastnik. Być może jest również używany przez wiele aplikacji hakerskich.

Ale musisz uważać, że nie możesz przepisać bezpiecznego zapytania ze swojej strony. Powyższy kod daje ci wskazówkę, aby przepisać lub przekierować (to zależy od ciebie) ten dynamiczny ciąg zapytania o hacking na stronę, która będzie przechowywać adres IP atakującego , a nawet ich pliki cookie, historię, przeglądarkę lub inne poufne informacje, dzięki czemu możesz poradzić sobie z nimi później, blokując ich konto lub kontakt z władzami.

 148
Author: 5ervant,
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-15 13:40:48

Dobrym pomysłem jest użycie mapera obiektowo-relacyjnego Jak Idiorm :

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();

$user->first_name = 'Jamie';
$user->save();

$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}

To nie tylko ratuje cię przed iniekcjami SQL, ale także przed błędami składni! Obsługuje również kolekcje modeli z łańcuchem metod, aby filtrować lub stosować akcje do wielu wyników naraz i wielu połączeń.

 130
Author: Thomas Ahle,
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
2020-03-29 15:35:46

Jest tak wiele odpowiedzi dla PHP i MySQL, ale tutaj jest kod dla PHP i Oracle do zapobiegania wtrysku SQL oraz regularnego korzystania ze sterowników oci8:

$conn = oci_connect($username, $password, $connection_string);
$stmt = oci_parse($conn, 'UPDATE table SET field = :xx WHERE ID = 123');
oci_bind_by_name($stmt, ':xx', $fieldval);
oci_execute($stmt);
 128
Author: Chintan Gor,
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
2016-05-25 13:51:10

Deprecated Warning: Przykładowy kod tej odpowiedzi (podobnie jak przykładowy kod pytania) wykorzystuje rozszerzenie PHP MySQL, które zostało przestarzałe w PHP 5.5.0 i całkowicie usunięte w PHP 7.0.0.

Ostrzeżenie Bezpieczeństwa : ta odpowiedź nie jest zgodna z najlepszymi praktykami bezpieczeństwa. Ucieczka jest niewystarczająca, aby zapobiec iniekcji SQL , zamiast tego użyj prepared statements . Stosuj strategię przedstawioną poniżej na własne ryzyko. (Również mysql_real_escape_string() został usunięty w PHP 7.)

Używanie PDOi MYSQLi jest dobrą praktyką, aby zapobiec zastrzykom SQL, ale jeśli naprawdę chcesz pracować z funkcjami i zapytaniami MySQL, lepiej byłoby użyć

Mysql_real_escape_string

$unsafe_variable = mysql_real_escape_string($_POST['user_input']);

Istnieje więcej możliwości, aby temu zapobiec: np. identify - jeśli wejście jest ciągiem znaków, liczbą, znakiem lub tablicą, jest tak wiele wbudowanych funkcji, które to wykrywają. Ponadto lepiej byłoby użyć tych funkcji do sprawdzenia wejścia data.

Is_string

$unsafe_variable = (is_string($_POST['user_input']) ? $_POST['user_input'] : '');

Is_numeric

$unsafe_variable = (is_numeric($_POST['user_input']) ? $_POST['user_input'] : '');

I o wiele lepiej jest używać tych funkcji do sprawdzania danych wejściowych za pomocą mysql_real_escape_string.

 124
Author: Rakesh Sharma,
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-05-09 03:00:59

Napisałem tę małą funkcję kilka lat temu:

function sqlvprintf($query, $args)
{
    global $DB_LINK;
    $ctr = 0;
    ensureConnection(); // Connect to database if not connected already.
    $values = array();
    foreach ($args as $value)
    {
        if (is_string($value))
        {
            $value = "'" . mysqli_real_escape_string($DB_LINK, $value) . "'";
        }
        else if (is_null($value))
        {
            $value = 'NULL';
        }
        else if (!is_int($value) && !is_float($value))
        {
            die('Only numeric, string, array and NULL arguments allowed in a query. Argument '.($ctr+1).' is not a basic type, it\'s type is '. gettype($value). '.');
        }
        $values[] = $value;
        $ctr++;
    }
    $query = preg_replace_callback(
        '/{(\\d+)}/', 
        function($match) use ($values)
        {
            if (isset($values[$match[1]]))
            {
                return $values[$match[1]];
            }
            else
            {
                return $match[0];
            }
        },
        $query
    );
    return $query;
}

function runEscapedQuery($preparedQuery /*, ...*/)
{
    $params = array_slice(func_get_args(), 1);
    $results = runQuery(sqlvprintf($preparedQuery, $params)); // Run query and fetch results.   
    return $results;
}

Pozwala to na uruchamianie instrukcji w jednowierszowym łańcuchu C#.Format jak:

runEscapedQuery("INSERT INTO Whatever (id, foo, bar) VALUES ({0}, {1}, {2})", $numericVar, $stringVar1, $stringVar2);

Ucieka ze względu na typ zmiennej. Jeśli spróbujesz sparametryzować nazwy tabel, kolumn, nie powiedzie się, ponieważ umieszcza każdy łańcuch w cudzysłowach, co jest nieprawidłową składnią.

Aktualizacja zabezpieczeń: poprzednia wersja str_replace zezwalała na zastrzyki poprzez dodanie tokenów {#} do danych użytkownika. Ta wersja preg_replace_callback nie powoduje problemów, jeśli wymiana zawiera te żetony.

 87
Author: Calmarius,
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 14:55:27