A nejmenší pomoc by vám poskytla funkce, kterou jste vyvinuli. PHP: \"Citace\". Psaní mysql dotazů, lomítek, uvozovek Vyhledávání v kódu

řetězec uvozovek (11)

Snažím se najít nejlepší způsob psaní dotazů. Také chápu, jak je důležité být konzistentní. Doposud jsem náhodně používal jednoduché uvozovky, dvojité uvozovky a zpětné zaškrtnutí bez jakéhokoli skutečného přemýšlení.

$query = "INSERT INTO table (id, col1, col2) VALUES (NULL, val1, value2)";

Ve výše uvedeném příkladu také zvažte, že „table“, „col[n]“ a „val[n]“ mohou být proměnné.

Jaký je pro to standard? Co děláš?

Četl jsem odpovědi na podobné otázky asi 20 minut, ale zdá se, že na tuto otázku neexistuje definitivní odpověď.

Odpovědi

Nyní předpokládejme, že v dotazu MySQL používáte přímou proměnnou příspěvku a poté ji použijte takto:

$query = "INSERT INTO `table` (`id`, `name`, `email`) VALUES (" ".$_POST["id"]." ", " ".$_POST["name"]." ", " ".$_POST["e-mail"].." ")";

Toto je nejlepší postup pro použití proměnných PHP v MySQL.

Tyto typy identifikátorů se používají hlavně v Mysql v dotazech ` , " , " a ().

    " nebo " použijte k zahrnutí řetězce jako hodnoty "01/26/2014 00:00:00" nebo "01/26/2014 00:00:00" . Tento identifikátor se používá pouze pro funkci řetězce "01/26/2014 00:00:00", jako je now() nebo sum ,max .

    ` použijte k zahrnutí tabulky nebo tabulky tabulky, např. vyberte název_sloupce z názvu_tabulky, kde id = "2"

    () se používají pouze k jednoduchému uzavření částí dotazu, například vyberte název_sloupce z názvu_tabulky, kde (id = "2" a pohlaví = "male") nebo jméno = "rakesh.

Kromě všech (dobře vysvětlených) odpovědí nebyly níže uvedeny žádné a na tyto otázky a odpovědi často přicházím.

Ve zkratce; MySQL si myslí, že chcete počítat na vlastní tabulku/sloupec a pomlčky jako „e-mail“ interpretujte jako e-mail .

Odmítnutí odpovědnosti. Tak jsem si řekl, že to přidám jako „pro informaci“ odpověď pro ty, kteří jsou v práci s databázemi úplně noví a nemusí rozumět již popsaným technickým termínům.

(Výše jsou dobré odpovědi týkající se SQL povahy vaší otázky, ale to může být také relevantní, pokud jste v PHP nováčkem.)

Může být důležité poznamenat, že PHP zachází s jednoduchými a dvojitými uvozovkami odlišně...

Řetězce v jednoduchých uvozovkách jsou "doslova" a je to docela hodně řetězců WYSIWYG. Řetězce v dvojitých uvozovkách jsou PHP interpretovány pro možné nahrazení proměnných (zpětné reference v PHP nejsou přesně řetězce, provedou příkaz v shellu a vrátí výsledek).

$foo = "bar"; echo "existuje $foo"; // Existuje $foo echo "existuje $foo"; // Je zde pruhová echo `ls -l`; // ... seznam adresářů

Pokud jsou tabulky a hodnoty cols proměnné, pak existují dva způsoby:

S dvojitými uvozovkami "" je úplný dotaz:

$query = "INSERT INTO $table_name (id, $col1, $col2) VALUES (NULL, "$val1", "$val2")";

$query = "INSERT INTO ".$table_name." (id, ".$col1.", ".$col2.") VALUES (NULL, ".$val1.", ".$val2."" ) ";

S jednoduchými uvozovkami "" :

$query = "INSERT INTO ".$table_name." (id, ".$col1.", ".$col2.") VALUES (NULL, ".$val1.", ".$val2."")";

Použijte backticks ``, když je název sloupce/hodnoty podobný klíčovému slovu vyhrazenému MySQL.

Poznámka. Pokud zadáte název sloupce s názvem tabulky, použijte zpětná zaškrtnutí takto:

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

Backticks by se měly používat pro identifikátory tabulek a sloupců, ale jsou potřeba pouze tehdy, když je identifikátor rezervovaným klíčovým slovem MySQL nebo když identifikátor obsahuje mezery nebo znaky mimo omezenou množinu (viz níže). Často se doporučuje vyhnout se používání vyhrazených klíčových slov jako identifikátorů sloupců nebo tabulek, pokud je to možné, aby se předešlo problému s citací.

U řetězcových hodnot, například v seznamu VALUES(), by se měly používat jednoduché uvozovky. MySQL podporuje dvojité uvozovky i pro hodnoty řetězců, ale jiné RDBMS přijímají jednoduché uvozovky, takže je dobré místo dvojitých uvozovek používat jednoduché uvozovky.

MySQL také očekává, že doslovné hodnoty DATE a DATETIME budou uváděny v jednoduchých uvozovkách jako řetězce, například "2001-01-01 00:00:00" . Další informace najdete v dokumentaci k datu a času, zejména alternativy k použití pomlčky jako oddělovače segmentů v řetězcích data.

