# Vestavěné bezpečnostní funkce PHP

> Bezpečnostní funkce PHP v přehledu: hesla, náhoda, escapování, kryptografie, relace a kde leží jejich meze u zranitelností jako PHP2Shell.

Zdroj: https://phpsos.de/cs/php/eingebaute-sicherheitsfunktionen/  
Stav: 25.10.2026  
Poskytovatel: PHP SOS, Nouzová pomoc pro PHP a bezpečnost serverů. Okamžitá pomoc přes WhatsApp: https://wa.me/491736494914?text=Dobr%C3%BD%20den%2C%20PHP%20SOS%2C%20pot%C5%99ebujeme%20pomoc%20s%20na%C5%A1%C3%ADm%20PHP%20serverem.%20Firma%3A%20%E2%80%A6%20Web%3A%20%E2%80%A6

**Stručně:** PHP nabízí pro nejčastější bezpečnostní úkoly hotové, prověřené funkce: `password_hash()` pro hesla, `random_bytes()` a `random_int()` pro tokeny, `htmlspecialchars()` proti cross-site scriptingu, připravené dotazy s PDO proti SQL injection, `hash_equals()` pro bezpečná porovnání a rozšíření Sodium pro kryptografii. Kdo tyto stavební prvky důsledně používá, vyhne se většině zranitelností aplikací. Proti chybám v samotném interpretu PHP, jako u PHP2Shell, nepomáhají; tam pomůže jen aktualizace.

## Proč vestavěné bezpečnostní funkce PHP?

Psát bezpečnostní kód vlastními silami je náchylné k chybám. Vlastní hash hesla, svépomocně sestavený generátor tokenů nebo vlastní filtr pro výstupy HTML často vypadají správně a přesto správné nejsou. Vestavěné funkce jsou zdokumentované, otestované a udržují se s aktualizacemi PHP.

Pro rozhodovatele z toho plyne: nezeptejte se své agentury nebo vývojového týmu, zda byly bezpečnostní funkce napsány ručně, ale zda se používají standardní funkce jazyka. Přehled podává tabulka.

| Úkol | Funkce | Dostupné od | Důležitá poznámka |
|---|---|---|---|
| Ukládání hesel | `password_hash()`, `password_verify()` | PHP 5.5 | sloupec až 255 znaků; bcrypt ořezává na 72 bajtů |
| Náhodné tokeny | `random_bytes()`, `random_int()` | PHP 7.0 | nikdy `rand()` nebo `mt_rand()` pro tajné hodnoty |
| Náhoda objektově | `Random\Randomizer` | PHP 8.2 | stejná bezpečnost, flexibilnější rozhraní |
| Escapování výstupu HTML | `htmlspecialchars()` | odjakživa | výchozí příznaky od 8.1 escapují i jednoduché uvozovky |
| Porovnání v konstantním čase | `hash_equals()` | PHP 5.6 | uživatelský vstup jako druhý argument |
| Moderní kryptografie | Sodium (`sodium_*`) | PHP 7.2 | místo vlastního šifrování |
| Tajemství ve stack trace | `#[\SensitiveParameter]` | PHP 8.2 | hodnota se v trace neobjeví |
| Kontrola vstupů | `filter_var()` | PHP 5.2 | kontroluje formát, nenahrazuje escapování |

## Hesla a tokeny

`password_hash()` vytvoří hash s přidanou solí, `password_verify()` jej ověří. S `PASSWORD_DEFAULT` PHP aktuálně používá bcrypt; od PHP 8.4 je výchozí nákladový faktor podle příručky 12 místo 10. Protože se výchozí algoritmus může změnit, příručka doporučuje sloupec v databázi s 255 znaky. U bcrypt se hesla po 72 bajtech ořezávají, což je třeba mít na paměti u velmi dlouhých hesel-vět.

```php
<?php
// Registrace
$hash = password_hash($passwort, PASSWORD_DEFAULT);

// Přihlášení, včetně automatického vylepšení starých hashů
if (password_verify($eingabe, $hash)) {
    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        $hash = password_hash($eingabe, PASSWORD_DEFAULT); // uložit znovu
    }
}

// Token pro odkaz na obnovení hesla
$token = bin2hex(random_bytes(32));
```

