# Built-in Security Functions of PHP

> PHP security functions at a glance: passwords, randomness, escaping, cryptography, sessions and where their limits lie with flaws like PHP2Shell.

Source: https://phpsos.de/en/php/eingebaute-sicherheitsfunktionen/  
As of: 25/10/2026  
Provider: PHP SOS, PHP emergency help and server security. Immediate help via WhatsApp: https://wa.me/491736494914?text=Hello%20PHP%20SOS%2C%20we%20need%20help%20with%20our%20PHP%20server.%20Company%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**In short:** For the most common security tasks, PHP ships with ready-made, vetted functions: `password_hash()` for passwords, `random_bytes()` and `random_int()` for tokens, `htmlspecialchars()` against cross-site scripting, prepared statements with PDO against SQL injection, `hash_equals()` for safe comparisons and the Sodium extension for cryptography. Anyone who uses these building blocks consistently avoids most application vulnerabilities. They do not help against flaws in the PHP interpreter itself, as with PHP2Shell; there only the update helps.

## Why built-in PHP security functions?

Writing security code yourself is error-prone. A home-made password hash, a self-built token generator or a homegrown filter for HTML output often looks right and still is not. The built-in functions are documented, tested and maintained with PHP updates.

For decision-makers this means: do not ask your agency or development team whether security functions were built in-house, but whether the language's standard functions are used. The table gives an overview.

| Task | Function | Available since | Important note |
|---|---|---|---|
| Storing passwords | `password_hash()`, `password_verify()` | PHP 5.5 | column up to 255 characters; bcrypt truncates at 72 bytes |
| Random tokens | `random_bytes()`, `random_int()` | PHP 7.0 | never `rand()` or `mt_rand()` for secrets |
| Randomness, object-oriented | `Random\Randomizer` | PHP 8.2 | same security, more flexible interface |
| Escaping HTML output | `htmlspecialchars()` | always | default flags since 8.1 also escape single quotes |
| Constant-time comparison | `hash_equals()` | PHP 5.6 | user input as the second argument |
| Modern cryptography | Sodium (`sodium_*`) | PHP 7.2 | instead of home-made encryption |
| Secrets in stack traces | `#[\SensitiveParameter]` | PHP 8.2 | value does not appear in the trace |
| Validating input | `filter_var()` | PHP 5.2 | checks format, does not replace escaping |

## Passwords and tokens

`password_hash()` creates a salted hash, and `password_verify()` checks it. With `PASSWORD_DEFAULT`, PHP currently uses bcrypt; since PHP 8.4 the default cost factor is, according to the manual, 12 instead of 10. Because the default algorithm may change, the manual recommends a database column of 255 characters. With bcrypt, passwords are cut off after 72 bytes, which is worth bearing in mind for very long passphrases.

```php
<?php
// Registration
$hash = password_hash($passwort, PASSWORD_DEFAULT);

// Login, including automatic upgrading of old hashes
if (password_verify($eingabe, $hash)) {
    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        $hash = password_hash($eingabe, PASSWORD_DEFAULT); // store again
    }
}

// Token for a password reset link
$token = bin2hex(random_bytes(32));
```