Takže pomocí vašeho příkladu bych zdvojnásobil přetypování řetězce PHP a použil jednoduché uvozovky pro hodnoty „val1“, „val2“ . NULL je klíčové slovo MySQL a nemá hodnotu, a proto se nepoužívá.

Žádný z těchto identifikátorů tabulek nebo sloupců nejsou vyhrazená slova nebo používají znaky, které vyžadují uvozování, ale stejně jsem je citoval zpětně (o tom později...).

Funkce související s RDBMS (jako je NOW() v MySQL) by neměly být citovány, ačkoli jejich argumenty podléhají stejným pravidlům nebo již zmíněným pravidlům citování.

Zpětná (`) tabulka a sloupec ┬──── ┬──┬───────┐ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ry = ↓ ↓ ↓ $ INSERT INTO `table` (`id`, `col1`, `col2`, `date`, `updated`) VALUES (NULL, "val1", "val2", "2001-01-01", NOW())"; Klíčové slovo v uvozovkách ─────┴┴┴┘ │ │ │ │ │ │ │││││ Řetězce s jednoduchými uvozovkami ─────└└──── — ───┴── ┴────┘ │ │ │││││ DATUM ────────────└│││ DATUM ───────────└└│││ ───┴── Necitovaná funkce ───────────────────────── ──└└─—└─ — ──┴┴┴┴┘

Variabilní interpolace

Vzory uvozovek pro proměnné se nemění, i když pokud máte v úmyslu interpolovat proměnné přímo do řetězce, musí být v PHP uvozovány dvakrát. Jen se ujistěte, že jste správně escapovali proměnné pro použití v SQL. (Doporučujeme místo toho použít API, které podporuje připravené příkazy jako obranu proti SQL injection.)

// Totéž s některými náhradami proměnných // Zde je název tabulky proměnných $table zpětně uvozován a proměnné // v seznamu VALUES jsou v jednoduchých uvozovkách $query = "INSERT INTO `$stůl`(`id`, `col1`, `col2`, `date`) VALUES (NULL, "$val1", "$val2", "$date")";

Připravené výpisy

Při práci s připravenými výpisy se podívejte do dokumentace, abyste zjistili, zda by měly být zahrnuty výplně výpisů. Mezi nejoblíbenější API dostupné v PHP, PDO a MySQLi patří neoprávněný zástupné symboly, jako většina připravených instrukčních API v jiných jazycích:

// příklad PDO s pojmenovanými parametry, neuvozen $query = "INSERT INTO `table` (`id`, `col1`, `col2`, `date`) VALUES (:id, :col1, :col2, :date)" ; // Příklad MySQLi s ? parametry, neuvozen $query = "INSERT INTO `table` (`id`, `col1`, `col2`, `date`) VALUES (?, ?, ?, ?)";

Symboly, které vracejí zpětný odkaz v identifikátorech:

Například:

Totéž lze provést pro názvy tabulek a názvy polí. Toto je velmi dobrý zvyk pokud svážete ID databáze se zadními okny.

Podívejte se na tuto odpověď, abyste se dozvěděli více o zpětných inferencích.

Nyní o Double quotes & Single Quotes (Michael to již zmínil).

Chcete-li však definovat hodnotu, musíte použít jednoduché nebo dvojité uvozovky. Podívejme se na další příklad.

INSERT INTO `název_tabulky` (`id, `název`) VALUES (NULL, název1);

Tady jsem schválně zapomněl zabalit title1 do uvozovek. Server nyní přijme title1 jako název sloupce (tj. identifikátor). Chcete-li tedy uvést, že se jedná o hodnotu, musíte použít dvojité nebo jednoduché uvozovky.