Pro porovnání tajných hodnot, například podpisu, je třeba použít `hash_equals()`. Běžné porovnání pomocí `===` může podle příručky prostřednictvím doby běhu prozradit, od kterého místa se dvě hodnoty liší. Podrobněji: [Bezpečné ukládání hesel v PHP](https://phpsos.de/cs/blog/passwoerter-in-php-speichern/) a [bezpečná náhodná čísla a tokeny](https://phpsos.de/cs/blog/sichere-tokens-php/).

## Escapování výstupů a bezpečné dotazy do databáze

Cross-site scripting vzniká, když se uživatelské vstupy dostanou do HTML bez filtrování. `htmlspecialchars()` převádí speciální znaky na entity HTML. Od PHP 8.1 jsou výchozími příznaky `ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401`; dříve se jednoduché uvozovky nepřeváděly. Kdo musí podporovat starší verze, uvede příznaky výslovně.

SQL injection brání připravené dotazy. Hodnoty se přenášejí odděleně od textu SQL a dotaz nemohou změnit:

```php
<?php
$stmt = $pdo->prepare('SELECT id, name FROM kunden WHERE email = :email');
$stmt->execute(['email' => $email]);
$kunde = $stmt->fetch();

echo htmlspecialchars($kunde['name'] ?? '', ENT_QUOTES, 'UTF-8');
```

Podrobnosti najdete v článku [Prevence cross-site scriptingu v PHP](https://phpsos.de/cs/ratgeber/xss-php/) a na stránce o SQL injection.

Kontrola vstupů je druhý, samostatný krok. `filter_var()` zjistí, zda má hodnota platný formát, například e-mailovou adresu nebo celé číslo v povoleném rozsahu. To ale nenahrazuje escapování při výstupu ani připravený dotaz, protože i formálně platné hodnoty mohou obsahovat speciální znaky.

## Tajemství držet mimo chybová hlášení

Nedoceněným únikem dat jsou stack trace. Pokud ve funkci nastane chyba, PHP zaprotokoluje řetězec volání včetně argumentů. Pokud tam je heslo nebo klíč API, skončí v logu. Od PHP 8.2 takové parametry označuje atribut `#[\SensitiveParameter]`; jejich hodnota se pak v trace objeví jen jako objekt `SensitiveParameterValue`.

```php
<?php
function anmelden(string $benutzer, #[\SensitiveParameter] string $passwort): bool
{
    // ... kontrola ...
    throw new RuntimeException('Datenbank nicht erreichbar');
}
```

Podle poznámky v příručce atribut působí jen u konkrétní implementace, ne u metody rozhraní. Nastavte jej proto tam, kde kód skutečně běží.

## Relace: bezpečná nastavení a nové standardy

Relace jsou častým cílem útoků. Ústřední jsou tři nastavení: `session.use_strict_mode=1` odmítá ID relací, které server sám nevytvořil; `session.cookie_httponly=1` brání přístupu ke cookie relace přes JavaScript; `session.cookie_samesite=Lax` ztěžuje odesílání spolu s požadavky z cizích stránek. Podle přijatého RFC se právě tyto tři hodnoty stanou s PHP 8.6 výchozími. Do té doby je nastavte sami, a i potom výslovně, aby konfigurace nezávisela na výchozích hodnotách.

```ini
session.use_strict_mode = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.cookie_secure = 1
```

Více v článku [Zabezpečení relací v PHP](https://phpsos.de/cs/ratgeber/session-sicherheit-php/).

## Co vestavěné funkce nezajistí

Projekt PHP je ve své bezpečnostní politice jasný: PHP není sandbox. Obejití `open_basedir` nebo `disable_functions`, škodlivé vstupy do `unserialize()` a nekontrolované argumenty funkcí jako `mysqli_query()` se nepovažují za bezpečnostní zranitelnosti PHP. Pro provozovatele to znamená: tato nastavení jsou užitečné zpevnění, ale ne bezpečnostní hranice, a `unserialize()` s cizími daty nemá nic společného.

Druhý bod se týká PHP2Shell. CVE-2026-109618 a CVE-2026-109619 podle php.net umožňují spuštění kódu dříve nebo během toho, než běží vaše aplikace; u mod_php byl předveden exploit ještě předtím, než se vůbec spustí aplikační kód. Žádná funkce ve vašem kódu to nezachytí. Bezpečné programování a aktuální verze PHP jsou dvě oddělené povinnosti a obě musí být splněny.

## Jak konkrétně pomáhá PHP SOS?

V [bezpečnostním auditu aplikací PHP](https://phpsos.de/cs/leistungen/sicherheitsaudit-penetrationstest/) cíleně kontrolujeme:

- **Zacházení s hesly a tokeny:** Používají se `password_hash()` a `random_bytes()`, nebo existují vlastní řešení?
- **Výstupy a dotazy:** Kde se vstupy dostanou do HTML, SQL nebo příkazů shellu bez filtrování?
- **Konfigurace relací:** Jsou nastaveny Strict Mode, HttpOnly, SameSite a Secure a nelze je přepsat?
- **Běh:** Běží podporovaná verze PHP s opravami z 21. 10. 2026?


## Časté dotazy

**Jak se v PHP správně ukládají hesla?**  
Pomocí password_hash() a PASSWORD_DEFAULT, kontroluje se pomocí password_verify(). Sloupec v databázi by měl podle příručky pojmout až 255 znaků, protože se výchozí algoritmus může v budoucnu změnit.

**Hodí se rand() pro tokeny?**  
Ne. Pro tokeny, klíče a odkazy pro obnovu hesla je třeba použít random_bytes() nebo random_int(). Podle příručky vracejí kryptograficky bezpečné náhodné hodnoty.

**Od kdy je Sodium součástí PHP?**  
Rozšíření Sodium pro moderní kryptografii je součástí PHP od verze 7.2.0. Starší verze je mohly doplnit jen přes PECL.

**Chrání tyto funkce před PHP2Shell?**  
Ne. Chrání před chybami ve vaší aplikaci. PHP2Shell sídlí v samotném interpretu a uzavře ho jen aktualizace PHP, následovaná restartem všech procesů PHP.

**Mění se standardy relací v PHP 8.6?**  
Ano, podle přijatého RFC se session.use_strict_mode a session.cookie_httponly zapnou ve výchozím stavu a session.cookie_samesite se nastaví na Lax. Stávající konfigurace byste přesto měli nastavit sami výslovně.

## Zdroje

- [Příručka PHP: password_hash()](https://www.php.net/manual/en/function.password-hash.php)
- [Příručka PHP: random_bytes()](https://www.php.net/manual/en/function.random-bytes.php)
- [Příručka PHP: hash_equals()](https://www.php.net/manual/en/function.hash-equals.php)
- [PHP RFC: Secure Session Configuration Defaults](https://wiki.php.net/rfc/session_security_defaults)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
