[RFC] [Discussion] Change default for zend.exception_ignore_args INI setting

php.internals

Andrew Lyons

1 year ago
Hi everyone, I've been working on a new RFC which proposes changing the default value for the zend.exception_ignore_args INI setting from Off to On. The intent of this change is to make PHP installations safer by default and prevent the accidental release of sensitive information in stack traces. * RFC: https://wiki.php.net/rfc/exception_ignore_args_default_value * Implementation: https://github.com/php/php-src/pull/18215 Best wishes, Andrew

Tim Düsterhus

1 year ago
Hi Am 2025-04-09 04:00, schrieb Andrew Lyons:
> The intent of this change is to make PHP installations safer by default > and > prevent the accidental release of sensitive information in stack > traces. > > * RFC: https://wiki.php.net/rfc/exception_ignore_args_default_value > * Implementation: https://github.com/php/php-src/pull/18215
As I had said on GitHub before, but to put it onto the list for visibility: I'd rather see the value in `php.ini-production` being changed to `Off` to match the built-in default. see https://github.com/php/php-src/pull/18215#issuecomment-2768618516 Best regards Tim Düsterhus

Bob Weinand

1 year ago
Hey, On 10.4.2025 17:19:57, Tim Düsterhus wrote:
> Hi > > Am 2025-04-09 04:00, schrieb Andrew Lyons: >> The intent of this change is to make PHP installations safer by >> default and >> prevent the accidental release of sensitive information in stack traces. >> >> * RFC: https://wiki.php.net/rfc/exception_ignore_args_default_value >> * Implementation: https://github.com/php/php-src/pull/18215 > > As I had said on GitHub before, but to put it onto the list for > visibility: > > I'd rather see the value in `php.ini-production` being changed to > `Off` to match the built-in default. see > https://github.com/php/php-src/pull/18215#issuecomment-2768618516
Full agreement with Tim here - make PHP friendly to development. There are only few places where secrets would be actually relevant, and those can be covered by #[SensitiveParameter]. I've been quite annoyed a few times - I install PHP, promptly all args missing in my logs. Not a great experience for me to then first have to toggle it. Also, it's something which you need to be even aware of - newcomers to PHP would see the stacktraces not containing arguments and not even know that they could enable them. @Tim: You have my full support to propose a counterproposal here. Bob

Larry Garfield

1 year ago
On Thu, Apr 10, 2025, at 1:53 PM, Bob Weinand wrote:
>> I'd rather see the value in `php.ini-production` being changed to >> `Off` to match the built-in default. see >> https://github.com/php/php-src/pull/18215#issuecomment-2768618516 > > > Full agreement with Tim here - make PHP friendly to development. > > There are only few places where secrets would be actually relevant, and > those can be covered by #[SensitiveParameter].
I tend to agree as well. #[SensitiveParameter] is the better solution in the 95% case. If there are libraries still not using it (as the RFC suggests), those should have bugs (or preferably patches) filed against them. The great thing about attributes is they're intrinsically backward compatible, so there's no meaningful cost to adding/patching liberally. --Larry Garfield

Kamil Tekiela

1 year ago
I can't find any information on why this setting was introduced. I guess that it was either to protect sensitive information or to reduce verbosity in the logs, but either one seems like a poor reason for this setting to exist. The INI comment seems to suggest that it was introduced to protect sensitive information, but that doesn't make sense to me since exceptions aren't revealed to the end user. And while verbose, arguments in stack traces are useful. On top of that, it seems to me like this is already covered by exception_string_param_max_len. If you set the length to 0, isn't it the same as setting exception_ignore_args to On? Or does that mean that no truncation is applied? I suppose if this INI setting must exist, then it should be set to Off in both development and production environments. The max length should probably be increased, too. I would also appreciate it if it gets clarified in the documentation why the two settings exist and what the difference between them is. And I wouldn't object to removing exception_ignore_args if it's a redundant setting. P.S. The RFC mentions execution_ignore_args too, but I assume that's a typo.

Andrew Lyons

