include and require run whatever file they are given. If a visitor can influence the file name, they can make your server run code you never intended.
The dangerous pattern
<?php
// DANGEROUS: do not write this
$page = $_GET["page"];
include $page . ".php";
A request such as ?page=../../config/database could load a file you never meant to expose. In some configurations (allow_url_include), an attacker can even include code from another website. This kind of attack is called local file inclusion (LFI) or remote file inclusion (RFI).
The fix: an allow-list
Do not build file names from input. Map a known key to a known file.
<?php
$pages = [
"home" => "pages/home.php",
"about" => "pages/about.php",
"contact" => "pages/contact.php",
];
$requested = "../../etc/passwd"; // imagine this came from $_GET["page"]
$file = $pages[$requested] ?? $pages["home"];
echo $file;
pages/home.php
Only the three listed pages can ever be included. Anything else falls back to the home page (or a 404 page).
Validate the key with a strict pattern
<?php
function pageName(string $input): ?string
{
return preg_match('/^[a-z0-9-]{1,40}$/', $input) === 1 ? $input : null;
}
var_dump(pageName("about-us"));
var_dump(pageName("../secret"));
string(8) "about-us"
NULL
Even with a pattern, check that the file is in your list or that it exists in a fixed folder.
Keep PHP files outside the web root
Only files that must be reached by a browser should live in the public folder. Put configuration, libraries and templates above it. If the server is ever misconfigured and prints PHP files as text, your secrets stay safe.
Server settings
In php.ini:
allow_url_include = Off
allow_url_fopen = Off ; if you do not need to read remote URLs
allow_url_include is off by default. Leave it off.
Protect include-only files
A file that is meant to be included but could also be opened directly by URL should stop if it is not included.
<?php
if (!defined("APP_ROOT")) {
http_response_code(403);
exit("Direct access is not allowed.");
}
Defining APP_ROOT in your front page makes the check pass only when your own code includes the file.