Current situation: PHP2Shell. Two critical PHP vulnerabilities (CVSS 9.2) allow malicious code without login. Patches since 21 October 2026.What to do now →

Validating and Filtering Input in PHP with filter_var

How developers check input in PHP with filter_var, filter_input_array and allowlists, which filters are deprecated and why validation does not replace prepared statements and output encoding.

Last updated 6 min readSecure development

In short: You validate input in PHP with filter_var() or filter_input_array() and clear rules per field: type, range, length and permitted values. Whatever does not fit is rejected, not silently rebuilt. Validation is important for clean data and a smaller attack surface, but it replaces neither prepared statements against SQL injection nor context-appropriate encoding against XSS.

Why "Filter Everything" Is Not a Concept

In many older PHP projects, there is a loop at the start of every file that sends all values from $_GET and $_POST through a sanitizing function. That feels safe. In practice, it sometimes removes too much, such as apostrophes in surnames, and sometimes too little, because the same function is supposed to apply to SQL, HTML and file names at once.

Sensible validation works differently: it asks, for each field, what is allowed there. An order quantity is an integer between 1 and an upper limit. A sort field is one of three fixed values. A date is in a certain format and after another date. Everything else is rejected.

OWASP distinguishes two levels here: the syntactic check of whether a value has the right form, and the semantic check of whether it makes sense in context. Both belong on the server; checks in the browser serve usability only.

Which Filters PHP Provides

The filter extension is included in PHP by default. The most important validation filters according to the PHP manual:

Filter Checks Important options and notes
FILTER_VALIDATE_INT integer min_range, max_range
FILTER_VALIDATE_FLOAT decimal number decimal for the separator
FILTER_VALIDATE_BOOL boolean value distinguishable from "false" with FILTER_NULL_ON_FAILURE
FILTER_VALIDATE_EMAIL form of an email address does not prove that the mailbox exists
FILTER_VALIDATE_URL form of a URL check the scheme yourself, no internationalized domains
FILTER_VALIDATE_IP IP address flags for private and reserved ranges
FILTER_VALIDATE_REGEXP custom pattern anchor the pattern with ^ and $

The sanitizing filter FILTER_SANITIZE_STRING has been deprecated since PHP 8.1. Anyone still using it should switch validation to suitable filters and use htmlspecialchars() for output.

filter_var in PHP: Checking a Form Cleanly

With filter_input_array(), an entire form can be checked against a definition in one step. Unknown fields are not accepted:

<?php
declare(strict_types=1);

$regeln = [
    'menge'   => ['filter' => FILTER_VALIDATE_INT,
                  'options' => ['min_range' => 1, 'max_range' => 50]],
    'email'   => FILTER_VALIDATE_EMAIL,
    'agb'     => ['filter' => FILTER_VALIDATE_BOOL, 'flags' => FILTER_NULL_ON_FAILURE],
    'versand' => ['filter' => FILTER_VALIDATE_REGEXP,
                  'options' => ['regexp' => '/^(standard|express)$/']],
];

$daten  = filter_input_array(INPUT_POST, $regeln) ?? [];
$fehler = [];

foreach ($regeln as $feld => $_) {
    if (!array_key_exists($feld, $daten) || $daten[$feld] === false || $daten[$feld] === null) {
        $fehler[] = $feld;
    }
}

if ($fehler !== []) {
    http_response_code(422);
    // output an error message per field, do not process the input
}

Note the strict comparisons with ===: a valid value 0 or an empty string must not be confused with a failure. Why loose comparisons are dangerous here is explained in Type Juggling: Loose Comparisons as a Security Risk. How PHP provides request data in the first place is described in Superglobals: $_GET, $_POST, $_FILES and More.

Pitfalls with Email, URL and Free Text

Some filters do less than their name suggests:

  1. URLs: FILTER_VALIDATE_URL also accepts schemes other than http and https. Afterwards, check the scheme against a fixed list with parse_url(). If the URL is fetched by the server, additional rules apply, see Preventing Server-Side Request Forgery in PHP.
  2. Email addresses: The filter checks the form, not ownership. Anyone who needs certainty sends a confirmation link.
  3. Free text: Names, messages and addresses can hardly be restricted to certain characters in a sensible way. Check length and character encoding, for example with mb_check_encoding($wert, 'UTF-8'), and rely on encoding on output.
  4. Regular expressions: Without ^ and $, a pattern only checks whether it occurs somewhere in the text, not whether the whole value matches.
  5. Arrays instead of strings: An attacker can send an array instead of a text. Without a type check, this leads to warnings or unexpected behavior.
Tip

For fixed selection values such as sorting, language or shipping method, an allowlist with in_array($wert, $erlaubt, true) or a match expression is simpler and stricter than any regular expression.

Another common mistake concerns numbers with meaning, such as prices, quantities or discounts. That a value is a valid number says nothing about whether it is permitted in context. A negative quantity, a discount above the goods value or a price taken from the form instead of the database are classic logic errors. Prices, permissions and status values therefore always belong on the server and never in hidden form fields.

JSON, Webhooks and Imports: Validation Beyond Forms

Many inputs today are no longer forms but JSON requests from apps, webhooks from payment services or import files from suppliers. The same applies there: read in safely first, then check. OWASP advises limiting size and nesting depth before data is even read in. In PHP, json_decode() with a maximum depth and JSON_THROW_ON_ERROR handles part of this:

<?php
$roh = file_get_contents('php://input', false, null, 0, 65536); // at most 64 KB
try {
    $daten = json_decode($roh, true, 16, JSON_THROW_ON_ERROR);
} catch (JsonException) {
    http_response_code(400);
    exit;
}
if (!is_array($daten) || !is_string($daten['bestellnummer'] ?? null)) {
    http_response_code(422);
    exit;
}

With webhooks, before the check of the content comes the question of whether the message really comes from the stated service. If the service offers a signature, verify it and compare it with hash_equals(). With import files, check every row like a form and abort cleanly on errors instead of accepting half records.

Error messages should name the affected field but not return the rejected value unfiltered. Otherwise the error message itself becomes a new output point that has to be encoded again.

What Validation Cannot Do

Validation reduces the attack surface, but according to OWASP it is not the main defense against injection. A surname with an apostrophe is valid and must still reach a database safely. That works only with prepared statements, as the guide Preventing SQL Injection in PHP shows. And a valid comment text becomes harmless only through context-appropriate encoding on output.

The right order is therefore: validate on receipt, parameterize on processing, encode on output. In addition, size limits belong in front: requests that are far too large should not even be fully processed by the web server and PHP. Settings such as post_max_size serve this purpose and should be reviewed after PHP2Shell anyway.

How does PHP SOS help?

  • We record all inputs of your application: forms, interfaces, uploads, imports and webhooks.
  • We define what is allowed per field and replace blanket sanitizing functions with clear checks.
  • We check whether database access and output are secured independently of validation.
  • We document the rules so that your team can carry them forward for new features.

A systematic review is described by the service Security Audit and Penetration Test.

Frequently asked questions

What is the difference between validating and sanitizing?

Validating checks whether a value meets expectations and rejects it otherwise. Sanitizing changes the value, for example by removing characters. For security decisions, rejecting is almost always better, because a silently altered value can create new errors.

Can FILTER_SANITIZE_STRING still be used?

The filter has been deprecated since PHP 8.1. The PHP manual instead recommends htmlspecialchars() on output. You should convert existing code, because deprecated functions may be removed in future versions.

Is FILTER_VALIDATE_URL enough to make links safe?

No. The filter only checks the form of the address. According to the manual, a valid URL does not have to start with http, so you have to check the scheme yourself against an allowlist such as https. The filter rejects internationalized domain names.

Do I also have to validate data from my own database?

On output, it is treated as untrustworthy and encoded appropriately. Validating again makes sense if the data originally came from outside and is now used in a new context, for example as a file name or part of a command.

Does validation help against PHP2Shell?

No. PHP2Shell concerns the processing of requests in the PHP interpreter, before your application code even runs. Only the PHP update followed by a restart of all PHP processes helps against it.

Sources

Immediate contact

Every hour counts. Write to us now.

Briefly describe your system, PHP version and observation. We will get back to you, clarify the situation and work remotely until the incident is closed and documented.

Immediate help