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
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.
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
Hey,
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.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
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-2768618516Full 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.
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.
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.
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.
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 toOff
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.
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.
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.
This discussion seems to have overlooked that the setting doesn't just
restrict thedisplay of arguments, it restricts thecollection 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]