it-swarm-eu.dev

Jsou připravené příkazy 100% bezpečné proti SQL injekci?

Jsou připravené příkazy ve skutečnosti 100% bezpečné proti SQL injekci za předpokladu, že všechny parametry poskytnuté uživatelem jsou předávány jako parametry vázané na dotaz?

Kdykoli vidím lidi, kteří používají staré mysql_ funkce na StackOverflow (což je, bohužel, příliš často) obecně říkám lidem, že připravené příkazy jsou Chuck Norris (nebo Jon Skeet) z SQL injekčních bezpečnostních opatření.

Nikdy jsem však neviděl žádnou dokumentaci, která kategoricky uvádí „toto je 100% bezpečné“. Chápu je, že oddělují jazyk dotazu a parametry až k předním dveřím serveru, který je potom považuje za samostatné entity.

Mám pravdu v tomto předpokladu?

80
Polynomial

Garance 100% bezpečné před SQL injekcí? Nechápu to (ode mě).

V zásadě by vaše databáze (nebo knihovna ve vašem jazyce, která interaguje s db), mohla implementovat připravené příkazy s vázanými parametry nebezpečným způsobem citlivým na nějaký druh pokročilého útoku, například využíváním přetečení vyrovnávací paměti nebo nulových koncových znaků u uživatelů - za předpokladu, řetězce, atd. (Dalo by se argumentovat, že tyto typy útoků by neměly být nazývány SQL injection, protože jsou zásadně odlišné, ale to je jen sémantika).

Nikdy jsem neslyšel o žádném z těchto útoků na připravené příkazy na reálných databázích v terénu a důrazně doporučuji používat vázané parametry, aby se zabránilo SQL injekci. Bez vázaných parametrů nebo vstupní hygieny je jeho triviální provádění SQL injekce. S pouhými vstupními sanitárními zařízeními je poměrně často možné kolem sanitace najít nejasnou mezeru.

Při vázaných parametrech je plán provádění dotazů SQL předem naplánován, aniž by se spoléhal na vstup uživatele, což by nemělo umožnit vstřikování SQL (protože vložené uvozovky, symboly komentářů atd. Se vkládají pouze do již sestaveného příkazu SQL).

Jediným argumentem proti použití připravených příkazů je, aby vaše databáze optimalizovala vaše plány provádění v závislosti na skutečném dotazu. Většina databází, když je zadán úplný dotaz, je dostatečně inteligentní, aby provedla optimální plán provádění; např. pokud dotaz vrátí velké procento tabulky, bude chtít projít celou tabulkou a najít shody; zatímco pokud to bude jen získat několik záznamů, můžete provést indexové vyhledávání [1] .

ÚPRAVA: Reakce na dvě kritiky (které jsou pro komentáře příliš dlouhé):

Zaprvé, jak jiní poznamenali ano, každá relační databáze podporující připravené příkazy a vázané parametry nemusí nutně předkompilovat připravený příkaz, aniž by přihlížela k hodnotě vázaných parametrů. Mnoho databází to obvykle dělá, ale je také možné, aby databáze při určování plánu provádění přihlížely k hodnotám vázaných parametrů. To není problém, protože struktura připraveného příkazu s oddělenými vázanými parametry usnadňuje databázi čistě odlišit příkaz SQL (včetně klíčových slov SQL) od dat ve vázaných parametrech (kde nebude nic ve vázaném parametru interpretováno jako klíčové slovo SQL). To není možné při vytváření příkazů SQL zřetězení řetězců, kde by se promíchaly proměnné a klíčová slova SQL.

Za druhé, jak upozorňuje jiná odpověď , použití vázaných parametrů při volání příkazu SQL v jednom bodě programu bezpečně zabrání vstřikování SQL při provádění tohoto volání nejvyšší úrovně. Pokud však máte jiné chyby v aplikaci SQL jinde v aplikaci (např. V uživatelsky definovaných funkcích, které jste uložili a spustili v databázi, jste nebezpečně napsali, abyste mohli vytvářet dotazy SQL pomocí zřetězení řetězce).

Pokud jste například ve své aplikaci napsali pseudokód, například:

sql_stmt = "SELECT create_new_user(?, ?)"
params = (email_str, hashed_pw_str)
db_conn.execute_with_params(sql_stmt, params)

Při spuštění tohoto příkazu SQL na úrovni aplikace nemůže dojít k žádné injekci SQL. Pokud však byla uživatelsky definovaná databázová funkce zapsána nebezpečně (pomocí syntaxe PL/pgSQL):

CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
DECLARE
   sql_str TEXT;
BEGIN
     sql_str := 'INSERT INTO users VALUES (' || email_str || ', ' || hashed_pw_str || ');'
     EXECUTE sql_str;