INSERT INTO `název_tabulky` (`id, `název`) VALUES (NULL, "název1");

Nyní, v kombinaci s PHP, dvojité a jednoduché uvozovky usnadňují psaní dotazů. Podívejme se na upravenou verzi dotazu ve vaší otázce.

$query = "INSERT INTO `table` (`id`, `col1`, `col2`) VALUES (NULL, "$val1", "$val2"");

Nyní pomocí dvojitých uvozovek v PHP přimějete proměnné $val1 a $val2 používat své hodnoty, čímž vytvoříte platný dotaz. jako

$val1 = "moje hodnota 1"; $val2 = "moje hodnota 2"; $query = "INSERT INTO `table` (`id`, `col1`, `col2`) VALUES (NULL, "$val1", "$val2"");

INSERT INTO `table` (`id`, `col1`, `col2`) VALUES (NULL, "moje hodnota 1", "moje hodnota 2")

Bylo zde mnoho užitečných odpovědí, které obecně vyvrcholily ve dvou bodech.

  1. BACKTICKS (`) se používají kolem názvů identifikátorů.
  2. Kolem hodnot se používají SINGLE UMOTES (")

A jak řekl @MichaelBerkowski

Backticks by se měly používat pro identifikátory tabulek a sloupců, ale jsou potřeba pouze tehdy, když je identifikátor rezervovaným klíčovým slovem MySQL nebo když identifikátor obsahuje mezery nebo znaky mimo omezenou množinu (viz níže). Často se doporučuje vyhnout se používání vyhrazených klíčových slov jako identifikátorů sloupců nebo tabulek, pokud je to možné, aby se předešlo problému s citací.

Existuje případ, kdy identifikátor nemůže být vyhrazené klíčové slovo nebo obsahovat prázdné znaky nebo znaky mimo omezenou množinu ale určitě vyžadují zpětné odkazy kolem nich.

123E10 je platný název identifikátoru, ale také platný literál INTEGER.

[Aniž bychom zacházeli do podrobností, jak byste získali takové ID jméno] Řekněme, že chci vytvořit dočasnou tabulku s názvem 123456e6.

Žádná CHYBA na backticks.

DB > vytvořit dočasnou tabulku `123456e6` (`id` char(8)); Dotaz je v pořádku, ovlivněno 0 řádků (0,03 s)

CHYBA, pokud nepoužíváte zpětná volání.

DB > vytvořit dočasnou tabulku 123451e6 (`id` char (8)); ERROR 1064 (42000): Máte chybu v syntaxi SQL; podívejte se do manuálu, který odpovídá verzi vašeho serveru MariaDB, kde najdete správnou syntaxi pro použití blízko "123451e6 (`id` char (8))" na řádku 1

123451a6 je však dobré ID jméno (bez zpětných zaškrtnutí).

DB > vytvořit dočasnou tabulku 123451a6 (`id` char (8)); Dotaz je v pořádku, ovlivněno 0 řádků (0,03 s)

Je to zcela proto, že 1234156e6 je také exponenciální číslo.

U řetězcových hodnot, například v seznamu VALUES(), by se měly používat jednoduché uvozovky.

Zpětné zatáčky se obvykle používají k označení identifikátoru a mohou být také bezpečné kvůli občasnému použití vyhrazených klíčových slov.

V kombinaci s PHP a MySQL, dvojité a jednoduché uvozovky výrazně zjednodušují dobu psaní dotazů.

V MySQL jsou dva typy uvozovek:

  1. " pro zahrnutí řetězcových literálů
  2. ` pro zahrnutí identifikátorů, jako jsou názvy tabulek a sloupců

A pak je tu "toto je speciální případ. Dá se použít pro." jeden z výše uvedených cílů najednou v závislosti na sql_mode serveru:

  1. Výchozí"znak" lze použít k vnoření řetězcových literálů "
  2. V režimu ANSI_QUOTES " lze symbol použít k zahrnutí identifikátorů, ANSI_QUOTES

Následující dotaz vytvoří různé výsledky (nebo chyby) v závislosti na režimu SQL:

VYBERTE "sloupec" Z tabulky WHERE foo = "bar"

ANSI_QUOTES zakázáno

Dotaz vybere řetězcový doslovný "sloupec", kde sloupec foo se rovná řetězci "bar"

Povoleno ANSI_QUOTES

Dotaz vybere sloupec sloupec, kde sloupec foo se rovná sloupci

Kdy použít

  • Navrhuji, abyste se vyhnuli použití ", aby váš kód nezávisel na režimech SQL
  • Vždy uvádějte identifikátory, protože je to osvědčený postup (diskutujte o tom několik otázek o SO)

Mezi použitím " " a " " je jasný rozdíl .

Když se v celém textu použije " ", nedochází k žádné "transformaci ani překladu". Je vytištěna tak, jak je.

S " " je vše, co obklopuje, "přeloženo nebo transformováno" na svou hodnotu.

Překladem/převodem mám na mysli toto: cokoliv obsažené v jednoduchých uvozovkách nebude „přeloženo“ na jejich hodnoty. Budou přijaty, protože jsou v uvozovkách. Příklad: a=23, pak echo "$a" vygeneruje $a na standardním výstupu. Zatímco echo "$a" bude produkovat 23 na standardním výstupu.

(PHP 4 >= 4.3.0, PHP 5)

mysql_real_escape_string — Escape speciální znaky v řetězci pro použití v příkazu SQL

Popis

mysql_real_escape_string (řetězec $unescaped_string [, zdroj $link_identifier = NULL]): tětiva

Escape speciální znaky v unescaped_string , přičemž bere v úvahu aktuální znakovou sadu připojení, takže je bezpečné je umístit do mysql_query(). Pokud mají být vkládána binární data, musí být tato funkce použita.

mysql_real_escape_string() volá funkci knihovny MySQL mysql_real_escape_string, která před následující znaky přidává zpětná lomítka: \x00, \n, \r, \ , " , " a \x1a.

Tato funkce musí být vždy (až na výjimky) použita k zabezpečení dat před odesláním dotazu do MySQL.

Pozor

Zabezpečení: výchozí znaková sada

Znaková sada musí být nastavena buď na úrovni serveru, nebo pomocí funkce API mysql_set_charset() aby to ovlivnilo mysql_real_escape_string() . Další informace naleznete v části koncepty o znakových sadách.

Parametry

unescaped_string

Řetězec, který má uniknout.

Identifikátor_linku

Připojení MySQL. Pokud není zadán identifikátor odkazu, použije se poslední odkaz, který otevřel mysql_connect() se předpokládá. Pokud žádný takový odkaz nenajde, pokusí se jej vytvořit mysql_connect() byl volán bez argumentů. Pokud není nalezeno nebo navázáno žádné spojení, an E_VAROVÁNÍ generuje se chyba úrovně.

Návratové hodnoty

Vrátí uniklý řetězec, popř NEPRAVDIVÉ na chybu.

Chyby/výjimky

Provedení této funkce bez připojení k MySQL se také spustí E_VAROVÁNÍ chyby na úrovni PHP. Tuto funkci provádějte pouze s platným připojením MySQL.

Příklady

Příklad č. 1 Jednoduchý mysql_real_escape_string() příklad

//Připojit
$link = mysql_connect("mysql_host" , "mysql_user" , "mysql_password" )
OR die(mysql_error());

//Dotaz
$query = sprintf ( "SELECT * FROM users WHERE user="%s" AND password="%s"",
mysql_real_escape_string($user),
mysql_real_escape_string($heslo));
?>

Příklad č. 2 mysql_real_escape_string() vyžaduje příklad připojení

Tento příklad ukazuje, co se stane, když při volání této funkce není přítomno připojení k MySQL.

Výše uvedený příklad vytvoří něco podobného jako:

Upozornění: mysql_real_escape_string(): Žádný takový soubor nebo adresář v /this/test/script.php na řádku 5 Upozornění: mysql_real_escape_string(): V /this/test/script.php na řádku 5 nelze vytvořit odkaz na server bool(false) string(41) "SELECT * FROM herci WHERE last_name = """

Příklad #3 Příklad SQL Injection Attack

// Nezkontrolovali jsme $_POST["heslo"], mohlo by to být cokoli, co uživatel chtěl! Například:
$_POST [ "username" ] = "aidan" ;
$_POST [ "heslo" ] = "" NEBO ""="" ;

// Dotaz na databázi pro kontrolu, zda existují nějací odpovídající uživatelé
$query = ( $_POST [ "uživatelské jméno" ]) " AND password=" ( $_POST [ "heslo" ]) "" ;
mysql_query($dotaz);

// To znamená, že dotaz odeslaný do MySQL by byl:
echo $dotaz ;
?>

Dotaz odeslaný do MySQL:

To by umožnilo komukoli přihlásit se bez platného hesla.

Poznámky

Před použitím je vyžadováno připojení k MySQL mysql_real_escape_string() jinak chyba úrovně E_VAROVÁNÍ se generuje a NEPRAVDIVÉ je vráceno. Pokud link_identifier není definován, použije se poslední připojení MySQL.

Poznámka: mysql_real_escape_string() neuteče % a _ . Jedná se o zástupné znaky v MySQL, pokud jsou kombinovány s JAKO, GRANT nebo ZRUŠIT.

před 8 lety

Jen malá funkce, která napodobuje původní mysql_real_escape_string, ale která nepotřebuje aktivní připojení mysql.Mohlo by být implementováno jako statická funkce v databázové třídě.Doufám, že to někomu pomůže.

funkce mysql_escape_mimic ($inp) (
if(is_array($inp))
return array_map (__METHOD__ , $inp );

If(!empty($inp ) && is_string ($inp )) (
return str_replace (array("\\" , "\0" , "\n" , "\r" , """ , """, "\x1a" ), array("\\\\" , "\ \0" , "\\n" , "\\r" , "\\"" , "\\"" , "\\Z" ), $inp );
}

Návrat $inp ;
}
?>

před 13 lety

Všimněte si, že mysql_real_escape_string nepřidává zpětná lomítka před \x00, \n, \r a \x1a, jak je uvedeno v dokumentaci, ale ve skutečnosti nahrazuje znak reprezentací přijatelnou pro MySQL pro dotazy (např. \n je nahrazeno "\ n" literteral). (\, ", a " jsou uvozeny, jak je zdokumentováno) To nemění způsob, jakým byste tuto funkci měli používat, ale myslím, že je dobré to vědět.

před 6 lety

Žádná diskuse o escapování není úplná, aniž byste všem řekli, že byste v zásadě nikdy neměli používat externí vstup ke generování interpretovaného kódu. To platí pro příkazy SQL nebo cokoli, co byste nazvali jakoukoli funkcí „eval“.

Takže namísto použití této strašně nefunkční funkce použijte parametrické připravené příkazy.

Upřímně řečeno, používání dat poskytnutých uživatelem k sestavení příkazů SQL by mělo být považováno za profesionální nedbalost a váš zaměstnavatel nebo klient by vás měli nést k odpovědnosti za to, že nepoužíváte parametrické připravené příkazy.

Co to znamená?

To znamená, že místo vytvoření příkazu SQL, jako je tento:

"INSERT INTO X (A) VALUES(".$_POST["a"].)"

K provedení příkazu, který vypadá takto: měli byste použít funkci Prepare() () mysqli:

"INSERT IN TO X (A) VALUES(?)"

Poznámka: To neznamená, že byste nikdy neměli generovat dynamické příkazy SQL. To znamená, že byste nikdy neměli používat data poskytnutá uživatelem ke generování těchto příkazů. Všechna data poskytnutá uživatelem by měla být předána jako parametry příkazu poté, co byl byl připraven.

Pokud tedy například vytváříte malý rámec a chcete provést vložení do tabulky na základě identifikátoru URI požadavku, je ve vašem nejlepším zájmu nepřebírat hodnotu $_SERVER["REQUEST_URI"] (nebo jakoukoli jeho část) a přímo to zřetězit se svým dotazem. Místo toho byste měli analyzovat část hodnoty $_SERVER["REQUEST_URI"], kterou chcete, a namapovat ji pomocí nějakého druhu funkce nebo asociativního pole na neuživatele poskytnutá hodnota. Pokud mapování nevytváří žádnou hodnotu, víte, že s daty poskytnutými uživatelem není něco v pořádku.

Nedodržení tohoto bylo příčinou řady problémů s injekcí SQL v rámci Ruby On Rails, i když používá parametricky připravené příkazy. Takto byl GitHub v jednu chvíli hacknut. Žádný jazyk tedy není vůči tomuto problému imunní. To je důvod, proč je to obecný osvědčený postup a ne něco specifického pro PHP a proč byste to měli OPRAVDU přijmout.

Také byste měli stále provádět nějakou validaci dat poskytovaných uživateli, i když používáte parametricky připravené příkazy. Je to proto, že data poskytnutá uživatelem se často stanou součástí nějakého generovaného kódu HTML a chcete zajistit, aby data poskytnutá uživatelem nezpůsobila bezpečnostní problémy v prohlížeči.

před 9 lety

V příkladu č. 2 o SQL injection je zajímavý vtip: AND má prioritu před OR, takže vložený dotaz se ve skutečnosti provede jako WHERE (user="aidan" AND password="") NEBO ""="", takže místo toho při vrácení databázového záznamu odpovídajícího libovolnému uživatelskému jménu (v tomto případě „aidan“) by to ve skutečnosti vrátilo VŠECHNY databázové záznamy. V žádném konkrétním pořadí. Útočník by se tedy mohl přihlásit jako jakýkoli účet, ale ne nutně s jakýmkoli kontrolu nad tím, o jaký účet se jedná.

Potenciál útoku by samozřejmě mohl jednoduše upravit své parametry tak, aby zacílily na konkrétní uživatele, kteří ho zajímají:

//Např. útočníkovy hodnoty
$_POST [ "uživatelské jméno" ] = "" ;
$_POST["heslo"] = "" NEBO uživatel = "administrátor" AND "" = "";

// Chybný dotaz
$dotaz = "SELECT * FROM users WHERE user="$_POST [ uživatelské jméno ] " AND heslo=" $_POST [ heslo ] "" ;

echo $dotaz ;

// Dotaz odeslaný do MySQL by zněl:
// SELECT * FROM users WHERE user="" AND password="" OR user="administrator" AND ""="";
// což by umožnilo komukoli získat přístup k účtu s názvem "administrátor"

?>

před 1 rokem

@feedr
Jeho poznámku jsem zpracoval následovně:
$string = "asda\0sd\x1aas\\\\\\\\dasd\"asdasd\na\"\"sdasdad";
$array1 = pole("\\\\\\\\", "\0", "\n", "\r", """, """, "\x1a");
$array2 = pole("\\\\\\\\\\\\\\\\\", "\\\0", "\\\n", "\\\r", "\\\ " ", "\\\"", "\\\Z");
echo($string);
echo(PHP_EOL);
for($i=0; $i if ($i==0)
$p = "/(?jiný
$p = "/(?echo($i);
echo($p);
echo($array2[$i]);
$string = preg_replace($p, $array2[$i], $string);
echo("\t");
echo($string);
echo(PHP_EOL);
}
echo(PHP_EOL);
echo($string);

Před 2 roky

Chcete-li citovat Sama na Numb Safari

[ "Žádná diskuse o escapování není úplná, aniž byste všem řekli, že byste v zásadě nikdy neměli používat externí vstup ke generování interpretovaného kódu. To platí pro příkazy SQL nebo cokoli, co byste nazvali jakoukoli funkcí "eval".

Takže namísto použití této strašně nefunkční funkce použijte parametrické připravené příkazy.

Upřímně řečeno, používání dat poskytnutých uživatelem k sestavení příkazů SQL by mělo být považováno za profesionální nedbalost a váš zaměstnavatel nebo klient by vás měl nést k odpovědnosti za to, že nepoužíváte parametrické připravené příkazy." ]

Sam má pravdu...........

Nemyslím si však, že je rozumné přestat se sanitací a úkol jednoduše předat parametricky připraveným výkazům.

Konkrétní vývojář pracující v konkrétní situaci bude vždy vědět více o platném vstupu (specifickém pro daný kontext).

Pokud požádáte uživatele, aby předal hodnotu, kterou jste mu již zadali, a víte, že všechny takové hodnoty začínají AB****** a řetězec by měl mít délku 7 nebo 11, ale nikdy jinou délku, pak máte základ dobrého předdezinfekce – různé povolené délky řetězce mohou naznačovat starší data.

Nikdy bych nechtěl jednoduše předat odpadky, které mohl uživatel se zlými úmysly předat prostřednictvím formuláře, do parametrických připravených výpisů, vždy bych chtěl nejprve provést vlastní kontrolu zdravého rozumu a v některých případech se mohou mýlit na straně opatrnosti a jednoduše zvolte úplné zrušení operace databáze.

Tímto způsobem se moje DB nezanáší nebezpečnými příkazy, které jsou bezpečné - prostě se nezanáší, což je lepší.

Zabezpečení ve vrstvách – sanitaci a validaci je třeba stále zvážit v každé situaci PŘED použitím připravených prohlášení.

Kromě toho, pokud mohu číst do oficiálního doc
==============================================

„Únik a SQL injection

Vázané proměnné se odesílají na server odděleně od dotazu a nemohou do něj tedy zasahovat. Server používá tyto hodnoty přímo v okamžiku provádění, po analýze šablony příkazu. Vázané parametry není nutné escapovat, protože se nikdy nenahrazují přímo do řetězce dotazu"

To mi naznačuje, že nebezpečí se ve vnitřnostech vyhýbá alternativním řešením, nikoli zrušením.

To znamená, že velký projekt s neúplnou konverzí na připravené příkazy, starý kód v různých částech organizace nebo servery, které spolu mluví, to vše může přenést špatné zprávy z imunního místa nebo situace na místo, které není imunní.

Dokud je sanitace kompetentně prováděna bez vzniku dalších rizik, pak bych osobně zůstal u určitých vrstev sanitace a poté vyvolal připravená prohlášení.


Nejprve trochu o tom, proč jsou tato lomítka obecně potřebná.
Pokud do dotazu dosadíme jakákoliv data, pak aby byla tato data odlišena od SQL příkazů, musí být umístěna v uvozovkách.
Například když píšete
SELECT * FROM tabulky WHERE jméno = Bill
pak databáze rozhodne, že Bill je název jiného pole, nenajde ho a vyhodí chybu. Nahrazovaná data (v tomto případě jméno Bill) proto musí být uzavřena v uvozovkách - databáze je pak bude považovat za řetězec, jehož hodnota musí být přiřazena do pole názvu:
SELECT * FROM table WHERE name = "Bill"
Uvozovky se však mohou objevit i v samotných datech. Např,
SELECT * FROM table WHERE name = "D"Artagnan"
Zde databáze rozhodne, že "D" jsou data a Artagnan je příkaz, který nezná, a také vyhodí chybu. Proto je nutné dohledat všechna data, abychom databázi vysvětlili, že uvozovky (a některé další speciální znaky) v nich nalezené odkazují na data.
V důsledku toho obdržíme správný požadavek, který nezpůsobí chyby:
SELECT * FROM table WHERE name = "D\"Artagnan"

Zjistili jsme tedy, že při nahrazování řetězcových dat do dotazu je třeba dodržovat dvě pravidla:
- všechna vložená data řetězce musí být uzavřena v uvozovkách (jednoduché nebo dvojité, ale jednoduché jsou pohodlnější a častěji používané).
- speciální znaky musí být escapovány lomítky.

Je třeba zvláště poznamenat: přidaná lomítka NEVSTUPUJÍ do databáze. Jsou potřeba pouze v žádosti. Při dopadu na základnu jsou lomítka odhozena. V souladu s tím je častou chybou použití stripslashes při získávání dat z databáze.

Vše výše uvedené platí pro data a data řetězců. Čísla lze vkládat bez koncovek nebo je obklopovat uvozovkami. Pokud to uděláte, pak NEZBYTNĚ! před vložením do dotazu vynutit požadovaný typ dat, například:
$id = intval ($id);
Pro jednoduchost (a spolehlivost) však můžete pracovat s čísly jako s řetězci (protože mysql je stále převádí na požadovaný typ). V souladu s tím vysledujeme všechna data vložená do požadavku a uzavřeme je do uvozovek.

Existuje také jedno další pravidlo - volitelné, ale mělo by se dodržovat, abyste se vyhnuli chybám:
Názvy polí a tabulek by měly být uzavřeny v zadních jednoduchých uvozovkách - "`" (klávesa s tímto symbolem je umístěna na standardní klávesnici vlevo od klávesy "1") Koneckonců, název pole se může shodovat s mysql klíčová slova, ale pokud použijeme zpětnou uvozovku, pak MySQL pochopí, že je vše správně:
SELECT * FROM `table` WHERE `date` = "2006-04-04"
Tyto uvozovky byste měli rozlišovat a nezaměňovat je za druhé. Měli byste také pamatovat na to, že zpětné zarážky neuniknou lomítky.

Naučili jsme se tedy, jak správně nahradit data do požadavku.
ALE! Dynamická konstrukce dotazu není omezena na substituci dat. Často musíme do dotazu nahradit SQL příkazy a názvy polí. A tady přejdeme k tématu bezpečnosti:

SQL Injection je metoda hackerského útoku, kdy jsou data přenášená do skriptu upravena tak, že dotaz vygenerovaný v tomto skriptu začne provádět něco úplně jiného, ​​než k čemu byl určen.
Pravidla ochrany proti takovým útokům lze rozdělit do dvou bodů:
1. Práce s daty.
2. Práce s ovládacími prvky dotazu.

První bod jsme podrobně probrali výše. Dá se říci, že to ve skutečnosti není obrana. Dodržování pravidel pro přidávání dat do dotazu je diktováno především požadavky SQL SYNTAX. A jako vedlejší efekt tu máme i ochranu proti hackování.

Druhý bod je mnohem obtížnější, protože neexistuje jediné univerzální pravidlo pro data - zpětné zaškrtnutí neochrání název pole před úpravou hackerem. Není možné používat uvozovky k ochraně názvů tabulek, příkazů SQL, parametrů příkazů LIMIT a dalších příkazů.
Základní pravidlo při nahrazování ovládacích prvků do dotazu tedy zní:
Pokud potřebujete dynamicky vkládat SQL příkazy nebo názvy polí, databází, tabulek do dotazu, pak je za žádných okolností nevkládejte přímo do dotazu.
Všechny možnosti pro takové doplňky musí být zapsány v ADVANCE ve vašem skriptu a vybrány na základě toho, co uživatel zadal.
Pokud například potřebujete předat název pole do objednávky operátorem, pak jej za žádných okolností nenahrazujte přímo. Nejprve to musíme zkontrolovat. Vytvořte například pole platných hodnot a nahraďte je do požadavku pouze v případě, že je v tomto poli přítomen předaný parametr:
$orders =array("název" , "cena" , "množství" );
$key = array_search($_GET["sort"], $orders));
$objednávka = $objednávky [ $klíč ];
$dotaz = "VYBRAT * Z "tabulky" ORDER BY$objednávka " ;

