HostiServer
2025-12-17 09:00:00
Ochrana před SQL Injection v roce 2026: kompletní průvodce od Hostiserver
⏱️ Doba čtení: ~10 minut | 📅 Aktualizováno: 17. prosince 2025
SQL Injection v roce 2026: proč je to stále problém
SQL Injection zůstává v OWASP Top 10 již více než 20 let. V listopadu 2025 OWASP zveřejnil aktualizovaný žebříček, kde Injection zaujímá 5. místo (A05:2025). FBI a CISA oficiálně označily SQLi jako "unforgivable defect" — zranitelnost, která by v moderním softwaru neměla existovat.
Ale existuje. V Hostiserveru to vidíme pravidelně: klienti přicházejí po napadení, s poškozenými databázemi, s úniky uživatelských dat. Ve většině případů je příčinou chybějící prepared statements nebo zastaralý kód bez validace.
Tento průvodce není teoretický přehled. Sestavili jsme konfigurace a přístupy, které skutečně používáme na managed serverech Hostiserver: od nastavení ModSecurity po MySQL hardening. Vše ověřeno v praxi.
⚠️ Důležité: Pokud váš web přijímá jakýkoli uživatelský vstup (formuláře, vyhledávání, filtry, URL parametry) — je potenciálně zranitelný. I "jednoduchý blog" na WordPressu se může stát obětí kvůli zranitelnému pluginu.
Jak funguje SQL Injection
SQL Injection je technika útoku, při které útočník vkládá škodlivý SQL kód do vstupních polí. Pokud aplikace nevaliduje vstup, tento kód se spustí na databázovém serveru.
Příklad zranitelného kódu
// ❌ NEBEZPEČNÉ — nikdy to nedělejte!
$username = $_POST['username'];
$query = "SELECT * FROM users WHERE username = '$username'";
$result = mysqli_query($conn, $query);
Pokud útočník zadá do pole username:
' OR '1'='1' --
Dotaz se změní na:
SELECT * FROM users WHERE username = '' OR '1'='1' --'
Výsledek: útočník získá přístup ke všem záznamům v tabulce.
Typy útoků
| Typ | Mechanismus | Obtížnost detekce |
|---|---|---|
| Classic (In-band) | Výsledek viditelný na stránce | Nízká |
| Union-based | UNION vytahuje data z jiných tabulek | Nízká |
| Error-based | Data přes chybové zprávy | Střední |
| Blind SQLi | Žádný viditelný výsledek, "hádání" | Vysoká |
| Time-based Blind | SLEEP() určuje pravdivost podmínek | Vysoká |
| Out-of-band | Data jdou na externí server | Velmi vysoká |
Aktuální CVE: SQL Injection v letech 2024-2025
SQLi není archaický problém. Kritické zranitelnosti se nacházejí i v moderním enterprise softwaru. Zde je několik příkladů, které sledujeme:
CVE-2025-25257: Fortinet FortiWeb
Ironický případ: SQL Injection v samotném WAF. CVSS 9.6 Critical. Zranitelnost umožňovala neautentizovanému útočníkovi spouštět SQL příkazy přes HTTP požadavky. (Zdroj informací).
CVE-2025-1094: PostgreSQL
Kritická zranitelnost v escapovacích funkcích PostgreSQL. CVSS 8.1 High. Obejití prepared statements přes nesprávné zpracování multibyte znaků. Postihla všechny verze do 17.3. (Zdroj informací).
CVE-2024-42327: Zabbix
CVSS 9.9 Critical. Jakýkoli uživatel s API přístupem mohl využít SQLi a eskalovat oprávnění. Zabbix používají tisíce společností pro monitoring infrastruktury. (Zdroj informací).
MOVEit Transfer (CVE-2023-34362)
Tento případ je stále zmiňován jako ukázka rozsahu problému. SQLi vedla ke kompromitaci více než 2 500 organizací. (Zdroj informací).
Co může útočník udělat
- Ukrást data — hesla, emaily, platební údaje
- Změnit data — zfalšovat transakce, změnit ceny
- Získat shell přístup — přes INTO OUTFILE
- Zničit databázi — DROP TABLE, TRUNCATE
- Eskalovat oprávnění — získat admin přístup
Prepared Statements: jediný správný přístup
Prepared Statements (parametrizované dotazy) jsou nejúčinnější ochranou proti SQL Injection. Oddělují SQL kód od dat. Doporučujeme to jako základní standard pro všechny projekty.
PHP PDO (doporučujeme)
// ✅ BEZPEČNÉ — PDO s prepared statements
$pdo = new PDO('mysql:host=localhost;dbname=app_db;charset=utf8mb4', $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // Důležité!
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username AND status = :status');
$stmt->execute([
':username' => $username,
':status' => 'active'
]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
PHP MySQLi
// ✅ BEZPEČNÉ — MySQLi s prepared statements
$mysqli = new mysqli('localhost', $user, $pass, 'app_db');
$mysqli->set_charset('utf8mb4');
$stmt = $mysqli->prepare('SELECT * FROM users WHERE username = ? AND status = ?');
$stmt->bind_param('ss', $username, $status);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
Proč to funguje
Při použití prepared statements:
- SQL dotaz se kompiluje odděleně od dat
- Parametry se předávají jako hodnoty, ne jako kód
- Speciální znaky se automaticky escapují
- I
' OR '1'='1se stane pouhým řetězcem
✅ Naše doporučení: VŽDY používejte prepared statements pro SQL dotazy s uživatelským vstupem. Bez výjimek.
Důležité: CVE-2025-1094
V únoru 2025 bylo zjištěno, že i prepared statements mohou být obejity při nesprávném zpracování multibyte znaků v PostgreSQL. Řešení je jednoduché: udržujte software aktuální (PostgreSQL 17.3+, 16.7+, 15.11+).
ORM a frameworky: vestavěná ochrana
Moderní frameworky mají ochranu proti SQL Injection "z krabice". Na serverech Hostiserver podporujeme všechny populární frameworky — Laravel, Django, Node.js stack.
Laravel Eloquent (PHP)
// ✅ BEZPEČNÉ — Eloquent automaticky parametrizuje
$users = User::where('username', $username)
->where('status', 'active')
->get();
// ✅ BEZPEČNÉ — Query Builder
$users = DB::table('users')
->where('username', $username)
->get();
// ❌ NEBEZPEČNÉ — raw dotazy bez bindings
// DB::select("SELECT * FROM users WHERE username = '$username'");
Django ORM (Python)
# ✅ BEZPEČNÉ — Django ORM
users = User.objects.filter(username=username, status='active')
# ✅ BEZPEČNÉ — raw dotaz s parametry
users = User.objects.raw('SELECT * FROM users WHERE username = %s', [username])
# ❌ NEBEZPEČNÉ — string formatting
# User.objects.raw(f"SELECT * FROM users WHERE username = '{username}'")
Node.js (Sequelize / Prisma)
// ✅ BEZPEČNÉ — Sequelize
const users = await User.findAll({
where: { username: username, status: 'active' }
});
// ✅ BEZPEČNÉ — Prisma
const users = await prisma.user.findMany({
where: { username: username, status: 'active' }
});
⚠️ Pozor: ORM chrání pouze při správném použití. Raw SQL uvnitř ORM může být stále zranitelné. Často to vidíme při auditu klientských projektů.
Validace vstupu: dodatečná vrstva
Validace je dodatečná ochrana, ne náhrada za prepared statements. Doporučujeme aplikovat oba přístupy současně.
Typy validace
| Typ | Popis | Příklad |
|---|---|---|
| Whitelist | Povolujeme pouze očekávané hodnoty | Řazení: pouze 'asc' nebo 'desc' |
| Type casting | Vynucená konverze typu | $id = (int) $_GET['id']; |
| Validace formátu | Kontrola formátu dat | Email, datum, UUID |
| Omezení délky | Omezení délky | Username: max 50 znaků |
Příklady validace v PHP
// ✅ Whitelist pro řazení (ORDER BY nelze parametrizovat)
$allowed_columns = ['created_at', 'username', 'email'];
$sort_column = in_array($_GET['sort'], $allowed_columns) ? $_GET['sort'] : 'created_at';
$allowed_directions = ['ASC', 'DESC'];
$sort_dir = in_array(strtoupper($_GET['dir']), $allowed_directions) ? strtoupper($_GET['dir']) : 'DESC';
// ✅ Type casting pro ID
$user_id = filter_var($_GET['id'], FILTER_VALIDATE_INT);
if ($user_id === false) {
throw new InvalidArgumentException('Invalid user ID');
}
// ✅ Regex pro specifické formáty
if (!preg_match('/^[a-zA-Z0-9_]{3,20}$/', $username)) {
throw new InvalidArgumentException('Invalid username format');
}
💡 Z naší zkušenosti: Whitelist je vždy lepší než Blacklist. Místo blokování nebezpečných znaků — povolujte pouze očekávané.
WAF: jak konfigurujeme na serverech Hostiserver
WAF analyzuje HTTP požadavky a blokuje podezřelé vzory dříve, než se dostanou k aplikaci. Obzvláště důležité pro legacy kód, který je obtížné přepsat.
Co používáme
| Řešení | Úroveň | Použití |
|---|---|---|
| ModSecurity | Serverový (Apache/Nginx) | Hluboká inspekce požadavků |
| Cloudflare WAF | DNS proxy | Edge protection, DDoS |
ModSecurity pravidla (naše konfigurace)
Zde je příklad pravidel, která konfigurujeme pro klienty:
# Pravidlo 1: Detekce SQLi vzorů
SecRule ARGS|REQUEST_BODY \
"@rx (?i)(union\s+select|sleep\(|benchmark\(|or\s+1=1)" \
"id:1001002,phase:2,pass,log,tag:'attack-sqli',setvar:'tx.inbound_anomaly_score=+5',msg:'SQLi pattern detected'"
# Pravidlo 2: Blokování při překročení anomaly score
SecRule TX:INBOUND_ANOMALY_SCORE "@ge 5" \
"id:1001099,phase:2,deny,status:403,log,msg:'Inbound anomaly score exceeded'"
Co tato pravidla blokují
UNION SELECT— vytahování dat z jiných tabulekSLEEP()— time-based blind SQLiBENCHMARK()— alternativní time-based útokOR 1=1— klasická boolean injection
OWASP Core Rule Set (CRS)
Pro komplexní ochranu instalujeme OWASP CRS — sadu pravidel pokrývající SQLi, XSS, LFI a další útoky:
# Instalace OWASP CRS pro Apache
sudo apt install libapache2-mod-security2
sudo mv /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf
# Stažení CRS
cd /etc/modsecurity
sudo git clone https://github.com/coreruleset/coreruleset.git
sudo cp coreruleset/crs-setup.conf.example coreruleset/crs-setup.conf
✅ Náš přístup: Kombinujeme ModSecurity na serveru s Cloudflare WAF. Cloudflare blokuje většinu útoků na edge, ModSecurity zachytí to, co projde.
MySQL Hardening: naše standardní nastavení
I když útočník najde SQLi, správně nakonfigurovaná databáze minimalizuje škody. Zde je, co děláme na managed serverech Hostiserver.
Síťová izolace
# /etc/mysql/mysql.conf.d/mysqld.cnf
# MySQL naslouchá pouze na localhost — KRITICKÉ!
bind-address = 127.0.0.1
# Port 3306 NENÍ otevřený do veřejného internetu
# Přístup povolen pouze z localhost nebo konkrétních IP přes firewall
Princip minimálních oprávnění
Každá aplikace dostává samostatného uživatele s minimálními právy:
-- ✅ Samostatný uživatel pro aplikaci
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'STRONG_RANDOM_PASSWORD';
-- Pouze potřebná práva na konkrétní databázi
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'localhost';
-- ❌ NEDÁVAT: GRANT ALL ON *.*
-- ❌ NEDÁVAT: SUPER, FILE, PROCESS, SHUTDOWN
FLUSH PRIVILEGES;
Naše standardní politika
| Co děláme | Jak |
|---|---|
| Odstraňujeme anonymní uživatele | DELETE FROM mysql.user WHERE User=''; |
| Root pouze lokálně | Zákaz root@'%' |
| Silná hesla | Password policy, min. 16 znaků |
| Izolace databází | Uživatel vidí pouze svou DB |
| TLS pro remote | REQUIRE SSL |
Limity a timeouty
# /etc/mysql/mysql.conf.d/mysqld.cnf
max_connections = 150
max_user_connections = 50
# Timeouty — uzavření idle spojení
wait_timeout = 300
interactive_timeout = 300
⚠️ Proč je to důležité: Pokud útočník najde SQLi, je omezen právy app_user. Bez FILE — nemůže zapisovat soubory. Bez SUPER — nemůže měnit konfiguraci serveru.
Monitoring: jak detekujeme útoky
Preventivní ochrana je důležitá, ale musíte také vidět, co se děje v reálném čase.
MySQL logování
# /etc/mysql/mysql.conf.d/mysqld.cnf
# Error log — vždy zapnutý
log_error = /var/log/mysql/error.log
# Slow query log — detekce podezřelých dotazů
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
Audit logování
Pro compliance a forensics používáme:
- MariaDB Audit Plugin — zdarma pro MariaDB
- MySQL Enterprise Audit — pro MySQL Enterprise
# MariaDB Audit Plugin
INSTALL SONAME 'server_audit';
SET GLOBAL server_audit_logging = ON;
SET GLOBAL server_audit_events = 'CONNECT,QUERY,TABLE';
Na co nastavujeme alerty
- Abnormálně vysoký počet požadavků z jedné IP
- Dotazy s podezřelými vzory (UNION, SLEEP)
- Chyby autentizace
- Změny struktury databáze
SIEM pro Enterprise
Pro enterprise klienty nabízíme integraci s Elastic Security (ELK SIEM) — centralizovaný sběr logů, korelace událostí, automatická detekce hrozeb.
💡 Tip: Slow query log není jen o výkonu. Abnormálně pomalé dotazy mohou indikovat time-based SQLi (SLEEP, BENCHMARK).
Doporučené verze pro rok 2026
Zastaralé verze PHP a MySQL mají známé zranitelnosti. Zde je, co doporučujeme klientům:
PHP
| Verze | Status | Naše doporučení |
|---|---|---|
| PHP 8.4 | ✅ Active Support | Nejlepší volba |
| PHP 8.3 | ✅ Active Support | Doporučeno |
| PHP 8.2 | ⚠️ Security Only | Minimální verze |
| PHP 8.1 a nižší | ❌ End of Life | Urgentně aktualizujte! |
MySQL / MariaDB
| Verze | Status | Naše doporučení |
|---|---|---|
| MySQL 8.4 LTS | ✅ Long Term Support | Nejlepší volba |
| MySQL 8.0.3x+ | ✅ Active Support | Doporučeno |
| MariaDB 10.11 LTS | ✅ Long Term Support | Doporučeno |
| MySQL 5.7 | ❌ End of Life | Kritické riziko! |
PostgreSQL (po CVE-2025-1094)
Pokud používáte PostgreSQL — aktualizujte na verze 17.3+, 16.7+, 15.11+, 14.16+ nebo 13.19+.
🔴 Kritické: MySQL 5.7 a PHP 7.x již nedostávají bezpečnostní aktualizace. Pokud jste na těchto verzích — kontaktujte nás, pomůžeme s migrací.
Checklist: zkontrolujte svůj projekt
🔐 Kód aplikace
- ☐ Všechny SQL dotazy používají prepared statements
- ☐ PDO:
ATTR_EMULATE_PREPARES = false - ☐ ORM/Query Builder místo raw SQL
- ☐ Whitelist validace pro ORDER BY, názvy tabulek
- ☐ Type casting pro číselné parametry
- ☐ Chybové zprávy nezobrazují SQL dotazy
🛡️ Serverová infrastruktura
- ☐ WAF (ModSecurity / Cloudflare) aktivní
- ☐ MySQL
bind-address = 127.0.0.1 - ☐ Port 3306 uzavřený zvenčí
- ☐ Samostatný databázový uživatel pro každou aplikaci
- ☐ Minimální oprávnění (SELECT, INSERT, UPDATE, DELETE)
- ☐ Zakázáno SUPER, FILE, PROCESS
📊 Monitoring
- ☐ Error log zapnutý
- ☐ Slow query log zapnutý
- ☐ Alerty na podezřelou aktivitu
🔄 Údržba
- ☐ PHP 8.2+ (doporučeno 8.3/8.4)
- ☐ MySQL 8.0+ / MariaDB 10.11+
- ☐ PostgreSQL 17.3+ (pokud používáte)
- ☐ Pravidelné zálohy
🛡️ Potřebujete pomoc se zabezpečením?
Můžeme provést audit vašeho projektu, nakonfigurovat WAF, hardening databáze a monitoring.
Co je součástí managed serverů Hostiserver:
- ModSecurity WAF s pravidly pro SQLi
- Cloudflare integrace
- MySQL hardening podle našich standardů
- Samostatní databázoví uživatelé
- Monitoring a alerty
- Pravidelné zálohy
Pro Enterprise:
- Elastic Security (ELK SIEM)
- MySQL Audit Plugin
- TLS šifrování
- 24/7 DevOps podpora
FAQ: Často kladené dotazy
- Chrání WordPress před SQL Injection?
-
WordPress core používá
$wpdb->prepare()a je dobře chráněný. Ale pluginy a šablony jsou jiný příběh. Z naší zkušenosti většina napadení WordPressu probíhá přes zranitelné pluginy.Doporučujeme: Používejte ověřené pluginy, pravidelně aktualizujte, nainstalujte WAF.
- Chrání prepared statements na 100%?
-
Prepared statements chrání před injekcí hodnot. Ale některé elementy nelze parametrizovat: názvy tabulek, ORDER BY, LIMIT. Pro ty — whitelist validace.
Také zvažte CVE-2025-1094: PostgreSQL měl zranitelnost v samotných escapovacích funkcích. Udržujte software aktuální.
- Stačí pouze WAF?
-
Ne. WAF je důležitá vrstva, ale ne všelék. CVE-2025-25257 ukázal, že i Fortinet FortiWeb (samotný WAF!) měl kritickou SQLi zranitelnost.
Správný přístup: prepared statements + validace + WAF + database hardening.
- PDO nebo MySQLi?
-
Obě jsou bezpečné při správném použití:
- PDO — podporuje 12+ databází, pojmenované placeholdery, doporučujeme pro nové projekty
- MySQLi — pouze MySQL/MariaDB, o něco rychlejší
Důležité pro PDO:
ATTR_EMULATE_PREPARES = false. - Jak otestovat web na SQL Injection?
-
Nástroje:
- SQLMap — automatizovaný tester
- Burp Suite — intercepting proxy
- OWASP ZAP — bezplatný skener
Důležité: Testujte pouze vlastní weby. Testování cizích bez povolení je trestný čin.
- Co dělat, když už došlo k napadení?
-
- Izolujte server od sítě
- Vytvořte zálohu pro forensics
- Zkontrolujte logy na vstupní bod
- Obnovte z čisté zálohy
- Opravte zranitelnost
- Změňte všechna hesla
- Informujte uživatele o úniku
Pokud potřebujete pomoc s incident response — kontaktujte naši podporu.
- Je potřeba samostatný databázový uživatel pro každý web?
-
Ano. Izolace je kritický element. Pokud je napaden jeden web — ostatní databáze zůstanou chráněny. Toto nastavujeme standardně na všech managed serverech.