1 year ago
> I can't find any information on why this setting was introduced.
No, I couldn't find anything either. I did try to investigate and all I could find was that it was introduced by Joe Watkins in 0819e6dc9b4788e5d44b64f8e606a56c969a1588 in 2019 but I haven't been able to find any history of RFC or discussion about it.
> I guess that it was either to protect sensitive information or to reduce > verbosity in the logs, but either one seems like a poor reason for > this setting to exist. The INI comment seems to suggest that it was > introduced to protect sensitive information, but that doesn't make > sense to me since exceptions aren't revealed to the end user.
In an ideal world they are never revealed to the end user, but real-world solutions fall short. It's not uncommon for people to have display_errors enabled for some reason or another. It is also possible for sites to be misconfigured, or for applications to make mistakes and accidentally dump stack traces without realising that they may contain arguments. Not all applications make use of an error handling framework, and people may enable display_errors without understanding the ramifications. Ultimately developers and administrators are all human (for now). We can, and do, make mistakes. PHP is already capable of preventing information leakage from such mistakes, but it is currently not the default configuration. Let's make it the default. This does not stop people who have actively configured stack trace arguments from having access to those args.
> And while verbose, arguments in stack traces are useful. On top of that, > it seems to me like this is already covered by > exception_string_param_max_len. If you set the length to 0, isn't it > the same as setting exception_ignore_args to On? Or does that mean > that no truncation is applied?
Completely off the top of my head, it is probably still worth having both. A configurable max length isn’t the same as a simple on/off toggle that can be flipped directly in code. The max length was actually introduced later by Tyson Andre in 07db64156e180c30daa5ab5d41ed72f9bba77e6d in 2020 and discussed here: https://externals.io/message/110717 From the look of the commit it _only_ applies to the `getTraceAsString()` method, and not `getTrace()`. It was intended to allow more information to be dumped than the previously hard-coded 15 bytes. Even setting it to 0 doesn’t consistently remove args — e.g., getTrace() still includes them, so merging these two is not really a clear or easy solution.
> I suppose if this INI setting must exist, then it should be set to Off in both development and production environments.
Can you elaborate? Setting it Off by default increases the risk of accidental information leakage when apps aren’t correctly configured. That's great for developers, but for production sites that's not a good idea. There is no way to guarantee that every PHP application is a perfect, well-configured, system which uses the latest frameworks. Many of us are dealing with massive legacy codebases. Where a framework correctly configures error handling, and determines that it needs the arguments as part of that, it is surely able to also call `ini_set('zend.exception_ignore_args', 0)` at the same time as it configures that error handler.
> The max length should probably be increased, too. I would also appreciate it if it gets > clarified in the documentation why the two settings exist and what the > difference between them is. And I wouldn't object to removing > exception_ignore_args if it's a redundant setting.
I agree it would be good to clarify this, but I don't think that either should be removed. Reading the Internals discussion for the length makes it a little clearer as to why it exists and why they're separate. I'd suggest that this can be done via PR process without need for an RFC. In any case, I would strongly object to removing `exception_ignore_args`. It is not a redundant setting and it is critical to many in production.
> P.S. The RFC mentions execution_ignore_args too, but I assume that's a typo.
Thanks - I'll fix that. On Fri, 11 Apr 2025 at 04:04, Kamil Tekiela <tekiela246@gmail.com> wrote:

Kamil Tekiela

1 year ago
> In an ideal world they are never revealed to the end user, but
real-world solutions fall short. It's not uncommon for people to have display_errors enabled for some reason or another. It is also possible for sites to be misconfigured, or for applications to make mistakes and accidentally dump stack traces without realising that they may contain arguments. Not all applications make use of an error handling framework, and people may enable display_errors without understanding the ramifications. Developers and system administrators are able to mess up their INI settings in whatever way they want. We can't prevent that. It's a much bigger issue if someone has display_errors set to On in production environment, and they could also have set the exception_ignore_args to Off. We can only suggest the default values for the production environment. In such a case, I'd argue that setting exception_ignore_args to Off while having display_errors=Off isn't a big problem. The exception is already sensitive information, maybe not on the level of PII, but it's definitely something that needs to be securely protected from end users.
>Can you elaborate? Setting it Off by default increases the risk of
accidental information leakage when apps aren’t correctly configured. That's great for developers, but for production sites that's not a good idea. There is no way to guarantee that every PHP application is a perfect, well-configured, system which uses the latest frameworks. Many of us are dealing with massive legacy codebases. Accidental leakage isn't a good argument for keeping this setting On in production. There should be no accidental leakage, and to prevent that PHP recommends setting display_errors to Off in the production environment. It is possible that some application messed up so badly that even with display_errors=Off they might still expose stack trace to the user, but that's a security bug that should already be reported to the application developers regardless of whether the stack trace contains arguments or not. In my opinion, the only valid argument here is whether it's ok for arguments to be present in the log files. This would only occur due to an Exception, Error or Warning/Notice being triggered by the code. Error log files are supposed to be checked regularly, and errors should be fixed. Thus, it's only in exceptional situations that this would happen, i.e. yet an undiscovered bug. Log files should be treated with similar care as the database, with only the administrators having controlled access. This means that the only information that should not appear in log files is the same information one wouldn't expect to find in the database, e.g. passwords. We have a solution for this now, it's #[SensitiveParameter]. You do bring up a good point that many applications still do not use it due to wanting to support older PHP versions, but keep in mind that exception_ignore_args is only available as of PHP 7.4, so users of older PHP versions are screwed either way. Those on PHP 7.4 > 8.2 can use this INI setting as a substitute for the new attribute. On the other hand, if an undiscovered bug happens in the production environment, it is very useful to know exactly under what circumstances that happened. Having the full arguments or even just a truncated part in the stack trace in your log files could make finding the bug much easier. So it's probably even more important to have the arguments in the production environment than it is in the development.