V poli předem popsaných možností vyhledáme slovo zadané uživatelem, a pokud ho najdeme, vybereme odpovídající prvek pole. Pokud není nalezena žádná shoda, bude vybrán první prvek pole.
Do požadavku tedy není dosazeno to, co uživatel zadal, ale to, co bylo napsáno v našem skriptu.
Totéž musí být provedeno ve všech ostatních případech.
Pokud je například dynamicky generována klauzule WHERE:
if (!empty($_GET [ "price" ])) $where .= "price="" . mysql_real_escape_string ($_GET [ "cena" ]). """ ;
$query = "SELECT * FROM `table` WHERE $where " ;

Je pro mě těžké si představit případ, kdy lze název tabulky vložit do dotazu dynamicky, ale pokud k tomu dojde, pak je také potřeba název vložit pouze ze sady předdefinované ve skriptu.
Parametry operátoru LIMIT by měly být vynuceny na celočíselný typ pomocí aritmetických operací nebo funkce intval().
Nemyslete si, že zde uvedené příklady vyčerpají všechny možnosti pro konstrukci dynamického dotazu. Musíte jen pochopit princip a aplikovat ho ve všech takových případech.

Vzhledem k povaze mé práce musím provádět bezpečnostní audity zdrojového kódu webových aplikací.
Spousta webových aplikací a spousta kódu...

