-
Notifications
You must be signed in to change notification settings - Fork 7.9k
RFC: Redacting parameters in back traces #7921
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@TimWolla, RFC karma has been granted. Best of luck with the RFC! :) |
This comment was marked as resolved.
This comment was marked as resolved.
21f93bb
to
44a0173
Compare
ext/pdo/pdo_dbh.c
Outdated
2, | ||
zend_ce_sensitive_parameter->name, | ||
0 | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if this could be done through ArgInfo and the stubs at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, this would be nice. For now I've dropped adding the attribute to existing functions from this PR. This nicely separates the implementation of the behavior from adding the attribute to functions, making this easier to review.
Found while implementing php#7921.
Found while implementing php#7921: > ==56540== 272 (56 direct, 216 indirect) bytes in 1 blocks are definitely lost in loss record 8 of 8 > ==56540== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) > ==56540== by 0x6D2C62: __zend_malloc (zend_alloc.c:3055) > ==56540== by 0x73E874: zend_add_attribute (zend_attributes.c:282) > ==56540== by 0x65BE40: zend_add_parameter_attribute (zend_attributes.h:104) > ==56540== by 0x65C760: zm_startup_password (password.c:452) > ==56540== by 0x592AC7: zm_startup_basic (basic_functions.c:364) > ==56540== by 0x71B18D: zend_startup_module_ex (zend_API.c:2202) > ==56540== by 0x71B1EC: zend_startup_module_zval (zend_API.c:2217) > ==56540== by 0x72CF48: zend_hash_apply (zend_hash.c:2011) > ==56540== by 0x71B8F0: zend_startup_modules (zend_API.c:2328) > ==56540== by 0x66CE1B: php_module_startup (main.c:2256) > ==56540== by 0x887FC8: php_cli_startup (php_cli.c:409)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
implementation seems correct at a glance, just minor nits
f16ef80
to
4dcb8d2
Compare
@TysonAndre Thank you for the review. I've implemented the suggested changes. Unfortunately the implementation is not yet correct and I could need some handholding here: Apparently I'm mishandling the reference counting when storing the original value within
Will result in:
I believe this is about the call to |
b60fa9e
to
ab8e416
Compare
The RFC was accepted and I've just cleaned up this PR for review. I've added a list of open issues / discussion points to the initial message of this PR:
|
Zend/tests/function_arguments/sensitive_parameter_value_unserialize.phpt
Outdated
Show resolved
Hide resolved
Zend/tests/function_arguments/sensitive_parameter_correctly_captures_original.phpt
Outdated
Show resolved
Hide resolved
8c3a450
to
16a7822
Compare
As all the open discussion points with regard to the implementation should be resolved now, I've squashed all my fixup commits to clean up the history. I've opted to keep the serialization commit separate, because that one is the change that differed from the original proposal. The only open issue I'm aware of, is that #8070 is not yet reviewed/merged. So, please take another look at the diff to make sure I didn't miss anything important! |
This comment was marked as resolved.
This comment was marked as resolved.
16a7822
to
3e13cd2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, code seems correct but everything else using zend_object std
is a struct with 2 or more properties.
Although it may make sense to leave as is and put the zval hidden;
in the struct in a followup PR to hide it completely (and override get_gc, etc. like ext/spl/spl_fixedarray.c), so no need to change
Zend/zend_attributes.c
Outdated
zend_ce_sensitive_parameter_value->create_object = attributes_sensitive_parameter_value_new; | ||
memcpy(&attributes_object_handlers_sensitive_parameter_value, &std_object_handlers, sizeof(zend_object_handlers)); | ||
attributes_object_handlers_sensitive_parameter_value.offset = XtOffsetOf(attributes_sensitive_parameter_value, std); | ||
attributes_object_handlers_sensitive_parameter_value.free_obj = attributes_sensitive_parameter_value_free_obj; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This works correctly, but doesn't need to be overridden with the current design and may confuse people about when to use XtOffsetOf to override the default .offset
of 0.
It calls zend_object_std_dtor
with the exact same address casted (offset=0), and zend_object_std_dtor is the default.
It'd be needed if you weren't using readonly properties but were putting things before std instead (e.g. ext/spl/spl_fixedarray.c)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As of PHP 8.0.0, new code should better use offset
of XtOffsetOf
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#ifndef XtOffsetOf
# define XtOffsetOf(s_type, field) offsetof(s_type, field)
#endif
I assume you mean "use offsetof
instead of XtOffsetOf
". Good to know that it's unconditionally defined one way
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, right, "offsetof instead of XtOffsetOf".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me give the reasons behind why I did it, like I did it:
- I've used a regular readonly property in the stub so that SensitiveParameterValue behaves like a user-land object as much as possible. It should protect against accidental exposure of the sensitive value, but if one explicitly uses Reflection to access the internal value (for whatever reason!) then it should work as if the object was implemented in userland (e.g. in a Polyfill).
As an example, this should be possible:
<?php
$foo = new SensitiveParameterValue('foo');
$r = new ReflectionClass($foo);
$p = $r->getProperty('value');
var_dump($p->getValue($foo));
- Protecting against the
(array)
cast is only possible for native implementations and for that I need to overrideget_properties_for
. While I don't need to store any additional internal values, I've added theattributes_sensitive_parameter_value
struct with only thestd
member for consistency with other natively implemented classes, so that the SensitiveParameterValue class is no special case. Likewise I've calculated the offsets, even if I know they are always0
, so that adding additional struct members works properly without needing to adjust multiple locations.
If you think that I should simple use zend_object
instead of attributes_sensitive_parameter_value
then I can adjust the code, no problem. Just wanted to give my reasons.
Regarding offset
vs XtOffsetOf
: Do you have an example for that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's a deliberate design decision to expose properties with reflection, then that's fine, never mind.
If you think that I should simple use
zend_object
instead ofattributes_sensitive_parameter_value
then I can adjust the code, no problem. Just wanted to give my reasons.
I'd personally consider it worth it, there's other parts of the code using zend_object directly when there's no extra properties, the other examples you looked at probably had at least one other internal property. (e.g. __PHP_Incomplete_Class
doesn't use std and has no hidden properties)
The code in php-src is used as an example by PECL authors, so the extra indirection may lead to copying an unnecessary abstraction for internal attributes on parameters to other places without understanding why
/* {{{ php_create_incomplete_class */
static zend_object *php_create_incomplete_object(zend_class_entry *class_type)
{
zend_object *object;
object = zend_objects_new( class_type);
object->handlers = &php_incomplete_object_handlers;
object_properties_init(object, class_type);
return object;
}
PHPAPI void php_register_incomplete_class_handlers(void)
{
memcpy(&php_incomplete_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
php_incomplete_object_handlers.read_property = incomplete_class_get_property;
php_incomplete_object_handlers.has_property = incomplete_class_has_property;
php_incomplete_object_handlers.unset_property = incomplete_class_unset_property;
php_incomplete_object_handlers.write_property = incomplete_class_write_property;
php_incomplete_object_handlers.get_property_ptr_ptr = incomplete_class_get_property_ptr_ptr;
php_incomplete_object_handlers.get_method = incomplete_class_get_method;
php_ce_incomplete_class->create_object = php_create_incomplete_object;
}
/* }}} */
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@TysonAndre Okay, if there's precedent then it makes sense to simplify this. I've made the change in 2fd9a60.
I've also added a test for reflection in 7e94af7.
If it's looking good to you I'll squash those commits into the initial commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I now went ahead and rebased it, as the commit links remain valid.
d4525ec
to
08ac4af
Compare
May I suggest putting this feature behind the ini configuration directive (defaulting to redact sensitive variables in production.ini, and show the wrapped sensitive values in development). I find it helpful to see the config parameters (especially in docker/k8s when configuring via env/configs/secrets) |
@kaznovac That said: As the RFC author I very intentionally did not propose any php.ini knobs for this, because PHP should not change its behavior based on some global configuration. To the best of my knowledge this opinion is shared by the majority of internals participants / active voters. |
@TimWolla I think in this instance "PHP should not change its behavior based on some global configuration" is already sort defeated by the variety of error formatting options we already have. (which is a good thing. It's an error display configuration, which very much fits in INI. INIs which we do not want is anything which alters the data/control flow.) Any ini should however only affect the step of translating backtrace to string and not affect debug_backtrace() output (i.e. only debug_print_backtrace(), Exception traces etc.). |
@bwoebi Personally I disagree with an ini setting even if it's just for the default pretty printer for stack traces. I think that it makes it too easy to accidentally break the "safe by default" behavior of the RFC as proposed. I also must admit that at this point it's hard to me to differentiate between actual review comments (“change requests”) and comments that just want to share some opinion on some matter (where I don't need to or don't should act on, unless agreed on with a separate vote). Can we get this PR with the behavior as-agreed-on in the RFC (+ the serialization erratum) across the finish line first? Having an INI setting can then be discussed in a new RFC that builds upon this one. |
Zend/tests/function_arguments/sensitive_parameter_correctly_captures_original.phpt
Outdated
Show resolved
Hide resolved
493810f
to
405914a
Compare
405914a
to
846a52d
Compare
846a52d
to
582a2a9
Compare
Merged as 9085197 |
I got a Question why SensitiveValue is only ever used on Parameters and not for example Properties? like if i got the most generic User Object class User
{
protected $password;
} and another function that takes a user object: public function doSomethingWithUser(User $user)
{
$this->doSomethingGeneric($user);
}
// this one can't make $object as Sensitive because it might not know that it can be?
public function doSomethingGeneric(object $object)
{
// there might be Exception
} and when some Exception happen inside this other function, the user password could be exposed in the Backtrace. Wouldn't it be better in such case to mark the Property for password as Sensitive so it isn't shown in the Backtrace while the User Object is? |
RFC: https://wiki.php.net/rfc/redact_parameters_in_back_traces
Open Issues:
SensitiveParameterValue::__unserialize()
: https://externals.io/message/117022