Andrew Lyons

1 year ago
> > Full agreement with Tim here - make PHP friendly to development.
Generally I do agree with this sentiment. Languages should be developer-friendly, but not at the expense of safety. Developers are able to configure an INI setting in multiple ways including modifying an ini file, using the `ini_set()` method, and SAPI configuration. We cannot assume that every installation of every PHP webapp out there is managed by someone who knows, and understands, the risks of every INI setting.
> > There are only few places where secrets would be actually relevant, and > > those can be covered by #[SensitiveParameter]. > I tend to agree as well. #[SensitiveParameter] is the better solution in the 95% case. If there are libraries still not using it (as the RFC suggests), those should have bugs (or preferably patches) filed against them. > > The great thing about attributes is they're intrinsically backward compatible, so there's no meaningful cost to adding/patching liberally.
While I agree that #[SensitiveParameter] is a nice idea, I believe it’s currently insufficient in both implementation and adoption to be relied on as the primary means of protecting sensitive data. Firstly it isn't only a few places where secrets are relevant. People have different views of what should be considered Sensitive. For example, under EU regulations anything which contains any Personally Identifiable Information (PII) should be considered sensitive, that includes first and last name, e-mail address, phone number, IP address, and so on. As developers we may not agree, but a stack trace which was displayed and included some of this information technically needs to be declared to the data commissioner in the EU within 72 hours (https://www.edps.europa.eu/data-protection/our-role-supervisor/personal-data-breach_en). Secondly you cannot apply the #[SensitiveParameter] attribute to object properties. If you have an object which contains any passwords or PII, then you have to declare anywhere that you receive that object in a parameter with the #[SensitiveParameter] attribute. In many projects that is a huge number of methods - for example anywhere you format a string _might_ be passed a string which contains PII and therefore that would arguably need to have the attribute. In the case of libraries like Guzzle where any Request may contain a credential then the entire Request object must be declared sensitive at every usage. Thirdly, if you pass additional parameters to a function/method there is no way to apply this attribute (https://3v4l.org/8lBso#v8.4.6). I'm sure there is still plenty of code out there which makes use of `func_get_args()` instead of variadic arguments. There are still libraries out there which insist on supporting ancient versions of PHP which don't support variadic arguments so there is no way to set these as Sensitive. Even massive and well-supported libraries like the AWS-SDK still use `func_get_args()` in places like their TokenProvider. While you're correct that it is backwards compatible (and I agree that this is a fantastic feature of attributes), it is not widely used. Taking a look at some of the most popular libraries around, many don't have a single instance of it when arguably they will be passing around lots of sensitive information, for example: - aws-sdk-php only uses the SensitiveParameter in unit tests but it accepts credentials in a variety of places - Monolog has no uses of it, but is almost certainly going to be passed around PII, Tokens, Session IDs, etc. - Guzzle has no uses of it, but again it will almost certainly be passed credentials, tokens, etc. (e.g. for authentication) These are all widely used, well-maintained projects, and yet offer none of the apparent protection from the attribute. While the attribute is backwards compatible, the process of identifying appropriate usage and contributing patches can carry a non-trivial cost to identify possible uses, create a patch, and go through any pull request review processes. Different groups have different views on what should be considered Sensitive, so it is also likely to get caught up in any reviews for discussion.

Andrew Lyons

1 year ago
On Thu, 10 Apr 2025 at 23:20, Tim Düsterhus <tim@bastelstu.be> wrote:
> As I had said on GitHub before, but to put it onto the list for > visibility: > > I'd rather see the value in `php.ini-production` being changed to `Off` > to match the built-in default. see > https://github.com/php/php-src/pull/18215#issuecomment-2768618516
Thanks Tim, Can you please explain why you think the default should be to always show arguments? I asked this question in the Pull Request too and didn't really get a clear answer. I did try to address your concerns in the RFC itself. To summarise: * you referenced a stackoverflow chat asking about the difference, and noting that the defaults for production and development should probably be standardised as much as possible; * you noted that the correct solution would be to set `display_errors` to Off; and * you also noted that the framework's error handler should be properly configured. In response to these I have extended the RFC to cover making the development INI file the same as the production INI and default value by setting all of these to the 'On' value. Regarding setting `display_errors` to Off, I do agree, but I feel that this is a separate RFC. I've highlighted this as future scope in the RFC and I've also noted that there is often still value in displaying errors without the arguments. That is to say that I feel that display_errors should default to Off, and exception_ignore_args should default to On. Having defaults which do not reveal arguments unless explicitly configured to do so is a much safer option than just showing everything. I do agree that the framework's error handler should be properly configured, but mistakes happen and it is better to fail in as safe a way as possible. The reality is that a framework that is configuring the error handling properly is also capable of calling `ini_set('zend.exception_ignore_args', 0);` during its own initialisation and being explicit about wanting to have that information. Developers are also able to configure their PHP environment with developer appropriate configuration. Ultimately mistakes can, and do, happen. PHP should be configured with safe defaults as standard.