Není žádným tajemstvím, že zranitelnost vkládání SQL je nejběžnější ze všech zranitelností webových aplikací na straně serveru. Jsou platformy a frameworky, kde jsou takové věci téměř úplně vyloučené, například ORM apod. Ale statistiky nám vytrvale říkají o absolutní převaze webových aplikací s jednoduchými zřetězenými SQL dotazy na internetu. Navíc existují případy, kdy je ORM obecně použitelné Nemůže například, když na uživatelských datech musí záviset nejen parametry výrazů, ale i samotná logika dotazu na úrovni operátora.

Takže, začněme.

Zbytečný únik postav
Nachází se v 83 % webových aplikací PHP zranitelných vůči injekcím SQL
Použití funkce escape pro znaky jako např
mysql_escape_string
mysql_real_escape_string
čárky
bez uvozovek. Nejčastěji se to projevuje v číselných parametrech (všechny druhy *_id).
Příklad
$sql = "VYBERTE uživatele ZE seznamu uživatelů WHERE userid=".mysql_real_escape_string($_GET["uid"]);

Zdá se, že je to bezpečný kód, ale pouze na povrchu. Vplížil se sem nejběžnější vzor SQL injekcí v PHP v mé praxi. Aby mohl útočník zaútočit na tuto chybu zabezpečení, musí se jednoduše vyhnout použití znaků " " \x00 \r \n \x1a v útočném vektoru.
Například:
/index.php?uid=-777 UNION SELECT heslo ZE seznamu uživatelů

