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

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.

Best wishes,

Andrew

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: PHP: rfc:exception_ignore_args_default_value
* Implementation: Change default setting for zend.exception_ignore_args by andrewnicols · Pull Request #18215 · php/php-src · GitHub

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 Change default setting for zend.exception_ignore_args by andrewnicols · Pull Request #18215 · php/php-src · GitHub

Best regards
Tim Düsterhus

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: PHP: rfc:exception_ignore_args_default_value
* Implementation: Change default setting for zend.exception_ignore_args by andrewnicols · Pull Request #18215 · php/php-src · GitHub

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 Change default setting for zend.exception_ignore_args by andrewnicols · Pull Request #18215 · php/php-src · GitHub

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

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
Change default setting for zend.exception_ignore_args by andrewnicols · Pull Request #18215 · php/php-src · GitHub

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

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.

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
Change default setting for zend.exception_ignore_args by andrewnicols · Pull Request #18215 · php/php-src · GitHub

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.

> 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 (Personal Data Breach | European Data Protection Supervisor).

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 (Online PHP editor | output for 8lBso). 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.

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:

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:

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.

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.

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: PHP: rfc:exception_ignore_args_default_value
* Implementation: Change default setting for zend.exception_ignore_args by andrewnicols · Pull Request #18215 · php/php-src · GitHub

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]

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.

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: php-src/Zend/zend_exceptions.c at 1684c52a88d1429cf19d4927fe2edf7ff37c66be · php/php-src · GitHub

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]