Okna.  Wirusy.  Notatniki.  Internet.  biuro.  Narzędzia.  Kierowcy


Na początek trochę o tym, dlaczego te ukośniki są w ogóle potrzebne.
Jeśli w zapytaniu podstawimy jakieś dane, to aby odróżnić te dane od poleceń SQL, należy je podać w cudzysłowie.
Na przykład, jeśli piszesz
WYBIERZ * Z tabeli GDZIE imię = Bill
wówczas baza danych uzna, że ​​Bill to nazwa innego pola, nie znajdzie jej i zgłosi błąd. Dlatego też podstawione dane (w ta sprawa imię Billa) należy ująć w cudzysłów - wówczas baza potraktuje to jako ciąg znaków, którego wartość należy przypisać do pola imię i nazwisko:
WYBIERZ * Z tabeli GDZIE imię = „Rachunek”
Jednak cudzysłowy można znaleźć także w samych danych. Np,
WYBIERZ * Z tabeli GDZIE imię = "D"Artagnan"
Tutaj baza danych uzna, że ​​„D” to dane, a Artagnan to polecenie, którego nie zna, i również zgłosi błąd. Dlatego konieczne jest prześledzenie wszystkich danych, aby wyjaśnić bazie danych, że znalezione w nich cudzysłowy (i inne znaki specjalne) odnoszą się do danych.
W rezultacie otrzymamy prawidłowe żądanie, które nie spowoduje błędów:
WYBIERZ * Z tabeli GDZIE nazwa = "D\Artagnan"

Tym samym dowiedzieliśmy się, że podstawiając dane do zapytania należy kierować się dwiema zasadami:
- wszystkie dane wprowadzane do zapytania muszą być ujęte w cudzysłów (pojedynczy lub podwójny, ale wygodniej i częściej stosuje się cudzysłów pojedynczy).
- we wszystkich zmiennych łańcuchowych znaki specjalne muszą być poprzedzone ukośnikami.

Należy szczególnie zaznaczyć: dodane ukośniki NIE trafiają do bazy danych. Są one potrzebne jedynie w żądaniu. Po uderzeniu w bazę, cięcia są odrzucane. W związku z tym częstym błędem jest używanie ukośników podczas pobierania danych z bazy danych.

W rzeczywistości wszystkie powyższe dotyczą danych i dat typu string. Liczby można wstawiać bez ukośników i bez cudzysłowów. Jeśli to zrobisz, to KONIECZNIE! wymuś rzutowanie danych na żądany typ przed wstawieniem ich do zapytania, na przykład:
$id = interwał ($id);
Jednak dla uproszczenia (i niezawodności) możesz pracować z liczbami tak samo, jak z ciągami znaków (ponieważ mysql nadal konwertuje je na żądany typ). W związku z tym prześledzimy i zacytujemy wszelkie dane wprowadzone do wniosku.