END;
$$
LANGUAGE plpgsql;

pak byste byli zranitelní vůči útokům SQL injekcí, protože provádí příkaz SQL vytvořený zřetězením řetězců, který mísí příkaz SQL s řetězci obsahujícími hodnoty uživatelem definovaných proměnných.

To znamená, že pokud se nepokoušíte být nebezpeční (konstruování příkazů SQL pomocí zřetězení řetězce), bylo by přirozenější psát uživatelem definované bezpečným způsobem, jako například:

CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
BEGIN
     INSERT INTO users VALUES (email_str, hashed_pw_str);
END;
$$
LANGUAGE plpgsql;

Dále, pokud jste skutečně cítili potřebu napsat příkaz SQL z řetězce v uživatelem definované funkci, můžete oddělit datové proměnné od příkazu SQL stejným způsobem jako uložené parametry_procesů/vázané parametry, a to i v rámci uživatelem definované funkce. Například v PL/pgSQL :

CREATE FUNCTION create_new_user(email_str, hashed_pw_str) RETURNS VOID AS
$$
DECLARE
   sql_str TEXT;
BEGIN
     sql_str := 'INSERT INTO users VALUES($1, $2)'
     EXECUTE sql_str USING email_str, hashed_pw_str;
END;
$$
LANGUAGE plpgsql;

Takže použití připravených příkazů je bezpečné před vstřikováním SQL, pokud neděláte jen nebezpečné věci jinde (to je vytváření příkazů SQL řetězcovým zřetězením).

54
dr jimbob

100% bezpečný? Ani blízko. Vázané parametry (připravené příkazové nebo jiné) účinně mohou zabránit, 100%, jedné třídě zranitelnosti SQL injekcí (za předpokladu, že žádné chyby db a rozumná implementace) ). V žádném případě nezabrání jiným třídám. Všimněte si, že PostgreSQL (můj db volby) má možnost vázat parametry k příkazům ad hoc, což ušetří zpáteční cestu ohledně připravených příkazů, pokud tyto vlastnosti nepotřebujete.

Musíte si uvědomit, že mnoho velkých, složitých databází jsou programy samy o sobě. Složitost těchto programů se docela liší a SQL injekce je něco, co je třeba dávat pozor na vnitřní programovací rutiny. Takové rutiny zahrnují spouštěče, uživatelem definované funkce, uložené procedury a podobně. Není vždy zřejmé, jak tyto věci interagují z aplikační úrovně, protože mnoho dobrých dba poskytuje určitý stupeň abstrakce mezi přístupovou úrovní aplikace a úrovní úložiště.

Při vázaných parametrech je strom dotazů analyzován, a pak alespoň v PostgreSQL jsou data prohlížena za účelem plánování. Plán je proveden. S připravenými příkazy je plán uložen, takže můžete znovu provést stejný plán s různými daty znovu a znovu (to může nebo nemusí být to, co chcete). Jde ale o to, že s vázanými parametry nemůže parametr vložit nic do stromu analýzy. Tato třída problému s SQL injekcí je tedy řádně postarána.

Nyní však musíme protokolovat, kdo píše, co do tabulky, takže přidáváme triggery a uživatelem definované funkce, které zapouzdřují logiku těchto triggerů. To představuje nové problémy. Pokud v nich máte nějaký dynamický SQL, musíte se obávat injekce SQL. Tabulky, do kterých píšou, mohou mít vlastní spouště, atd. Podobně by volání funkce mohlo vyvolat další dotaz, který by mohl vyvolat další volání funkce atd. Každá z nich je plánována nezávisle na hlavním stromu.

Co to znamená je, že když spustím dotaz s vázaným parametrem, jako je foo'; drop user postgres; --, Nemůže přímo implikovat strom dotazů nejvyšší úrovně a způsobit, že přidá další příkaz, aby upustil uživatele postgresu. Pokud však tento dotaz zavolá jinou funkci přímo nebo ne, je možné, že někde po lince bude funkce zranitelná a uživatel postgresu bude vyřazen. Vázané parametry nabízely žádnou ochranu sekundárních dotazů. Tyto sekundární dotazy musí zajistit, aby v co největší míře používaly také vázané parametry, a pokud ne, budou muset použít vhodné rutiny pro citování.

Králičí díra jde hluboko.

BTW pro dotaz na Stack Overflow, kde je tento problém zjevný, viz https://stackoverflow.com/questions/37878426/conditional-where-expression-in-dynamic-query/37878574#37878574

Také problematičtější verze (kvůli omezení příkazů obslužného programu) na https://stackoverflow.com/questions/38016764/perform-create-index-in-plpgsql-doesnt-run/38021245#38021245

25
Chris Travers