Hledejte v kódu
Komplikované sémantikou jazyka. Pro jednoduché vyhledávání můžete použít egrep:
egrep -Rin "(vyberte|aktualizovat|vložit|smazat|nahradit).*(z|nastavit|do).*(mysql_escape_string|mysql_real_escape_string|přidat lomítka)" . | grep -v "[\""]["\"]"

Logika vyhledávacího výrazu je následující: najděte všechny řádky, ve kterých není žádná sekvence znaků uvozovek ("", "", "", "") vlevo od funkcí filtrování. Tato metoda samozřejmě není zdaleka 100%, ale pro provádění sémantické analýzy není možné vyžadovat regulární výraz.
Pro snazší zobrazení informací můžete funkci barevně zvýraznit v konzole:
egrep -Rin "(vyberte|aktualizovat|vložit|smazat|nahradit).*(z|nastavit|do).*(mysql_escape_string|mysql_real_escape_string|přidat lomítka)" . | grep -v "[\""]["\"]" | egrep --color "(mysql_escape_string|mysql_real_escape_string|addslashes)"

K ochraně proti této zranitelnosti zástupných znaků je nejlepší použít přetypování.
To vždy funguje rychleji a je spolehlivější než všechny druhy filtrování a promítání.
Ve výše uvedeném příkladu by oprava mohla vypadat takto:
$sql = "VYBRAT uživatele ZE seznamu uživatelů WHERE userid=".intval($_GET["uid"]);

Tímto krátký esej končí. Vyzývám všechny webové vývojáře, aby se pokusili zkontrolovat své zdroje pro takové návrhy. Ještě lépe rozšiřte daný vyhledávací skript pro lidi.

V podstatě jsem se tedy ponořil hluboko do oblastí MySQL a PHP... konkrétně do bezpečnostních opatření, která bych měl přijmout při práci s databází a vstupy do formulářů. Doposud jsem zjistil, že je vysoce doporučeno následující:

  1. Připravené výpisy
  2. Použití _real_escape_string()
  3. NEPOUŽÍVEJTE magické uvozovky, protože to mate databáze a nakonec vám to dá věci jako „Nevolal jsi to...“.

To je všechno skvělé a sleduji to. Přemýšlel jsem však, zda mám uniknout znakům jako znak dolaru [$], znak procenta [%] a možná i další. Mohl dotaz interpretovat znak dolaru jako proměnnou PHP? A co syntaxe LIKE, kterou jsem slyšel, používá symbol % nebo dokonce zástupný znak? Připravené výpisy by se o to všechno měly technicky postarat, ale chtěl jsem být na bezpečné straně a ujistit se, že jsem vše správně zakryl. V případech, kdy zapomenu použít připravená prohlášení nebo je prostě zanedbávám, jsem doufal, že tato druhá linie obrany mi může napovědět, že se mohu zbavit závratí.

Zde je to, co aktuálně používám k útěku:

Funkce escape($connection, $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); return $new_data; )

Je to tedy správné? Dělám něco strašně špatně? Vezměte prosím na vědomí, že při vrácení dat databáze budu muset odstranit zpětná lomítka před znaky $,% a _.

Dělám něco strašně špatně?

Nejprve o vašem výzkumu.

Připravené výpisy - jedinýúžasná věc, kterou jsi našel.

I když použití mysqli_real_escape_string (za předpokladu, že používáte připravené příkazy) by bylo zbytečné a škodlivé(vytvoření výsledku, který jste si sami poznamenali: "Volal jsi není\t...").

A Magic Quotes byly z jazyka již dávno odstraněny - takže opravdu nestojí za nic.

Takže i většina vašich počátečních předpokladů je zjevně špatná.

Nyní k vaší otázce.

Mohl dotaz interpretovat znak dolaru jako proměnnou PHP?

A co syntaxe LIKE, kterou jsem slyšel, používá symbol % nebo dokonce zástupný znak?

Ano, slyšeli jste správně. Přesným účelem operátoru LIKE je provádět vyhledávání vzorů. Zakázat tyto postavy v LIKE by nedávalo sebemenší smysl.

Pokaždé, když budete používat operátor LIKE, musíte se rozhodnout, který konkrétní znak použijete a který nepovolíte. Nelze použít jednorázové řešení. Nemluvě o tom, že ve všech ostatních interakcích mysql nemá znak % žádný zvláštní význam.

To vše by se technicky měly postarat připravené výkazy

Připravené výpisy nemají nic společného se znaky $ nebo %. Připravené příkazy odkazují na SQL injection, ale žádný znak to nemůže způsobit (správně použitý operátor LIKE by se nedal nazvat "injection", že?).

Konečně k tomu nejhoršímu.

V případě, že zapomenete použít připravená prohlášení nebo je jednoduše zapomenete dodržovat,

nic tě nezachrání.

A nejmenší pomoc by vám poskytla funkce, kterou jste vyvinuli.

Shrnout.

  1. Zbavte se této funkce.
  2. Použití výplně * reprezentovat každou jednotlivou proměnnou v dotazu.
  3. Escape % a _ znaky na vstupu pouze v případě, že budou použity v operátoru LIKE a nechcete, aby byly interpretovány.
  4. Pro výstup použijte htmlspecialchars(), nikoli vstup mysql.

*přečtěte si připravená prohlášení, pokud vám tento termín není znám.

Nemusíte se vyhýbat znaku dolaru. MySQL se na tento znak konkrétně nedívá a PHP jej rozpoznává pouze ve zdrojovém kódu, nikoli v hodnotách řetězců (pokud na řetězci nevoláte eval, ale to je úplně jiný červ).

Pokud byste jako argument LIKE používali vstup uživatele a nechtěli byste, aby uživatel mohl používat zástupné znaky, museli byste kód % a _ ukončit. K tomu může dojít, pokud zpracováváte vyhledávací formulář. Při ukládání do databáze jej nemusíte používat.

Při přístupu k databázi nemusíte používat htmlspecialchars. Toto by se mělo používat pouze při zobrazování dat uživateli na stránce HTML, aby se zabránilo vkládání XSS.

Podle toho, jaká data a k čemu slouží.

Pokud zjistíte, že výchozí přednastavené příkazy PHP jsou příliš velké a složité, doporučuji vám podívat se na některé třídy dostupné na githubu, abyste získali představu o zjednodušených dotazech.

Příklad vkládání dotazů s touto třídou

$data = Array ("login" => "admin", "active" => true, "firstName" => "Jan", "lastName" => "Doe", "password" => $db->func( "SHA1(?)",Array ("tajné heslo+sůl")), // heslo = SHA1("tajné heslo+sůl") "createdAt" => $db->now(), // createdAt = NOW() " expires" => $db->now("+1Y") // vyprší = NOW() + interval 1 rok // Podporované intervaly [s]sekunda, [min]minuta, [h]hodina, [d]den, [Měsíc rok); $id = $db->insert("uživatelé", $data); if ($id) echo "uživatel byl vytvořen. Id=" . $id; else echo "vložení se nezdařilo: " . $db->getLastError();