Jest jeszcze jedna zasada - opcjonalna, ale należy jej przestrzegać, aby uniknąć błędów:
Nazwy pól i tabel należy ująć w pojedynczy cudzysłów - „`” (klucz z tym symbolem znajduje się na standardowa klawiatura na lewo od klawisza „1”) Przecież nazwa pola może być taka sama jak słowa kluczowe mysql, ale jeśli użyjemy cudzysłowu, MySQL wszystko zrozumie poprawnie:
WYBIERZ * Z `tabeli` GDZIE `data` = "2006-04-04"
Cytaty te należy rozróżnić i nie mylić ze sobą. Należy również pamiętać, że od cudzysłowów końcowych nie należy uciekać ukośnikami.

Dowiedzieliśmy się więc, jak poprawnie podstawić dane do żądania.
ALE! Zapytania dynamiczne nie ograniczają się do podstawiania danych. Często w zapytaniu musimy podstawiać polecenia SQL i nazwy pól. I tu już przechodzimy do tematu bezpieczeństwa:

SQL Injection to rodzaj ataku hakerskiego, gdy dane przekazywane do skryptu zostają zmodyfikowane w taki sposób, że zapytanie wygenerowane w tym skrypcie zaczyna wykonywać coś zupełnie innego niż to, do czego było przeznaczone.
Zasady ochrony przed takimi atakami można podzielić na dwa punkty:
1. Praca z danymi.
2. Praca z kontrolkami zapytań.

Punkt pierwszy został szczegółowo omówiony powyżej. Można powiedzieć, że w istocie nie jest to obrona. Spełnienie zasad dodawania danych do zapytania podyktowane jest przede wszystkim wymogami SKŁADNI SQL. A jako efekt uboczny mamy również ochronę przed hakerami.

Drugi punkt jest znacznie trudniejszy, ponieważ nie ma jednej uniwersalnej zasady dotyczącej danych - cudzysłów w żaden sposób nie uchroni nazwy pola przed modyfikacją przez hakera. Nie można podać nazwy tabeli, Instrukcje SQL, opcje poleceń LIMIT i inne operatory.
Dlatego główną zasadą przy zastępowaniu kontroli w żądaniu jest:
Jeżeli chcemy dynamicznie podstawiać w zapytaniu instrukcje SQL lub nazwy pól, baz danych, tabel, to pod żadnym pozorem nie należy ich wstawiać bezpośrednio do zapytania.
Wszystkie opcje takich dodatków muszą być zapisane z wyprzedzeniem w skrypcie i wybrane na podstawie tego, co wprowadził użytkownik.
Przykładowo, jeśli chcesz przekazać nazwę pola do zamówienia za pomocą wyciągu, to w żadnym wypadku nie powinieneś zastępować jej bezpośrednio. Muszę to najpierw sprawdzić. Na przykład utwórz tablicę prawidłowych wartości i podstaw w żądaniu tylko wtedy, gdy w tej tablicy występuje przekazany parametr:
$zamówienia =array("nazwa", "cena", "ilość");
$key = array_search($_GET ["sort"], $zamówienia));
$orderby = $zamówienia [ $key ];
$zapytanie = „WYBIERZ * Z „tabeli” ZAMÓW PRZEZ $orderby”;
Szukamy słowa wpisanego przez użytkownika w tablicy wcześniej opisanych opcji i jeśli je znajdziemy, wybieramy odpowiedni element tablicy. Jeśli nie zostanie znalezione żadne dopasowanie, wybrany zostanie pierwszy element tablicy.
Tym samym żądanie zostaje zastąpione nie tym, co wpisał użytkownik, ale tym, co zostało zapisane w naszym skrypcie.
To samo należy zrobić we wszystkich innych przypadkach.
Na przykład, jeśli klauzula WHERE jest generowana dynamicznie:
if (!empty($_GET [ "cena" ])) $where .= "cena="" .mysql_real_escape_string ($_GET [ "cena" ]).""" ;
$zapytanie = „WYBIERZ * Z `tabeli` GDZIE $gdzie”;
Trudno mi sobie wyobrazić przypadek, w którym można dynamicznie wstawić nazwę tabeli do zapytania, ale jeśli tak się stanie, to również trzeba wstawić nazwę tylko ze zbioru zapisanego wcześniej w skrypcie.
Należy wymusić rzutowanie parametrów instrukcji LIMIT na typ całkowity za pomocą działania arytmetyczne lub funkcja intval().
Nie powinieneś sądzić, że wymienione tutaj przykłady wyczerpują wszystkie opcje dynamicznego wykonywania zapytań. Wystarczy zrozumieć tę zasadę i zastosować ją we wszystkich takich przypadkach.

Funkcje pracy z operatorem LIKE
Absolutnie osobna sprawa- Operator LIKE.
Po pierwsze, oprócz zwykłego zakończenia, w zmiennych podstawionych w LIKE, ukośniki muszą zostać podwojone. Oznacza to, że jeśli zmienna zawiera znak \, należy ją podwoić, a następnie wykonać zwykłe zakończenie, poprzez mysql_real_escape_string.
Na przykład, jeśli szukamy ciągu znaków
znak \ nazywa się „ukośnikiem odwrotnym” i potrzebujemy dokładnego dopasowania, po prostu używamy mysql_real_escape_string i zapytanie jest standardowe:
WYBIERZ * Z testu WHERE pole = "znak \\ nazywa się \"odwrotnym ukośnikiem\"" Jeśli chcemy zastąpić ten ciąg w LIKE, najpierw musimy zastąpić każdy ukośnik dwoma, a następnie zastosować mysql_real_escape_string. Wynik będzie
WYBIERZ * Z tabeli GDZIE pole LIKE "%symbol \\\\ nazywa się \"odwrotnym ukośnikiem\"%"
Po drugie, zauważ, że żadna z funkcji dodających ukośniki nie dodaje ich do metaznaków wyszukiwania „%” i „_” używanych w operatorze LIKE. Jeśli więc używasz tego operatora i nie chcesz, aby znaki _ i% były używane jako symbole wieloznaczne, dodaj ukośniki ręcznie. Można to zrobić za pomocą polecenia
$data = addCslashes($data, "%_" ); Uwaga - nie dodaje rzęs! Ta funkcja ma w nazwie dodatkowe „c”.

Okazuje się zatem, że zmienne użyte w operatorze LIKE musimy przetwarzać oddzielnie.
najpierw zamień jeden ukośnik na dwa, używając na przykład następującego kodu:
$var = str_replace („\\”, „\\\\”, $var); następnie (jest to możliwe wraz ze wszystkimi innymi danymi wchodzącymi do żądania) śledzimy:
$var = mysql_real_escape_string($var); a następnie, jeśli chcemy, aby _ i% pasowały dokładnie do nas, robimy to
$var = addCslashes ($var, "_%" );
W rezultacie, jeśli będziemy szukać np. takiej linii
znak \ nazywany jest „ukośnikiem odwrotnym”, a znak _ nazywany jest „podkreśleniem” następnie po przetworzeniu w żądaniu powinno to wyglądać tak:
„%symbol \\\\ nazywa się „ukośnikiem odwrotnym”, a symbol \_ nazywa się „podkreśleniem” Oznacza to, że ukośnik, który pierwotnie znajdował się w wierszu, wzrósł czterokrotnie. Pozostałe znaki zostały jak zwykle odnalezione. Plus - prześledzono znak podkreślenia.

O ukośnikach. Jak się ich pozbyć
Ukośnik lub ukośnik odwrotny z angielskiego ukośnika odwrotnego to ukośnik odwrotny („”), który nagle pojawia się sam w zmiennych w niezrozumiały sposób. Jest dodawany do niektórych znaków specjalnych, ale najczęściej jest zauważany dzięki cudzysłowom.
Dzieje się tak z powodu specjalnego Ustawienia PHP, zwykle domyślnie włączone w hostingu. Teoretycznie ustawienia te mogą zwiększyć bezpieczeństwo skryptów pracujących z bazą danych. W praktyce automatyczne dodawanie ukośników często powoduje zamieszanie i niedogodności, zarówno podczas pracy z bazą danych, jak i w przypadku jej braku.
Obydwa te przypadki omówimy szczegółowo poniżej.

Za automatyczne dodawanie ukośników odpowiedzialne są dyrektywy php.ini, które łącznie nazywane są „magicznymi cudzysłowami”:
magic_quotes_gpc i magic_quotes_runtime Jeśli ta pierwsza jest włączona, PHP automatycznie dodaje ukośniki do danych, które przyszły od użytkownika - z POST, POBIERZ żądania i cookie (oraz loginu i hasła otrzymanego w ramach Autoryzacji HTTP).
Jeśli to drugie, to do danych otrzymanych w trakcie wykonywania skryptu dodawane są ukośniki - np. z pliku lub bazy danych.

Jeśli pracujesz bez bazy danych lub pracujesz z bazą danych poprawnie (co zostanie omówione poniżej), dodatkowe ukośniki tylko Ci przeszkadzają i musisz się ich pozbyć. Najprostszym i najbardziej poprawnym sposobem jest wyłączenie automatycznego dodawania w ustawieniach PHP.
Można to zrobić albo poprawiając odpowiednie dyrektywy w pliku php.ini, jeśli masz do niego dostęp, albo tworząc plik .htaccess w katalogu głównym witryny i dodając do niego linie
php_flag magic_quotes_gpc 0
php_flag magic_quotes_runtime 0

Jeśli nie możesz tego wyłączyć w ten sposób, będziesz musiał napisać kod o różnym stopniu złożoności, aby wyczyścić przychodzące dane z ukośników. (Jeśli jednak chcesz napisać aplikację przenośną niezależną od ustawień PHP, to i tak musisz ją napisać. I umieścić ją w osobnym bloku na początku swoich skryptów).

Najłatwiej sobie poradzić z danymi otrzymanymi podczas operacji, pisząc na początku skryptu
set_magic_quotes_runtime(0); W przypadku danych otrzymanych od użytkownika wszystko jest znacznie bardziej skomplikowane. Do tego kodu potrzebujemy dwóch funkcji:

  • Możesz sprawdzić, czy PHP dodało to za pomocą funkcji get_magic_quotes_gpc.
  • Funkcja stripslashes usuwa ukośniki.
    W związku z tym za pomocą pierwszego należy sprawdzić, a jeśli PHP dodał, następnie iterować po wszystkich przychodzących zmiennych i wyczyścić za pomocą drugiego.
    Jeśli robisz to dobrze, używając Register_globals = off , wystarczy zastosować ukośniki do wszystkich tablic zawierających dane pochodzące z przeglądarki.
    na przykład możesz umieścić następujący kod we wszystkich skryptach witryny:
    paski funkcyjne (& $el ) (
    if (is_array ($el ))
    foreach($el jako $k => $v )
    paski($el[$k]);
    else $el = stripslashes($el );
    }
    if (get_magic_quotes_gpc()) (
    paski($_GET);
    paski($_POST);
    paski($_COOKIE);
    paski($_REQUEST);
    if (isset($_SERVER [ "PHP_AUTH_USER" ])) paski ($_SERVER [ "PHP_AUTH_USER" ]);
    if (isset($_SERVER [ "PHP_AUTH_PW" ])) paski ($_SERVER [ "PHP_AUTH_PW" ]);
    }
    W tej sprawie złe ustawienia Register_globals w ogóle trudno będzie znaleźć akceptowalne rozwiązanie, dlatego lepiej - powtarzam - od razu pracować z właściwymi ustawieniami.

    Uwagi

    • Wśród powodów, dla których nie warto polegać na „magicznych cytatach”, jest jeszcze jeden. Bardzo mało prawdopodobne, ale jednak. „Magiczne cytaty” to w rzeczywistości nie dwie dyrektywy, ale trzy. Trzeci to magic_quotes_sybase . Nie tylko dodaje cudzysłów zamiast ukośnika, ale także anuluje efekt magic_quotes_gpc. Jeśli jakimś cudem obie te dyrektywy mają status „włączony”, to ostatnia nie zadziała! Oznacza to, że opierając się na „magicznych cytatach”, w tym przypadku uzyskamy wszystkie uroki niepoprawnie skomponowanych zapytań. Generalnie, czysto teoretycznie, należy wziąć pod uwagę obecność tej dyrektywy, gdyż stwarza ona również taką niespodziankę, jak… zmiana zachowania funkcji addlash i stripslashes! Jeśli magic_quotes_sybase = on , wówczas te funkcje rozpoczynają odpowiednio dodawanie i usuwanie pojedynczego cudzysłowu zamiast ukośnika.
    • Wszystkie podane przykłady dotyczą wyłącznie bazy danych Mysql. Specyficzne zasady kompilowania zapytań mogą się różnić w przypadku innych systemów DBMS, ale ogólna zasada pozostaje takie samo:
      • jeśli zapewnia API do pracy z bazą danych lub biblioteką strony trzeciej funkcje specjalne do składania próśb, a istnieje możliwość ich wykorzystania, to trzeba je przede wszystkim wykorzystać.
      • jeśli nie ma takich funkcji, to powinieneś poszukać w dokumentacji funkcji ucieczki znaków specjalnych dla tego DBMS.
    Uwaga: kształty
    Podczas wyprowadzania wartości w znacznikach wejściowych formularza ukośniki nie pomagają.
    Aby wyświetlić cały tekst w takim polu, wartość należy ująć w cudzysłów i do danych wyjściowych zastosuj funkcję htmlspecialchars().
    Przykład:

    Należy również zauważyć (chociaż nie ma to nic wspólnego z cudzysłowami i ukośnikami), że funkcja htmlspecialchars powinna być używana podczas ogólnego wysyłania danych do przeglądarki w przypadku wszystkich danych otrzymanych od niezweryfikowanego użytkownika. Dlaczego warto to zrobić, możesz na żądanie przeczytać w Google, czym jest podatność XSS
    przez phpfaq.ru
  • Ze względu na charakter mojej pracy muszę wykonywać audyty bezpieczeństwa kodu źródłowego aplikacji webowych.
    Mnóstwo aplikacji internetowych i mnóstwo kodu...

    Nie jest tajemnicą, że luki w zabezpieczeniach polegające na wstrzykiwaniu SQL są najczęstszymi ze wszystkich luk w zabezpieczeniach serwerów aplikacji internetowych. Istnieją platformy i frameworki, gdzie takie rzeczy są prawie całkowicie wykluczone, np. ORM itp. Jednak statystyki uparcie mówią nam o absolutnej przewadze aplikacji webowych w Internecie z prostymi, połączonymi zapytaniami SQL.Dodatkowo zdarzają się przypadki, gdzie ORM ma ogólne zastosowanie np. wtedy, gdy od danych użytkownika powinny zależeć nie tylko parametry wyrażeń, ale także sama logika zapytań na poziomie operatora.

    Zacznijmy więc.

    Bezużyteczna ucieczka postaci
    Występuje w 83% aplikacji internetowych PHP podatnych na wstrzyknięcie SQL
    Stosowanie funkcji ucieczki znaku, takiej jak
    mysql_escape_string
    mysql_real_escape_string
    dodaje rzęsy
    bez cudzysłowów. Najczęściej objawia się w parametrach numerycznych (wszelkiego rodzaju * _id).
    Przykład
    $sql = "WYBIERZ użytkownika Z listy użytkowników GDZIE userid=".mysql_real_escape_string($_GET["uid"]);

    Wygląda na bezpieczny kod, ale tylko z wyglądu. Wkradł się tutaj najczęstszy schemat wstrzykiwania SQL w PHP. Zaatakowanie tej luki wymaga od osoby atakującej po prostu nieużywania znaków „ ” \x00 \r \n \x1a w wektorze ataku.
    Na przykład:
    /index.php?uid=-777 UNION WYBIERZ hasło Z listy użytkowników

    Szukaj w kodzie
    Skomplikowane przez semantykę języka. Do prostego wyszukiwania możesz użyć egrep:
    egrep -Rin "(wybierz|aktualizuj|wstaw|usuń|zamień).*(z|ustaw|w).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]"

    Logika wyrażenia wyszukiwania polega na znalezieniu wszystkich ciągów znaków, które nie zawierają sekwencji cudzysłowów („”, „”, „”, „”) po lewej stronie funkcji filtrujących. Metoda oczywiście jest daleka od 100%, ale nie można wymagać wyrażenia regularnego do przeprowadzenia analizy semantycznej.
    Dla wygody wyświetlania informacji możesz w konsoli podświetlić funkcję kolorem:
    egrep -Rin "(wybierz|aktualizuj|wstaw|usuń|zamień).*(z|ustaw|w).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]" | egrep --color "(mysql_escape_string|mysql_real_escape_string|addslashes)"

    Aby zabezpieczyć się przed tą luką w szablonie, najlepiej zastosować rzutowanie typów.
    Jest to zawsze szybsze i bardziej niezawodne niż wszelkiego rodzaju filtrowanie i ekranowanie.
    W powyższym przykładzie łatka może mieć postać:
    $sql = "WYBIERZ użytkownika Z listy użytkowników GDZIE userid=".intval($_GET["uid"]);

    Na tym kończę ten krótki esej. Wzywam wszystkich twórców stron internetowych, aby spróbowali sprawdzić swoje źródła pod kątem takich konstrukcji. Jeszcze lepiej, rozwiń powyższy skrypt wyszukiwania dla ludzi.

    ciąg cytatów (11)

    Próbuję znaleźć najlepszy sposób pisania życzeń. Rozumiem też, jak ważne jest bycie konsekwentnym. Do tej pory przypadkowo używałem pojedynczych cudzysłowów, podwójnych cudzysłowów i odwrotnych znaków, bez większego zastanowienia.

    $query = "WSTAW DO tabeli (id, kol1, kol2) WARTOŚCI (NULL, wartość1, wartość2)";

    W powyższym przykładzie weź pod uwagę, że „table”, „col[n]” i „val[n]” mogą być zmiennymi.

    Jaki jest tego standard? Co robisz?

    Czytam odpowiedzi na podobne pytania od około 20 minut, ale wydaje się, że nie ma ostatecznej odpowiedzi na to pytanie.

    Odpowiedzi

    Załóżmy teraz, że używasz zmiennej postu bezpośredniego w zapytaniu MySQL, a następnie użyj jej w ten sposób:

    $query = "WSTAW DO `tabeli` (`id`, `name`, `email`) WARTOŚCI (" ".$_POST["id"]." ", " ".$_POST["nazwa"]." ", " ".$_POST["e-mail"]." ")";

    Jest to najlepsza praktyka używania zmiennych PHP w MySQL.

    Przeważnie w Mysql te typy identyfikatorów są używane w zapytaniach ` , " , " i ().

      " lub " używane do dołączenia ciągu jako wartości "26-01-2014 00:00:00" lub "26-01-2014 00:00:00" . Ten identyfikator jest używany tylko w funkcji ciągu „26-01-2014 00:00:00”, takiej jak now() lub suma,max.

      ` użyj, aby dołączyć tabelę lub tabelę, np. wybierz nazwę_kolumny z nazwa_tabeli, gdzie id="2"

      () służą jedynie do prostego umieszczenia części zapytania, np. wybierz nazwę_kolumny z nazwa_tabeli gdzie (id = „2” i płeć = „mężczyzna”) lub nazwa = „rakesh.

    Oprócz wszystkich (dobrze wyjaśnionych) odpowiedzi, poniżej nie wspomniano o żadnych, a często odwiedzam te pytania i odpowiedzi.

    W skrócie; MySQL myśli, że chcesz zająć się matematyką na własnej tabeli/kolumnie i interpretuj łączniki takie jak „e-mail” jako e-mail.

    Odmowa odpowiedzialności. Pomyślałem więc, że dodam to jako odpowiedź „FYI” dla tych, którzy są zupełnie nowicjuszami w pracy z bazami danych i którzy mogą nie rozumieć już opisanych terminów technicznych.

    (Powyżej znajdują się dobre odpowiedzi dotyczące charakteru pytania SQL, ale może to być również istotne, jeśli dopiero zaczynasz korzystać z PHP.)

    Być może warto zauważyć, że PHP inaczej traktuje pojedyncze i podwójne cudzysłowy...

    Ciągi znaków w pojedynczym cudzysłowie są „literałami” i reprezentują sporo ciągów WYSIWYG. Łańcuchy w cudzysłowie są interpretowane przez PHP pod kątem możliwego podstawienia zmiennych (odniesienia wsteczne w PHP nie są dokładnie ciągami znaków, wykonują polecenie w powłoce i zwracają wynik).

    $foo = "pasek"; echo "jest $foo"; // Jest $foo echo "jest $foo"; // Występuje echo słupkowe `ls -l`; // ... lista katalogów

    Jeśli kolumny i wartości tabel są zmiennymi, istnieją dwa sposoby:

    W przypadku podwójnych cudzysłowów „” pełne zapytanie brzmi:

    $query = "WSTAW DO $nazwa_tabeli (id, $col1, $col2) WARTOŚCI (NULL, "$val1", "$val2"";

    $query = "WSTAW DO ".$nazwa_tabeli." (id, ".$col1.", ".$col2.") WARTOŚCI (NULL, ".$val1."", ".$val2."" )”;

    Z pojedynczymi cudzysłowami „”:

    $query = "WSTAW DO ".$nazwa_tabeli." (id, ".$col1.", ".$col2.") WARTOŚCI (NULL, ".$val1.", ".$val2.")";

    Użyj znaczników wstecznych ``, gdy nazwa kolumny/wartości jest podobna do słowa kluczowego zastrzeżonego MySQL.

    Notatka. Jeśli określisz nazwę kolumny z nazwą tabeli, użyj znaczników wstecznych w następujący sposób:

    `nazwa_tabeli` . `nazwa_kolumny`<- Примечание: исключить. из задних клещей.

    Backticków należy używać w przypadku identyfikatorów tabel i kolumn, ale są one konieczne tylko wtedy, gdy identyfikator jest zarezerwowanym słowem kluczowym MySQL lub gdy identyfikator zawiera białe znaki lub znaki spoza zastrzeżonego zestawu (patrz poniżej). Często zaleca się, jeśli to możliwe, unikanie używania zastrzeżonych słów kluczowych jako identyfikatorów kolumn lub tabel, aby uniknąć problemów z cytowaniem.

    Pojedyncze cudzysłowy należy stosować w przypadku wartości łańcuchowych, np. na liście WARTOŚCI(). Podwójne cudzysłowy są obsługiwane przez MySQL również dla wartości łańcuchowych, ale pojedyncze cudzysłowy są szerzej akceptowane przez inne RDBMS, dlatego dobrym pomysłem jest używanie pojedynczych cudzysłowów zamiast podwójnych.

    MySQL oczekuje również, że dosłowne wartości DATE i DATETIME będą miały pojedyncze cudzysłowy jako ciągi znaków, np. „2001-01-01 00:00:00”. Więcej informacji, w szczególności o alternatywach dla używania łącznika jako separatora segmentów w ciągach dat, można znaleźć w dokumentacji literatury dotyczącej daty i godziny.

    Używając więc twojego przykładu, zacytowałbym ciąg PHP dwukrotnie i użyłbym pojedynczych cudzysłowów dla wartości „val1”, „val2” . NULL jest słowem kluczowym MySQL i specjalną (nie)wartością i dlatego nie jest używane.

    Żaden z tych identyfikatorów tabel lub kolumn nie jest słowami zastrzeżonymi ani nie używa znaków wymagających cytowania, ale i tak zacytowałem je jeden po drugim (więcej o tym później...).

    Funkcje związane z RDBMS (takie jak NOW() w MySQL) nie powinny być cytowane, chociaż ich argumenty podlegają tym samym regułom lub zasadom cytowania, o których już wspomniano.

    Backtick (`) tabela i kolumna $query = " WSTAW DO `tabeli` (`id`, `col1`, `col2`, `data`, `updated`) WARTOŚCI (NULL, "val1", "val2", "2001-01-01", NOW())"; Słowo kluczowe bez cudzysłowu ─────┴┴┴┘ │ │ │ │ │ │ │││││ Ciągi w pojedynczym cudzysłowie (”) ┴────┘ │ │ ││││ │ Pojedynczy cudzysłów (") DATA ────────┘ │││││ Funkcja niecytowana ───────┴┴┴┴┘

    Interpolacja zmienna

    Wzorce cytowania zmiennych nie ulegają zmianie, chociaż jeśli zamierzasz interpolować zmienne bezpośrednio w ciągu znaków, musi to być podwójny cudzysłów w PHP. Tylko upewnij się, że poprawnie uciekłeś do zmiennych do użycia w SQL. (Zamiast tego zaleca się użycie interfejsu API obsługującego przygotowane instrukcje jako ochronę przed iniekcją SQL).

    // To samo dotyczy zamiany niektórych zmiennych // Tutaj nazwa tabeli zmiennej $table jest ujęta w cudzysłów, a zmienne // na liście WARTOŚCI są ujęte w pojedynczy cudzysłów $query = "INSERT INTO `$stół`(`id`, `col1`, `col2`, `data`) WARTOŚCI (NULL, „$wart1”, „$wart2”, „$data”)";

    Przygotowane oświadczenia

    Podczas pracy z przygotowanymi aplikacjami należy zapoznać się z dokumentacją, aby określić, czy należy określić obiekty zastępcze aplikacji. Najpopularniejsze API dostępne w PHP, PDO i MySQLi obejmują nieautoryzowany symbole zastępcze, jak większość przygotowanych instrukcji API w innych językach:

    // Przykład PDO z nazwanymi parametrami, bez cudzysłowu $query = "WSTAW DO `tabeli` (`id`, `col1`, `col2`, `data`) WARTOŚCI (:id, :col1, :col2, :data)" ; // Przykład MySQLi z ? parametry, bez cudzysłowu $query = "WSTAW DO `tabeli` (`id`, `col1`, `col2`, `data`) WARTOŚCI (?, ?, ?, ?)";

    Symbole zwracające odwołanie wsteczne w identyfikatorach:

    Na przykład:

    To samo można zrobić z nazwami tabel i nazwami pól. To jest bardzo dobry zwyczaj, jeśli powiążesz identyfikator bazy danych z powrotem z systemem Windows.

    Sprawdź tę odpowiedź, aby dowiedzieć się więcej o wycofywaniu się.

    Teraz o podwójnych cudzysłowach i pojedynczych cudzysłowach (Michael już o tym wspomniał).

    Aby jednak zdefiniować wartość, należy użyć pojedynczych lub podwójnych cudzysłowów. Zobaczmy inny przykład.

    WSTAW DO `nazwa_tabeli` (`id, `tytuł`) WARTOŚCI (NULL, tytuł 1);

    Tutaj celowo zapomniałem owinąć tytuł 1 w cudzysłów. Serwer zaakceptuje teraz tytuł1 jako nazwę kolumny (tj. identyfikator). Aby więc wskazać, że jest to wartość, należy użyć podwójnych lub pojedynczych cudzysłowów.

    WSTAW DO `nazwa_tabeli` (`id, `tytuł`) WARTOŚCI (NULL, "tytuł1");

    Teraz, w połączeniu z PHP, cudzysłowy i pojedyncze cudzysłowy znacznie ułatwiają pisanie zapytań. Przyjrzyjmy się zmodyfikowanej wersji zapytania w Twoim pytaniu.

    $query = "WSTAW DO `tabeli` (`id`, `col1`, `col2`) WARTOŚCI (NULL, "$val1", "$val2"";

    Teraz, używając podwójnych cudzysłowów w PHP, sprawisz, że zmienne $val1 i $val2 użyją swoich wartości, tworząc w ten sposób prawidłowe zapytanie. tak jak

    $val1 = „moja wartość 1”; $val2 = „moja wartość 2”; $query = "WSTAW DO `tabeli` (`id`, `col1`, `col2`) WARTOŚCI (NULL, "$val1", "$val2"";

    WSTAW DO `tabeli` (`id`, `col1`, `col2`) WARTOŚCI (NULL, „moja wartość 1”, „moja wartość 2”)

    Było tu wiele pomocnych odpowiedzi, których kulminacją były ogólnie dwa punkty.

    1. TYŁY (`) są używane wokół nazw identyfikatorów.
    2. POJEDYNCZE CYTATY (") są używane wokół wartości.

    I jak powiedział @MichaelBerkowski

    Backticków należy używać w przypadku identyfikatorów tabel i kolumn, ale są one konieczne tylko wtedy, gdy identyfikator jest zarezerwowanym słowem kluczowym MySQL lub gdy identyfikator zawiera białe znaki lub znaki spoza zastrzeżonego zestawu (patrz poniżej). Często zaleca się, jeśli to możliwe, unikanie używania zastrzeżonych słów kluczowych jako identyfikatorów kolumn lub tabel, aby uniknąć problemów z cytowaniem.

    Istnieje przypadek, w którym identyfikator nie może być zastrzeżone słowo kluczowe lub zawierać białe znaki Lub postacie spoza limitowanego zestawu, ale koniecznie wymagają linków zwrotnych wokół nich.

    123E10 jest poprawną nazwą identyfikatora, ale także prawidłowym literałem INTEGER.

    [Bez wchodzenia w szczegóły, jak uzyskać taką nazwę identyfikatora], powiedzmy, że chcę utworzyć tabelę tymczasową o nazwie 123456e6 .

    Brak BŁĘDU w przypadku backticków.

    DB > utwórz tabelę tymczasową `123456e6` (`id` char(8)); Zapytanie OK, wpływ na 0 wierszy (0,03 s)

    BŁĄD, jeśli nie używasz wywołań zwrotnych.

    DB > utwórz tabelę tymczasową 123451e6(`id` char(8)); BŁĄD 1064 (42000): Wystąpił błąd w składni SQL; sprawdź instrukcję odpowiadającą Twojej wersji serwera MariaDB, aby znaleźć właściwą składnię do użycia w pobliżu „123451e6 (`id` char (8))” w linii 1

    Jednakże 123451a6 jest dokładną nazwą identyfikacyjną (bez znaczników wstecznych).

    DB > utwórz tabelę tymczasową 123451a6(`id` char(8)); Zapytanie OK, wpływ na 0 wierszy (0,03 s)

    Dzieje się tak całkowicie dlatego, że 1234156e6 jest również liczbą wykładniczą.

    Pojedyncze cudzysłowy należy stosować w przypadku wartości łańcuchowych, np. na liście WARTOŚCI().

    Backticki służą zwykle do wskazania identyfikatora i mogą być również bezpieczne ze względu na przypadkowe użycie zarezerwowanych słów kluczowych.

    W połączeniu w PHP i MySQL cudzysłowy podwójne i pojedyncze znacznie ułatwiają pisanie zapytań.

    W MySQL istnieją dwa typy cudzysłowów:

    1. ", aby uwzględnić literały łańcuchowe
    2. `, aby uwzględnić identyfikatory, takie jak nazwy tabel i kolumn

    A potem jest: „To jest szczególny przypadek. Można go wykorzystać jeden powyższych celów na raz, w zależności od sql_mode serwera sql_mode :

    1. Domyślny„znak” może być używany do zagnieżdżania literałów łańcuchowych „
    2. W trybie ANSI_QUOTES znak „ może być używany do włączania identyfikatorów, ANSI_QUOTES

    Poniższe zapytanie zwróci różne wyniki (lub błędy) w zależności od trybu SQL:

    WYBIERZ „kolumnę” Z tabeli GDZIE foo = „bar”

    ANSI_QUOTES wyłączone

    Zapytanie wybierze literał „kolumnę”, gdzie foo jest równe ciągowi „pasek”

    Włączono ANSI_QUOTES

    Zapytanie wybierze kolumnę kolumnę, gdzie foo równa się kolumna

    Kiedy użyć

    • Sugeruję unikanie używania „, aby Twój kod nie zależał od trybów SQL
    • Zawsze dołączaj identyfikatory, ponieważ jest to dobra praktyka (kilka pytań na temat SO omawia tę kwestię)

    Istnieje wyraźna różnica pomiędzy używaniem „” i „”.

    Kiedy „” jest używane we wszystkim, nie ma „transformacji ani tłumaczenia”. Jest wydrukowany tak, jak jest.

    W przypadku „ ” wszystko, co otacza, jest „tłumaczone lub konwertowane” na jego wartość.

    Przez tłumaczenie/konwersję rozumiem, co następuje: wszystko, co jest zawarte w pojedynczych cudzysłowach, nie zostanie „przetłumaczone” na ich wartości. Zostaną zaakceptowane, ponieważ znajdują się w cudzysłowie. Przykład: a=23 , następnie echo "$a" wygeneruje $a na standardowym wyjściu. Natomiast echo "$a" wygeneruje 23 na standardowym wyjściu.

    Zasadniczo zagłębiałem się w MySQL i PHP... w szczególności w środki bezpieczeństwa, które muszę podjąć, gdy mam do czynienia z danymi wejściowymi z baz danych i formularzy. Na razie uważam, że szczególnie godne polecenia są:

    1. Przygotowane oświadczenia
    2. Używanie _real_escape_string()
    3. NIE używaj magicznych cudzysłowów, ponieważ myli to bazy danych i kończy się dawaniem takich rzeczy jak „Nazwałeś to, nie…”.

    Wszystko jest super i podążam za tym. Zastanawiałem się jednak, czy znaki takie jak znak dolara [$], znak procentu [%] i ewentualnie inne powinny zostać zmienione. Być może zapytanie nie mogłoby zinterpretować znaku dolara jako zmiennej PHP? A co ze składnią LIKE, o której słyszałem, że używa znaku % lub nawet symbolu wieloznacznego? Przygotowane oświadczenia powinny technicznie zająć się tym wszystkim, ale chciałem po prostu być po bezpiecznej stronie i upewnić się, że wszystko dobrze uciekło. W przypadku gdybym zapomniał użyć przygotowanych stwierdzeń lub po prostu je zaniedbał, miałem nadzieję, że ta druga linia obrony podpowie mi, że mogę pozbyć się zawrotów głowy.

    Oto, czego obecnie używam do ucieczki:

    Funkcja escape($połączenie, $data)( $new_data = trim($data); $new_data = i_real_escape_string($connection, $new_data); $new_data = addcslashes($new_data, "%_$"); $new_data = htmlspecialchars ($new_data, ENT_NOQUOTES); zwróć $new_data; )

    Czy to prawda? Czy robię coś strasznie złego? Pamiętaj, że podczas zwracania danych z bazy danych będę musiał usunąć ukośniki odwrotne przed znakami $,% i _.

    Czy robię coś strasznie złego?

    Najpierw o twoich badaniach.

    Przygotowane zestawienia - jedyny cudowną rzecz, którą znalazłeś.

    Podczas używania mysqli_real_escape_string (zakładając, że używasz przygotowanych instrukcji) byłoby bezużyteczne i szkodliwe(tworząc wynik, który sam zanotowałeś: „Nazwałeś\nie…”).

    A magiczne cytaty już dawno zostały usunięte z języka – a zatem nie są nic warte.

    Zatem nawet większość Twoich pierwotnych założeń jest wyraźnie błędna.

    A teraz twoje pytanie.

    Być może zapytanie nie mogłoby zinterpretować znaku dolara jako zmiennej PHP?

    A co ze składnią LIKE, o której słyszałem, że używa znaku % lub nawet symbolu wieloznacznego?

    Tak, dobrze słyszałeś. Dokładnym celem operatora LIKE jest wyszukiwanie wzorców. Wyłączenie tych znaków w LIKE nie miałoby najmniejszego sensu.

    Za każdym razem, gdy zamierzasz użyć operatora LIKE, musisz zdecydować, którego konkretnego znaku użyć, a którego nie. Nie można skorzystać z rozwiązania jednorazowego. Nie wspominając, że we wszystkich innych interakcjach MySQL znak % nie ma żadnego specjalnego znaczenia.

    Przygotowane zestawienia powinny technicznie to wszystko załatwić

    Przygotowane instrukcje nie mają nic wspólnego ze znakami $ ani %. Przygotowane instrukcje są powiązane z iniekcją SQL, ale żaden pojedynczy znak nie może jej wywołać (czy „wstrzyknięcie” można nazwać poprawnie użytym operatorem LIKE, prawda?).

    Wreszcie najstraszniejsza część.

    W przypadku, gdy zapomnisz skorzystać z przygotowanych instrukcji lub po prostu zaniedbasz ich wykonanie,

    nic cię nie uratuje.

    A najmniej pomocna będzie funkcja, którą zaprojektowałeś.

    Podsumować.

    1. Pozbądź się tej funkcji.
    2. Używać symbole zastępcze * do reprezentowania każdej indywidualnej zmiennej w zapytaniu.
    3. Uciekaj od znaków % i _ na wejściu tylko wtedy, gdy będą one użyte w operatorze LIKE i nie chcesz, aby były interpretowane.
    4. Użyj htmlspecialchars() jako wyjścia, a nie wejścia mysql.

    * Przeczytaj przygotowane oświadczenia, jeśli nie znasz tego terminu.

    Nie musisz uciekać przed znakiem dolara. MySQL nie traktuje tego znaku specjalnie, a PHP rozpoznaje go tylko w kodzie źródłowym, a nie w wartościach łańcuchowych (chyba że wykonasz ewaluację na łańcuchu, ale to zupełnie inny robak robaków).

    Będziesz musiał uciec przed % i _ tylko wtedy, gdy użyłeś danych wejściowych użytkownika jako argumentu LIKE i nie chcesz, aby użytkownik mógł używać symboli wieloznacznych. Może się to zdarzyć, jeśli przetwarzasz formularz wyszukiwania. Nie musisz go używać podczas przechowywania w bazie danych.

    Podczas uzyskiwania dostępu do bazy danych nie musisz używać htmlspecialchars . Należy tego używać tylko podczas wyświetlania danych użytkownikowi na stronie HTML, aby zapobiec wstrzyknięciu XSS.

    W zależności od tego, jakie dane i do czego służą.

    Jeśli uznasz, że domyślne gotowe instrukcje PHP są zbyt duże i skomplikowane, sugeruję zapoznanie się z niektórymi klasami dostępnymi na githubie, aby dać ci pojęcie o uproszczonych zapytaniach.

    Przykład wstawiania zapytań z tą klasą

    $data = Array („login” => „admin”, „active” => true, „firstName” => „John”, „lastName” => „Doe”, „password” => $db->func( "SHA1(?)",Array("tajne hasło+sól")), // hasło = SHA1("tajne hasło+sól") "createdAt" => $db->now(), // utworzonyAt = TERAZ() " wygasa" => $db->now("+1Y") // wygasa = TERAZ() + interwał 1 rok // Obsługiwane interwały [s]sekunda, [m]inute, [h]godzina, [d]dzień, [Miesiąc, rok); $id = $db->insert("użytkownicy", $dane); if ($id) echo "Utworzono użytkownika. Id=" . $id; else echo "wstawienie nie powiodło się: ". $db->getLastError();


    Na początek trochę o tym, dlaczego te ukośniki są w ogóle potrzebne.
    Jeśli w zapytaniu podstawimy jakieś dane, to aby odróżnić te dane od poleceń SQL, należy je podać w cudzysłowie.
    Na przykład, jeśli piszesz
    WYBIERZ * Z tabeli GDZIE imię = Bill
    wówczas baza danych uzna, że ​​Bill to nazwa innego pola, nie znajdzie jej i zgłosi błąd. Dlatego podstawione dane (w tym przypadku imię Billa) należy ująć w cudzysłów - wówczas baza potraktuje to jako ciąg znaków, którego wartość należy przypisać do pola name:
    WYBIERZ * Z tabeli GDZIE imię = „Rachunek”
    Jednak cudzysłowy można znaleźć także w samych danych. Np,
    WYBIERZ * Z tabeli GDZIE imię = "D"Artagnan"
    Tutaj baza danych uzna, że ​​„D” to dane, a Artagnan to polecenie, którego nie zna, i również zgłosi błąd. Dlatego konieczne jest prześledzenie wszystkich danych, aby wyjaśnić bazie danych, że znalezione w nich cudzysłowy (i inne znaki specjalne) odnoszą się do danych.
    W rezultacie otrzymamy prawidłowe żądanie, które nie spowoduje błędów:
    WYBIERZ * Z tabeli GDZIE nazwa = "D\Artagnan"

    Tym samym dowiedzieliśmy się, że podstawiając w zapytaniu dane stringowe należy kierować się dwiema zasadami:
    - wszystkie wstawione dane ciągu muszą być ujęte w cudzysłów (pojedynczy lub podwójny, ale wygodniej i częściej stosuje się cudzysłów pojedynczy).
    - znaki specjalne muszą być poprzedzone ukośnikami.

    Należy szczególnie zaznaczyć: dodane ukośniki NIE trafiają do bazy danych. Są one potrzebne jedynie w żądaniu. Po uderzeniu w bazę, cięcia są odrzucane. W związku z tym częstym błędem jest używanie ukośników podczas pobierania danych z bazy danych.

    Wszystkie powyższe dotyczą danych i dat typu string. Liczby można wstawiać bez ukośników i bez cudzysłowów. Jeśli to zrobisz, to KONIECZNIE! wymuś rzutowanie danych na żądany typ przed wstawieniem ich do zapytania, na przykład:
    $id = interwał ($id);
    Jednak dla uproszczenia (i niezawodności) możesz pracować z liczbami tak samo, jak z ciągami znaków (ponieważ mysql nadal konwertuje je na żądany typ). W związku z tym prześledzimy i zacytujemy wszelkie dane wprowadzone do wniosku.

    Jest jeszcze jedna zasada - opcjonalna, ale należy jej przestrzegać, aby uniknąć błędów:
    Nazwy pól i tabel należy ująć w tylne pojedyncze cudzysłowy - „`” (klawisz z tym znakiem znajduje się na standardowej klawiaturze po lewej stronie klawisza „1”) W końcu nazwa pola może pasować do słów kluczowych mysql, ale jeśli użyj cudzysłowu, MySQL zrozumie, że wszystko jest w porządku:
    WYBIERZ * Z `tabeli` GDZIE `data` = "2006-04-04"
    Cytaty te należy rozróżnić i nie mylić ze sobą. Należy również pamiętać, że od cudzysłowów końcowych nie należy uciekać ukośnikami.

    Dowiedzieliśmy się więc, jak poprawnie podstawić dane do żądania.
    ALE! Zapytania dynamiczne nie ograniczają się do podstawiania danych. Często w zapytaniu musimy podstawiać polecenia SQL i nazwy pól. I tu już przechodzimy do tematu bezpieczeństwa:

    SQL Injection to rodzaj ataku hakerskiego, gdy dane przekazywane do skryptu zostają zmodyfikowane w taki sposób, że zapytanie wygenerowane w tym skrypcie zaczyna wykonywać coś zupełnie innego niż to, do czego było przeznaczone.
    Zasady ochrony przed takimi atakami można podzielić na dwa punkty:
    1. Praca z danymi.
    2. Praca z kontrolkami zapytań.

    Punkt pierwszy został szczegółowo omówiony powyżej. Można powiedzieć, że w istocie nie jest to obrona. Spełnienie zasad dodawania danych do zapytania podyktowane jest przede wszystkim wymogami SKŁADNI SQL. A jako efekt uboczny mamy również ochronę przed hakerami.

    Drugi punkt jest znacznie trudniejszy, ponieważ nie ma jednej uniwersalnej zasady dotyczącej danych - cudzysłów w żaden sposób nie uchroni nazwy pola przed modyfikacją przez hakera. Nie można cytować nazwy tabeli, instrukcji SQL, opcji polecenia LIMIT ani innych instrukcji.
    Dlatego główną zasadą przy zastępowaniu kontroli w żądaniu jest:
    Jeżeli chcemy dynamicznie podstawiać w zapytaniu instrukcje SQL lub nazwy pól, baz danych, tabel, to pod żadnym pozorem nie należy ich wstawiać bezpośrednio do zapytania.
    Wszystkie opcje takich dodatków muszą być zapisane z wyprzedzeniem w skrypcie i wybrane na podstawie tego, co wprowadził użytkownik.
    Przykładowo, jeśli chcesz przekazać nazwę pola do zamówienia za pomocą wyciągu, to w żadnym wypadku nie powinieneś zastępować jej bezpośrednio. Muszę to najpierw sprawdzić. Na przykład utwórz tablicę prawidłowych wartości i podstaw w żądaniu tylko wtedy, gdy w tej tablicy występuje przekazany parametr:
    $zamówienia =array("nazwa", "cena", "ilość");
    $key = array_search($_GET ["sort"], $zamówienia));
    $orderby = $zamówienia [ $key ];
    $zapytanie = "WYBIERZ * Z `tabeli` ZAMÓW PRZEZ$zamówienie" ;

    Szukamy słowa wpisanego przez użytkownika w tablicy wcześniej opisanych opcji i jeśli je znajdziemy, wybieramy odpowiedni element tablicy. Jeśli nie zostanie znalezione żadne dopasowanie, wybrany zostanie pierwszy element tablicy.
    Tym samym żądanie zostaje zastąpione nie tym, co wpisał użytkownik, ale tym, co zostało zapisane w naszym skrypcie.
    To samo należy zrobić we wszystkich innych przypadkach.
    Na przykład, jeśli klauzula WHERE jest generowana dynamicznie:
    if (!empty($_GET [ "cena" ])) $where .= "cena="" .mysql_real_escape_string ($_GET [ "cena" ]).""" ;
    $query = "WYBIERZ * Z `tabeli` GDZIE $gdzie" ;

    Trudno mi sobie wyobrazić przypadek, w którym można dynamicznie wstawić nazwę tabeli do zapytania, ale jeśli tak się stanie, to również trzeba wstawić nazwę tylko ze zbioru zapisanego wcześniej w skrypcie.
    Parametry instrukcji LIMIT muszą zostać zamienione na typ całkowity za pomocą operacji arytmetycznych lub funkcji intval().
    Nie powinieneś sądzić, że wymienione tutaj przykłady wyczerpują wszystkie opcje dynamicznego wykonywania zapytań. Wystarczy zrozumieć tę zasadę i zastosować ją we wszystkich takich przypadkach.

    Jeśli zauważysz błąd, zaznacz fragment tekstu i naciśnij Ctrl + Enter
    UDZIAŁ: