Request: Prevention of FPD in Fatal/Parse/Other Errors

php.internals

Ted Phillips

10 years ago
Hi, Currently the only real "solution" to Full Path Disclosure vulnerabilities in software developed in PHP is to keep display_errors disabled. Even if a developer wishes to prevent disclosures at the application level instead, it is not currently possible with the current implementation of PHP: - The output text of Parse and Fatal Errors cannot be modified, far as I can tell - Some errors can be triggered by specially crafted requests and occur before the application initializes (think max_input_vars, and other input-related errors) - Custom error handlers are needed to remove path strings for other errors, but if multiple frameworks are used together, the error handlers may conflict on which strings should be removed. Only 1 shutdown function can actually be used. I propose that the default error handling in PHP be updated as follows: - Include a new ini directive for fpd_prevention, defaulting to On or a string for replacement, like the ever-popular [path] - Provide a new function hide_fpd_path (or pick a better name), defined as: function hide_fpd_path($path, $replacement_string = '') $replacement_string would default to the system defined [path] string or other ini value. The function provides an interface to a registry of paths that should not appear in error output, should an error occur. - Automatically treat the paths in include_path (and updated by set_include_path) as if they were registered with hide_fpd_path, using the default replacement string or other [include-path] string. Because of set_include_path's existence, it may be best to apply this at error time. - Automatically register the containing path of PHP_SELF at initialization. This will deal will fatal errors occurring before the application can specify its paths, such as when max_input_vars is exceeded by a crafted request. - When outputting any error, including E_ERROR or E_PARSE, filter the file paths with this new registry, applying the most specific pathnames first. Now the security implications of display_errors are largely mitigated. - Many custom error handlers use debug_backtrace or debug_print_backtrace. I would suggest adding a new DEBUG_BACKTRACE_SKIP_FPD constant in case the error handler absolutely does not want the paths filtered by PHP. However, because there are cases where multiple frameworks have error handlers with different internal filters, I believe the default behavior of debug_backtrace should pre-filter those. This way, developers of other modules and plugins can still add filters, regardless of whatever framework is on top. By resolving full path disclosures at the scripting engine level, or at least providing a built-in solution for them (which per my suggestion could be disabled if system administrators don't want to use it), PHP can help change the conversation on full path disclosures: Many instances of full-path disclosure vulnerabilities currently go unresolved because there is a debate whether they are configuration issues (of display_errors) vs bugs in the application, because some developers are resistant to writing software that works well in tandem with the software of other developers, and because many developers do not want to release security patches every time a fatal error is found, especially when it is still possible to cause fatal errors that the application has no control over. The question is sometimes raised whether FPD issues are really worthy of being considered security issues at all; however, I have seen authorities issue CVEs for them. I think this suggestion provides a solution for all these camps of people. Thanks, Ted

Rowan Collins

10 years ago
Hi Ted, On 30/06/2016 17:17, Ted Phillips wrote:
> - Include a new ini directive for fpd_prevention, defaulting to On or a string for replacement, like the ever-popular [path]
[...]
> - Automatically register the containing path of PHP_SELF at initialization. This will deal will fatal errors occurring before the application can specify its paths, such as when max_input_vars is exceeded by a crafted request.
Could you give some concrete examples of what an error message would look like before and after this change? I'm trying to understand what the tradeoff might be for users trying to track down where an error has occurred. Regards,
-- Rowan Collins [IMSoP]

Ted Phillips

10 years ago
Right, so I think the only issue would be when we hide the path to {main} and anything relative to that. The trade-off would be that the developer will need to be familiar with his/her own directory structure. After thinking about it some more, there is the possibility that there are multiple scripts with the same name at multiple levels (e.g. /www/a.php and /www/deeper/a.php) that both might be accessed directly. Therefore, I think we should use the document root (I can't imagine a case where PHP would not know it), in case it is different from PHP_SELF (not familiar with how they work together). I don't know of another way that would be useful for max_input_vars errors and similar. Comparatively, if we hide anything in the include path, and replace it with [inc], the developer will know it's in one of the include paths. Any other custom paths and replacements would usually be developer specified like fpd_hide_path('/home/acct/git/lib', '[git]'), so the developer would be able to figure it out. If the error is written to a log file, I'm not sure that there is a reason to filter paths in the log file, but maybe you want to provide an option for it. My suggestion is mainly for errors that are sent to the browser or that are likely to be sent to the browser after being caught (debug_backtrace). Here's a case showing what happens to the document root: Before: ------- Fatal Error: Uncaught Error: Call to undefined method Fake_Class::nonexistent_method() in /home/acct/www/script.php:2 Stack trace: #0 {main} thrown in /home/acct/www/deep/script.php on line 2 After: ------ Fatal Error: Uncaught Error: Call to undefined method Fake_Class::nonexistent_method() in /[path]/script.php:2 Stack trace: #0 {main} thrown in /[path]/deep/script.php on line 2 If the first error is sent to the browser, a third party learns the site's acct name on the web server. If the second error is sent to the browser, the third party doesn't really learn anything. When the traces get longer, and we include files above the root for some reason, maybe I would say just use /[path]/../sidepath if there's not a specific replacement for it. This would only be an problem if users are including files from other people's accounts or are using really strange paths; even so, the output wouldn't disclose any more information than the current implementation. Thanks, Ted

Niklas Keller

10 years ago
Ted Phillips <pegasus@vaultwiki.org> schrieb am Do., 7. Juli 2016, 21:37:
> Right, so I think the only issue would be when we hide the path to {main} > and anything relative to that. The trade-off would be that the developer > will need to be familiar with his/her own directory structure. After > thinking about it some more, there is the possibility that there are > multiple scripts with the same name at multiple levels (e.g. /www/a.php and > /www/deeper/a.php) that both might be accessed directly. Therefore, I think > we should use the document root (I can't imagine a case where PHP would not > know it), in case it is different from PHP_SELF (not familiar with how they > work together). I don't know of another way that would be useful for > max_input_vars errors and similar. > > Comparatively, if we hide anything in the include path, and replace it > with [inc], the developer will know it's in one of the include paths. > Any other custom paths and replacements would usually be developer > specified like fpd_hide_path('/home/acct/git/lib', '[git]'), so the > developer would be able to figure it out. > > If the error is written to a log file, I'm not sure that there is a reason > to filter paths in the log file, but maybe you want to provide an option > for it.My
Why do you want to hide paths in log files? suggestion is mainly for errors that are sent to the browser or that are
> likely to be sent to the browser after being caught (debug_backtrace). > > Here's a case showing what happens to the document root: > > Before: > ------- > Fatal Error: Uncaught Error: Call to undefined method > Fake_Class::nonexistent_method() in /home/acct/www/script.php:2 > Stack trace: #0 {main} thrown in /home/acct/www/deep/script.php on line 2 > > After: > ------ > Fatal Error: Uncaught Error: Call to undefined method > Fake_Class::nonexistent_method() in /[path]/script.php:2 > Stack trace: #0 {main} thrown in /[path]/deep/script.php on line 2 > > If the first error is sent to the browser, a third party learns the site's > acct name on the web server. >
FPD isn't a vulnerability by itself. It can just be used as help if other vulnerabilities appear. If the second error is sent to the browser, the third party doesn't really
> learn anything.
The third party learns there's a fake class with some method that is doesn't have. If there's a stack trace, there may be passwords or other sensitive information in it. Short story short: You shouldn't ever display errors from PHP in production. Thus such a change is unneccessary. When the traces get longer, and we include files above the root for some
> reason, maybe I would say just use /[path]/../sidepath if there's not a > specific replacement for it. This would only be an problem if users are > including files from other people's accounts
Seems like you're on shared hosting with everything being in the webroot automatically. That's often not the case and many apps will explixitly store their code outside of the document root so they don't disclose their source in case of an server configuration error. or are using really strange paths; even so, the output wouldn't disclose