Každých několik let hodnotí projekt Open Web Application Security Project (OWASP) nejkritičtější bezpečnostní rizika webových aplikací. Od první zprávy byla rizika vstřikování vždy na vrcholu. Mezi všemi typy vkládání je vkládání SQL je jedním z nejběžnějších vektorů útoku a pravděpodobně nejnebezpečnější. Jelikož je Python jedním z nejpopulárnějších programovacích jazyků na světě, je důležité vědět, jak se chránit před Python SQL injection.
V tomto tutoriálu se naučíte:
- Co Python SQL injection je a jak tomu zabránit
- Jak vytvářet dotazy s literály i identifikátory jako parametry
- Jak bezpečně spouštět dotazy v databázi
Tento výukový program je vhodný pro uživatele všech databázových strojů . Zde uvedené příklady používají PostgreSQL, ale výsledky lze reprodukovat v jiných systémech správy databází (jako je SQLite, MySQL, Microsoft SQL Server, Oracle atd.).
Box zdarma: 5 Thoughts On Python Mastery, bezplatný kurz pro vývojáře Pythonu, který vám ukáže plán a způsob myšlení, které budete potřebovat, abyste své dovednosti v Pythonu posunuli na další úroveň.
Pochopení Python SQL Injection
Útoky SQL Injection jsou tak běžnou bezpečnostní chybou, jako je legendární xkcd webcomic tomu věnoval komiks:
Generování a provádění SQL dotazů je běžným úkolem. Společnosti po celém světě však často dělají hrozné chyby, pokud jde o skládání SQL příkazů. Zatímco vrstva ORM obvykle skládá SQL dotazy, někdy musíte napsat své vlastní.
Když používáte Python ke spouštění těchto dotazů přímo do databáze, existuje šance, že uděláte chyby, které by mohly ohrozit váš systém. V tomto tutoriálu se naučíte, jak úspěšně implementovat funkce, které skládají dynamické SQL dotazy bez vystavuje váš systém riziku pro Python SQL injection.
Nastavení databáze
Chcete-li začít, nastavíte novou databázi PostgreSQL a naplníte ji daty. V průběhu kurzu budete tuto databázi používat k tomu, abyste byli z první ruky svědky toho, jak funguje Python SQL injection.
Vytvoření databáze
Nejprve otevřete svůj shell a vytvořte novou PostgreSQL databázi vlastněnou uživatelem postgres
:
$ createdb -O postgres psycopgtest
Zde jste použili volbu příkazového řádku -O
pro nastavení vlastníka databáze na uživatele postgres
. Zadali jste také název databáze, což je psycopgtest
.
Poznámka: postgres
je zvláštní uživatel , které byste normálně vyhradili pro administrativní úkoly, ale pro tento tutoriál je dobré použít postgres
. Ve skutečném systému byste však měli vytvořit samostatného uživatele, který bude vlastníkem databáze.
Vaše nová databáze je připravena k použití! Můžete se k němu připojit pomocí psql
:
$ psql -U postgres -d psycopgtest
psql (11.2, server 10.5)
Type "help" for help.
Nyní jste připojeni k databázi psycopgtest
jako uživatel postgres
. Tento uživatel je také vlastníkem databáze, takže budete mít oprávnění ke čtení u každé tabulky v databázi.
Vytvoření tabulky s daty
Dále musíte vytvořit tabulku s některými informacemi o uživateli a přidat do ní data:
psycopgtest=# CREATE TABLE users (
username varchar(30),
admin boolean
);
CREATE TABLE
psycopgtest=# INSERT INTO users
(username, admin)
VALUES
('ran', true),
('haki', false);
INSERT 0 2
psycopgtest=# SELECT * FROM users;
username | admin
----------+-------
ran | t
haki | f
(2 rows)
Tabulka má dva sloupce:username
a admin
. admin
udává, zda má uživatel oprávnění správce. Vaším cílem je zacílit na admin
pole a pokusit se jej zneužít.
Nastavení virtuálního prostředí Python
Nyní, když máte databázi, je čas nastavit prostředí Pythonu. Podrobné pokyny, jak to udělat, najdete v Python Virtual Environments:A Primer.
Vytvořte své virtuální prostředí v novém adresáři:
(~/src) $ mkdir psycopgtest
(~/src) $ cd psycopgtest
(~/src/psycopgtest) $ python3 -m venv venv
Po spuštění tohoto příkazu se objeví nový adresář s názvem venv
bude vytvořen. Tento adresář bude ukládat všechny balíčky, které nainstalujete ve virtuálním prostředí.
Připojování k databázi
Chcete-li se připojit k databázi v Pythonu, potřebujete adaptér databáze . Většina databázových adaptérů se řídí verzí 2.0 specifikace Python Database API Specification PEP 249. Každý hlavní databázový stroj má přední adaptér:
Databáze | Adaptér |
---|---|
PostgreSQL | Psycopg |
SQLite | sqlite3 |
Oracle | cx_oracle |
MySql | MySQLdb |
Chcete-li se připojit k databázi PostgreSQL, budete muset nainstalovat Psycopg, což je nejoblíbenější adaptér pro PostgreSQL v Pythonu. Django ORM jej používá ve výchozím nastavení a je také podporován SQLAlchemy.
Ve svém terminálu aktivujte virtuální prostředí a použijte pip
k instalaci psycopg
:
(~/src/psycopgtest) $ source venv/bin/activate
(~/src/psycopgtest) $ python -m pip install psycopg2>=2.8.0
Collecting psycopg2
Using cached https://....
psycopg2-2.8.2.tar.gz
Installing collected packages: psycopg2
Running setup.py install for psycopg2 ... done
Successfully installed psycopg2-2.8.2
Nyní jste připraveni vytvořit připojení k vaší databázi. Zde je začátek vašeho skriptu Python:
import psycopg2
connection = psycopg2.connect(
host="localhost",
database="psycopgtest",
user="postgres",
password=None,
)
connection.set_session(autocommit=True)
Použili jste psycopg2.connect()
k vytvoření spojení. Tato funkce přijímá následující argumenty:
-
host
je IP adresa nebo DNS serveru, kde se nachází vaše databáze. V tomto případě je hostitelem váš místní počítač nebolocalhost
. -
database
je název databáze, ke které se chcete připojit. Chcete se připojit k databázi, kterou jste vytvořili dříve,psycopgtest
. -
user
je uživatel s oprávněním k databázi. V tomto případě se chcete připojit k databázi jako vlastník, takže předáte uživatelipostgres
. -
password
je heslo pro kohokoli, koho jste zadali vuser
. Ve většině vývojových prostředí se uživatelé mohou připojit k místní databázi bez hesla.
Po nastavení připojení jste relaci nakonfigurovali pomocí autocommit=True
. Aktivace autocommit
znamená, že nebudete muset ručně spravovat transakce vydáváním commit
nebo rollback
. Toto je výchozí chování ve většině ORM. Toto chování využijete i zde, abyste se místo správy transakcí mohli soustředit na skládání SQL dotazů.
Poznámka: Uživatelé Django mohou získat instanci připojení používaného ORM z django.db.connection
:
from django.db import connection
Provedení dotazu
Nyní, když máte připojení k databázi, jste připraveni provést dotaz:
>>>>>> with connection.cursor() as cursor:
... cursor.execute('SELECT COUNT(*) FROM users')
... result = cursor.fetchone()
... print(result)
(2,)
Použili jste connection
objekt k vytvoření cursor
. Stejně jako soubor v Pythonu, cursor
je implementován jako kontextový manažer. Když vytvoříte kontext, cursor
se otevře, abyste mohli odesílat příkazy do databáze. Když kontext opustíte, cursor
se zavře a už jej nemůžete používat.
Poznámka: Chcete-li se dozvědět více o kontextových manažerech, podívejte se na Python Context Managers a na příkaz „with“.
V kontextu jste použili cursor
k provedení dotazu a načtení výsledků. V tomto případě jste zadali dotaz na počítání řádků v users
stůl. Chcete-li načíst výsledek z dotazu, spustili jste cursor.fetchone()
a obdržel n-tici. Protože dotaz může vrátit pouze jeden výsledek, použili jste fetchone()
. Pokud by dotaz měl vrátit více než jeden výsledek, pak byste museli buď iterovat přes cursor
nebo použijte jeden z dalších fetch*
metody.
Použití parametrů dotazu v SQL
V předchozí části jste vytvořili databázi, navázali připojení k ní a provedli dotaz. Použitý dotaz byl statický . Jinými slovy, neměl žádné parametry . Nyní začnete ve svých dotazech používat parametry.
Nejprve implementujete funkci, která kontroluje, zda je uživatel správcem. is_admin()
přijme uživatelské jméno a vrátí tomuto uživateli status správce:
# BAD EXAMPLE. DON'T DO THIS!
def is_admin(username: str) -> bool:
with connection.cursor() as cursor:
cursor.execute("""
SELECT
admin
FROM
users
WHERE
username = '%s'
""" % username)
result = cursor.fetchone()
admin, = result
return admin
Tato funkce provede dotaz k načtení hodnoty admin
sloupec pro dané uživatelské jméno. Použili jste fetchone()
vrátit n-tici s jediným výsledkem. Poté jste tuto n-tici rozbalili do proměnné admin
. Chcete-li otestovat svou funkci, zkontrolujte některá uživatelská jména:
>>> is_admin('haki')
False
>>> is_admin('ran')
True
Zatím je vše dobré. Funkce vrátila očekávaný výsledek pro oba uživatele. Ale co neexistující uživatel? Podívejte se na tento traceback Pythonu:
>>>>>> is_admin('foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 12, in is_admin
TypeError: cannot unpack non-iterable NoneType object
Když uživatel neexistuje, zobrazí se TypeError
je zvednutý. Důvodem je .fetchone()
vrátí None
když nejsou nalezeny žádné výsledky, a rozbalení None
vyvolá TypeError
. Jediné místo, kde můžete rozbalit n-tici, je místo, kde zadáte admin
z result
.
Chcete-li pracovat s neexistujícími uživateli, vytvořte speciální případ pro result
je None
:
# BAD EXAMPLE. DON'T DO THIS!
def is_admin(username: str) -> bool:
with connection.cursor() as cursor:
cursor.execute("""
SELECT
admin
FROM
users
WHERE
username = '%s'
""" % username)
result = cursor.fetchone()
if result is None:
# User does not exist
return False
admin, = result
return admin
Zde jste přidali speciální případ pro zpracování None
. Pokud username
neexistuje, pak by funkce měla vrátit False
. Ještě jednou otestujte funkci na některých uživatelích:
>>> is_admin('haki')
False
>>> is_admin('ran')
True
>>> is_admin('foo')
False
Skvělý! Funkce nyní zvládne i neexistující uživatelská jména.
Využití parametrů dotazu pomocí Python SQL Injection
V předchozím příkladu jste ke generování dotazu použili řetězcovou interpolaci. Poté jste provedli dotaz a odeslali výsledný řetězec přímo do databáze. Je tu však něco, co jste během tohoto procesu mohli přehlédnout.
Vzpomeňte si na username
argument, který jste předali is_admin()
. Co přesně tato proměnná představuje? Můžete předpokládat, že username
je pouze řetězec, který představuje skutečné uživatelské jméno. Jak však uvidíte, vetřelec může tento druh dohledu snadno zneužít a způsobit velké škody provedením Python SQL injection.
Zkuste zkontrolovat, zda je následující uživatel správcem nebo ne:
>>>>>> is_admin("'; select true; --")
True
Počkejte... Co se právě stalo?
Podívejme se ještě jednou na implementaci. Vytiskněte skutečný dotaz, který se provádí v databázi:
>>>>>> print("select admin from users where username = '%s'" % "'; select true; --")
select admin from users where username = ''; select true; --'
Výsledný text obsahuje tři výroky. Abyste přesně porozuměli tomu, jak Python SQL injection funguje, musíte zkontrolovat každou část jednotlivě. První příkaz je následující:
select admin from users where username = '';
Toto je váš zamýšlený dotaz. Středník (;
) ukončí dotaz, takže na výsledku tohoto dotazu nezáleží. Další na řadě je druhý příkaz:
select true;
Toto prohlášení vytvořil vetřelec. Je navržen tak, aby vždy vrátil True
.
Nakonec vidíte tento krátký kód:
--'
Tento úryvek zneškodňuje vše, co přijde po něm. Vetřelec přidal symbol komentáře (--
), abyste změnili vše, co jste mohli vložit za poslední zástupný symbol, na komentář.
Když spustíte funkci s tímto argumentem, vždy vrátí True
. Pokud například použijete tuto funkci na své přihlašovací stránce, mohl by se narušitel přihlásit pomocí uživatelského jména '; select true; --
a bude jim udělen přístup.
Pokud si myslíte, že je to špatné, může to být ještě horší! Vetřelci se znalostí struktury vaší tabulky mohou pomocí Python SQL injection způsobit trvalé poškození. Vetřelec může například vložit aktualizační příkaz ke změně informací v databázi:
>>>>>> is_admin('haki')
False
>>> is_admin("'; update users set admin = 'true' where username = 'haki'; select true; --")
True
>>> is_admin('haki')
True
Pojďme si to znovu rozebrat:
';
Tento fragment ukončí dotaz, stejně jako v předchozí injekci. Další příkaz je následující:
update users set admin = 'true' where username = 'haki';
Tato sekce aktualizuje admin
true
pro uživatele haki
.
Nakonec je tu tento fragment kódu:
select true; --
Stejně jako v předchozím příkladu tato část vrací true
a okomentuje vše, co za ním následuje.
proč je to horší? Pokud se útočníkovi podaří provést funkci s tímto vstupem, pak uživatel haki
stane se správcem:
psycopgtest=# select * from users;
username | admin
----------+-------
ran | t
haki | t
(2 rows)
Vetřelec již nemusí používat hack. Mohou se pouze přihlásit pomocí uživatelského jména haki
. (Pokud vetřelec opravdu chtěli způsobit škodu, pak mohli dokonce vydat DROP DATABASE
příkaz.)
Než zapomenete, obnovte haki
zpět do původního stavu:
psycopgtest=# update users set admin = false where username = 'haki';
UPDATE 1
Proč se to děje? Co víte o username
argument? Víte, že by to měl být řetězec představující uživatelské jméno, ale ve skutečnosti toto tvrzení nekontrolujete ani nevynucujete. To může být nebezpečné! To je přesně to, co útočníci hledají, když se snaží hacknout váš systém.
Vytváření bezpečných parametrů dotazu
V předchozí části jste viděli, jak může vetřelec zneužít váš systém a získat oprávnění správce pomocí pečlivě vytvořeného řetězce. Problém byl v tom, že jste dovolili, aby se hodnota předaná z klienta spustila přímo do databáze, aniž byste museli provádět jakoukoli kontrolu nebo ověřování. Injekce SQL spoléhají na tento typ zranitelnosti.
Kdykoli je v databázovém dotazu použit vstup uživatele, existuje možná zranitelnost pro SQL injection. Klíčem k zabránění vkládání jazyka Python SQL je zajistit, aby se hodnota používala tak, jak vývojář zamýšlel. V předchozím příkladu jste zamýšleli pro username
k použití jako řetězec. Ve skutečnosti byl použit jako nezpracovaný příkaz SQL.
Chcete-li se ujistit, že se hodnoty používají tak, jak jsou zamýšleny, musíte escape hodnota. Chcete-li například zabránit vetřelcům vložit nezpracované SQL místo argumentu řetězce, můžete uvozovky obejít:
>>>>>> # BAD EXAMPLE. DON'T DO THIS!
>>> username = username.replace("'", "''")
Toto je jen jeden příklad. Existuje mnoho speciálních postav a scénářů, na které je třeba myslet, když se pokoušíte zabránit vkládání Pythonu SQL. Naštěstí pro vás, moderní databázové adaptéry, přicházejí s vestavěnými nástroji pro zabránění vkládání jazyka Python SQL pomocí parametrů dotazu . Ty se používají místo interpolace prostého řetězce k sestavení dotazu s parametry.
Poznámka: Různé adaptéry, databáze a programovací jazyky odkazují na parametry dotazu pod různými názvy. Mezi běžné názvy patří bind variables , náhradní proměnné a substituční proměnné .
Nyní, když lépe rozumíte zranitelnosti, jste připraveni přepsat funkci pomocí parametrů dotazu namísto interpolace řetězců:
1def is_admin(username: str) -> bool:
2 with connection.cursor() as cursor:
3 cursor.execute("""
4 SELECT
5 admin
6 FROM
7 users
8 WHERE
9 username = %(username)s
10 """, {
11 'username': username
12 })
13 result = cursor.fetchone()
14
15 if result is None:
16 # User does not exist
17 return False
18
19 admin, = result
20 return admin
Zde je to, co se v tomto příkladu liší:
-
V řádku 9 použili jste pojmenovaný parametr
username
k označení, kam má uživatelské jméno směřovat. Všimněte si, jak parametrusername
již není ohraničeno jednoduchými uvozovkami. -
V řádku 11 předali jste hodnotu
username
jako druhý argument procursor.execute()
. Připojení bude používat typ a hodnotuusername
při provádění dotazu v databázi.
Chcete-li tuto funkci otestovat, vyzkoušejte některé platné a neplatné hodnoty, včetně nebezpečného řetězce z dříve:
>>>>>> is_admin('haki')
False
>>> is_admin('ran')
True
>>> is_admin('foo')
False
>>> is_admin("'; select true; --")
False
Úžasný! Funkce vrátila očekávaný výsledek pro všechny hodnoty. A co víc, nebezpečný řetězec již nefunguje. Abyste pochopili proč, můžete si prohlédnout dotaz vygenerovaný funkcí execute()
:
>>> with connection.cursor() as cursor:
... cursor.execute("""
... SELECT
... admin
... FROM
... users
... WHERE
... username = %(username)s
... """, {
... 'username': "'; select true; --"
... })
... print(cursor.query.decode('utf-8'))
SELECT
admin
FROM
users
WHERE
username = '''; select true; --'
Připojení zpracovalo hodnotu username
jako řetězec a escapoval všechny znaky, které by mohly ukončit řetězec a zavést Python SQL injection.
Předávání parametrů bezpečného dotazu
Databázové adaptéry obvykle nabízejí několik způsobů, jak předat parametry dotazu. Pojmenované zástupné symboly jsou obvykle nejlepší pro čitelnost, ale některým implementacím může prospět použití jiných možností.
Pojďme se rychle podívat na některé správné a nesprávné způsoby použití parametrů dotazu. Následující blok kódu ukazuje typy dotazů, kterým se chcete vyhnout:
# BAD EXAMPLES. DON'T DO THIS!
cursor.execute("SELECT admin FROM users WHERE username = '" + username + '");
cursor.execute("SELECT admin FROM users WHERE username = '%s' % username);
cursor.execute("SELECT admin FROM users WHERE username = '{}'".format(username));
cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'");
Každý z těchto příkazů předává username
z klienta přímo do databáze, bez provádění jakékoli kontroly nebo ověřování. Tento druh kódu je zralý pro pozvání Python SQL injection.
Naproti tomu tyto typy dotazů by pro vás měly být bezpečné:
# SAFE EXAMPLES. DO THIS!
cursor.execute("SELECT admin FROM users WHERE username = %s'", (username, ));
cursor.execute("SELECT admin FROM users WHERE username = %(username)s", {'username': username});
V těchto příkazech username
je předán jako pojmenovaný parametr. Nyní bude databáze používat zadaný typ a hodnotu username
při provádění dotazu nabízí ochranu před Python SQL injection.
Použití SQL Composition
Dosud jste používali parametry pro literály. Literály jsou hodnoty, jako jsou čísla, řetězce a data. Ale co když máte případ použití, který vyžaduje sestavení jiného dotazu – takového, kde je parametrem něco jiného, například název tabulky nebo sloupce?
Na základě předchozího příkladu implementujme funkci, která přijímá název tabulky a vrací počet řádků v této tabulce:
# BAD EXAMPLE. DON'T DO THIS!
def count_rows(table_name: str) -> int:
with connection.cursor() as cursor:
cursor.execute("""
SELECT
count(*)
FROM
%(table_name)s
""", {
'table_name': table_name,
})
result = cursor.fetchone()
rowcount, = result
return rowcount
Zkuste spustit funkci na vaší tabulce uživatelů:
>>>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in count_rows
psycopg2.errors.SyntaxError: syntax error at or near "'users'"
LINE 5: 'users'
^
Příkazu se nepodařilo vygenerovat SQL. Jak jste již viděli, databázový adaptér zachází s proměnnou jako s řetězcem nebo literálem. Název tabulky však není prostý řetězec. Zde přichází na řadu složení SQL.
Už víte, že není bezpečné používat k sestavení SQL interpolaci řetězců. Naštěstí Psycopg poskytuje modul nazvaný psycopg.sql
které vám pomohou bezpečně skládat SQL dotazy. Přepišme funkci pomocí psycopg.sql.SQL()
:
from psycopg2 import sql
def count_rows(table_name: str) -> int:
with connection.cursor() as cursor:
stmt = sql.SQL("""
SELECT
count(*)
FROM
{table_name}
""").format(
table_name = sql.Identifier(table_name),
)
cursor.execute(stmt)
result = cursor.fetchone()
rowcount, = result
return rowcount
V této implementaci jsou dva rozdíly. Nejprve jste použili sql.SQL()
sestavit dotaz. Potom jste použili sql.Identifier()
k anotaci hodnoty argumentu table_name
. (identifikátor je název sloupce nebo tabulky.)
Poznámka: Uživatelé oblíbeného balíčku django-debug-toolbar
může dostat chybu v panelu SQL pro dotazy složené pomocí psycopg.sql.SQL()
. Oprava se očekává pro vydání ve verzi 2.0.
Nyní zkuste funkci spustit na users
tabulka:
>>> count_rows('users')
2
Skvělý! Dále se podívejme, co se stane, když tabulka neexistuje:
>>>>>> count_rows('foo')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in count_rows
psycopg2.errors.UndefinedTable: relation "foo" does not exist
LINE 5: "foo"
^
Funkce vyvolá UndefinedTable
výjimka. V následujících krocích použijete tuto výjimku jako označení, že vaše funkce je v bezpečí před útokem Python SQL injection.
Poznámka: Výjimka UndefinedTable
byl přidán ve verzi psycopg2 2.8. Pokud pracujete se starší verzí Psycopg, dostanete jinou výjimku.
Chcete-li to dát dohromady, přidejte možnost počítat řádky v tabulce do určitého limitu. Tato funkce může být užitečná pro velmi velké tabulky. Chcete-li to implementovat, přidejte LIMIT
klauzule k dotazu spolu s parametry dotazu pro hodnotu limitu:
from psycopg2 import sql
def count_rows(table_name: str, limit: int) -> int:
with connection.cursor() as cursor:
stmt = sql.SQL("""
SELECT
COUNT(*)
FROM (
SELECT
1
FROM
{table_name}
LIMIT
{limit}
) AS limit_query
""").format(
table_name = sql.Identifier(table_name),
limit = sql.Literal(limit),
)
cursor.execute(stmt)
result = cursor.fetchone()
rowcount, = result
return rowcount
V tomto bloku kódu jste označili limit
pomocí sql.Literal()
. Stejně jako v předchozím příkladu psycopg
při použití jednoduchého přístupu sváže všechny parametry dotazu jako literály. Při použití sql.SQL()
, musíte každý parametr explicitně anotovat pomocí sql.Identifier()
nebo sql.Literal()
.
Poznámka: Specifikace Python API bohužel neřeší vazbu identifikátorů, pouze literály. Psycopg je jediný populární adaptér, který přidal možnost bezpečně skládat SQL s literály i identifikátory. Díky této skutečnosti je ještě důležitější věnovat zvýšenou pozornost při vázání identifikátorů.
Spusťte funkci, abyste se ujistili, že funguje:
>>>>>> count_rows('users', 1)
1
>>> count_rows('users', 10)
2
Nyní, když vidíte, že funkce funguje, ujistěte se, že je také bezpečná:
>>>>>> count_rows("(select 1) as foo; update users set admin = true where name = 'haki'; --", 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 18, in count_rows
psycopg2.errors.UndefinedTable: relation "(select 1) as foo; update users set admin = true where name = '" does not exist
LINE 8: "(select 1) as foo; update users set adm...
^
Tento traceback ukazuje, že psycopg
unikla hodnotě a databáze ji považovala za název tabulky. Protože tabulka s tímto názvem neexistuje, UndefinedTable
byla vznesena výjimka a nebyli jste napadeni!
Závěr
Úspěšně jste implementovali funkci, která skládá dynamické SQL bez vystavuje váš systém riziku pro Python SQL injection! Ve svém dotazu jste použili literály i identifikátory, aniž byste ohrozili zabezpečení.
Dozvěděli jste se:
- Co Python SQL injection je a jak jej lze zneužít
- Jak zabránit vkládání jazyka Python SQL pomocí parametrů dotazu
- Jak bezpečně vytvářet příkazy SQL které jako parametry používají literály a identifikátory
Nyní můžete vytvářet programy, které odolají útokům zvenčí. Jděte do toho a překazte hackery!