Rowan Tommins [IMSoP]

1 year ago
On 09/04/2025 03:00, Andrew Lyons wrote:
> Hi everyone, > > I've been working on a new RFC which proposes changing the default > value for the zend.exception_ignore_args INI setting from Off to On. > > The intent of this change is to make PHP installations safer by > default and prevent the accidental release of sensitive information in > stack traces. > > * RFC: https://wiki.php.net/rfc/exception_ignore_args_default_value > * Implementation: https://github.com/php/php-src/pull/18215
This discussion seems to have overlooked that the setting doesn't just restrict the *display* of arguments, it restricts the *collection* of those arguments into the Exception object, which has visible effects on the behaviour and performance of the program. Because of PHP's reference counting memory model, programmers can usually rely on memory being freed and destructors being called when a local variable goes out of scope. Without zend.exception_ignore_args=1, the lifetime of any zval which happened to be involved in a parameter anywhere on the stack, is extended to last until the Exception object is destructed. That can mean holding onto large amounts of memory, holding open file handles and network connections, or firing "RAII" destructors in an unexpected order. Note that this is tied to the lifetime of the exception object, not when it is thrown and caught; and it is recursive - an array containing an object with a property pointing to another object keeps all those arrays and objects alive, just in case you want to inspect them in the error trace. Another edge case I encountered a few years ago is when I wrote some code that serialized an exception (to propagate it from a worker process to its parent): the exception itself was serializable, but a completely unrelated change caused an unserialized object to show up as a parameter somewhere on the call stack, causing the whole exception to become unserializable. On the other side, PHP is not a Functional Programming language, so knowing the arguments that were passed into a function is rarely enough to reconstruct the state that led to the exception. Manual backtraces can also collect a copy of $this for method calls, with the DEBUG_BACKTRACE_PROVIDE_OBJECT option, but the exception constructor never passes that. Nor does it collect a snapshot of static and global variables, or the state of opaque objects and resources like file/stream handles. Collecting arguments seems like a special case which could be handled by debug or APM extensions, rather than something that most users will ever need.
-- Rowan Tommins [IMSoP]

Kamil Tekiela

1 year ago
>This discussion seems to have overlooked that the setting doesn't just
restrict the *display* of arguments, it restricts the *collection* of those arguments into the Exception object, which has visible effects on the behaviour and performance of the program. Oh, I didn't know that. I assumed the arguments wouldn't be freed until the exception is destructed with it either on or off. In that case, please ignore everything I said previously.

Rowan Tommins [IMSoP]

1 year ago
On 13/04/2025 15:07, Kamil Tekiela wrote:
>> This discussion seems to have overlooked that the setting doesn't just >> restrict the*display* of arguments, it restricts the*collection* of >> those arguments into the Exception object, which has visible effects on >> the behaviour and performance of the program. > Oh, I didn't know that. I assumed the arguments wouldn't be freed > until the exception is destructed with it either on or off. In that > case, please ignore everything I said previously.
For reference, this is the code in question: https://github.com/php/php-src/blob/1684c52a88d1429cf19d4927fe2edf7ff37c66be/Zend/zend_exceptions.c#L257 When the exception is created, it calls the internal implementation of debug_backtrace, passing either DEBUG_BACKTRACE_IGNORE_ARGS or 0 (which is why it never captures $this). The result is then stored in the private $trace property, to be used by whatever wants it later. zend.exception_string_param_max_len, on the other hand, is used only in zend_trace_to_string - i.e. when converting the stored trace for display, or on call to the getTraceAsString() method. It has no effect if zend.exception_ignore_args was set to 1 when the exception was created, because the 'args' key won't be there to display. It might be useful to have a "middle way" for both debug_backtrace() and exception traces which directly summarised the arguments in some way, and stored only that summary, thus limiting the performance and side-effects of capturing the original values. This wouldn't solve the sensitive information problem, though, so I'm not sure how good an idea it would be.
-- Rowan Tommins [IMSoP]