For comparing secrets, such as a signature, `hash_equals()` should be used. According to the manual, an ordinary comparison with `===` can reveal through its run time at which position two values differ. In more detail: [Storing passwords securely in PHP](https://phpsos.de/en/blog/passwoerter-in-php-speichern/) and [secure random numbers and tokens](https://phpsos.de/en/blog/sichere-tokens-php/).

## Escaping output and querying the database safely

Cross-site scripting arises when user input lands in HTML unfiltered. `htmlspecialchars()` converts special characters into HTML entities. Since PHP 8.1 the default flags are `ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401`; before that, single quotes were not converted. Anyone who has to support older versions specifies the flags explicitly.

Prepared statements prevent SQL injection. The values are transmitted separately from the SQL text and cannot alter the query:

```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');
```

The details are under [Preventing cross-site scripting in PHP](https://phpsos.de/en/ratgeber/xss-php/) and on the page about SQL injection.

Validating input is a second, separate step. `filter_var()` determines whether a value has a valid format, such as an email address or an integer within a permitted range. This does not replace escaping on output or a prepared statement, however, because even formally valid values can contain special characters.

## Keeping secrets out of error messages

An underrated data leak is stack traces. When an error occurs in a function, PHP logs the call chain including the arguments. If a password or API key is in there, it ends up in the log. Since PHP 8.2, the attribute `#[\SensitiveParameter]` marks such parameters; their value then appears in the trace only as a `SensitiveParameterValue` object.

```php
<?php
function anmelden(string $benutzer, #[\SensitiveParameter] string $passwort): bool
{
    // ... check ...
    throw new RuntimeException('Datenbank nicht erreichbar');
}
```

According to a note in the manual, the attribute takes effect only on the concrete implementation, not on the method of an interface. Set it, therefore, where the code actually runs.

## Sessions: secure settings and new defaults

Sessions are a frequent target of attack. Three settings are central: `session.use_strict_mode=1` rejects session IDs that the server did not create itself; `session.cookie_httponly=1` prevents JavaScript access to the session cookie; `session.cookie_samesite=Lax` makes it harder for the cookie to be sent along from other sites. According to the accepted RFC, exactly these three values become the default with PHP 8.6. Set them yourself until then, and explicitly afterwards too, so that the configuration does not depend on the default.

```ini
session.use_strict_mode = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.cookie_secure = 1
```

More under [Securing sessions in PHP](https://phpsos.de/en/ratgeber/session-sicherheit-php/).

## What built-in functions do not do

The PHP project is clear in its security policy: PHP is not a sandbox. Bypasses of `open_basedir` or `disable_functions`, malicious input to `unserialize()` and unchecked arguments to functions such as `mysqli_query()` do not count as security vulnerabilities of PHP. For operators this means: these settings are useful hardening but not a security boundary, and `unserialize()` has no business with foreign data.

The second point concerns PHP2Shell. According to php.net, CVE-2026-109618 and CVE-2026-109619 allow code execution before or while your application runs; for mod_php, an exploit was demonstrated before any application code is executed at all. No function in your code can intercept that. Secure programming and a current PHP version are two separate obligations, and both must be met.

## How does PHP SOS help?

In the [security audit for PHP applications](https://phpsos.de/en/leistungen/sicherheitsaudit-penetrationstest/), we check specifically:

- **Password and token handling:** Are `password_hash()` and `random_bytes()` used, or are there home-made solutions?
- **Output and queries:** Where does input land unfiltered in HTML, SQL or shell commands?
- **Session configuration:** Are strict mode, HttpOnly, SameSite and Secure set and not overridable?
- **Runtime:** Is a supported PHP version running with the fixes of 21 October 2026?


## Frequently asked questions

**How do you store passwords correctly in PHP?**  
With password_hash() and PASSWORD_DEFAULT, verified with password_verify(). According to the manual, the database column should hold up to 255 characters, because the default algorithm may change in future.

**Is rand() suitable for tokens?**  
No. For tokens, keys and password links, use random_bytes() or random_int(). According to the manual, they deliver cryptographically secure random values.

**Since when has Sodium been included in PHP?**  
The Sodium extension for modern cryptography has been part of PHP since PHP 7.2.0. Older versions could only add it via PECL.

**Do these functions protect against PHP2Shell?**  
No. They protect against errors in your application. PHP2Shell sits in the interpreter itself and is closed only by a PHP update, followed by a restart of all PHP processes.

**Will the session defaults change in PHP 8.6?**  
Yes, according to the accepted RFC, session.use_strict_mode and session.cookie_httponly will be switched on by default and session.cookie_samesite set to Lax. You should still set existing configurations explicitly yourself.

## Sources

- [PHP Manual: password_hash()](https://www.php.net/manual/en/function.password-hash.php)
- [PHP Manual: random_bytes()](https://www.php.net/manual/en/function.random-bytes.php)
- [PHP Manual: 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/)
