#59234 closed enhancement (wontfix)
Introduce a `wp_json_decode()` function, including validation when available
| Reported by: | jrf | Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | |
| Component: | General | Version: | 6.4 |
| Severity: | normal | Keywords: | php83 dev-feedback has-patch |
| Cc: | Focuses: |
Description
From: https://core.trac.wordpress.org/ticket/59231:
New json_validate() function
This function is a high-performance way to validate json prior to decoding it. This function cannot be polyfilled without a performance hit.
However, due to the potential for using json for Denial-of-Service attack vectors (via a HUGE file/stream), I would strongly recommend for WP Core to start using this new function in all appropriate places wrapped within an
if ( function_exists() ) {}.
The
json_decode()function is used 44 times withinsrc(excluding external dependencies).
We may want to consider introducing a
wp_json_decode()function to ensure the use ofjson_validate()(when available).
This would then mirror the already existing `wp_json_encode()` function.
Attachments (1)
Change History (12)
#2
in reply to: ↑ 1
;
follow-up:
↓ 4
@
3 years ago
@ayeshrajans
Replying to ayeshrajans:
Thanks for opening this ticket. I have a few thoughts on this:
- If we were to introduce a
wp_json_decodefunction (which I actually did, that lead me to these points), we will have to throw an exception to handle errors. This is becausefalseitself is a valid return value, now we are overstepping the general use case of "decode" to "validate and decode".
I imagine a WP_Error object could work as a "invalid" return value and be the only exception to what can be reliably decoded by the function ?
Having said that, I'd be happy for WP to start using Exceptions, though I suspect that needs a separate discussion.
Happy to have a think about the function name, maybe call it wp_json_validate_and_decode() ?
I do still think a function is better than having the duplicate code all over the place with a larger risk of people forgetting to validate.
- If we were to run PHP 8.3 native
json_validate_and_json_decodeinside the newwp_json_decode, wouldn't that be a performance degradation for valid data?
From what I read in the mailing list discussion and the RFC, the PHP native (C) implementation is lightning fast, so that should be unnoticeable.
- I think a more mild approach would be to polyfill
json_validatefunction? That way, we are giving the choice to the programmer if we are dealing with potentially invalid JSON. For PHP 8.3, we don't have a performance penalty, but at least now the choice is explicit?
There was a whole discussion about the (im)possibility of polyfilling this reliably and correctly without impacting performance on the mailinglist and I think some of it is also mentioned in the RFC.
The problem is not so much with small bits of json, but with the large files/streams and that is exactly the case we want to harden against. A polyfill will just not do in that case and have a heavy performance hit, while the native C implementation does not.
#3
@
3 years ago
- Milestone 6.4 → 6.5
There is no patch and no time before Beta 1 to make and test one, so, I am moving this ticket into the 6.5 milestone.
#4
in reply to: ↑ 2
@
3 years ago
Replying to jrf:
Happy to have a think about the function name, maybe call it
wp_json_validate_and_decode()?
wp_safe_json_decode is a good alternative. It'll imply that it validates, and decodes.
- If we were to run PHP 8.3 native
json_validate_and_json_decodeinside the newwp_json_decode, wouldn't that be a performance degradation for valid data?From what I read in the mailing list discussion and the RFC, the PHP native (C) implementation is lightning fast, so that should be unnoticeable.
- I think a more mild approach would be to polyfill
json_validatefunction? That way, we are giving the choice to the programmer if we are dealing with potentially invalid JSON. For PHP 8.3, we don't have a performance penalty, but at least now the choice is explicit?There was a whole discussion about the (im)possibility of polyfilling this reliably and correctly without impacting performance on the mailinglist and I think some of it is also mentioned in the RFC.
The problem is not so much with small bits of json, but with the large files/streams and that is exactly the case we want to harden against. A polyfill will just not do in that case and have a heavy performance hit, while the native C implementation does not.
How about this implementation:
<?php function wp_safe_json_decode($json, $assoc = false, $depth = 512, $options = 0) { // Perform validation if json_validate is available if (function_exists('json_validate') && json_validate( $json, $depth, $flags & JSON_INVALID_UTF8_IGNORE ) === FALSE ) { return new WP_Error('json_validation_error', 'JSON validation failed.'); } // Decode the JSON string $result = json_decode($json, $assoc, $depth, $options); if (json_last_error() !== JSON_ERROR_NONE) { // Return a WP_Error with the appropriate error message return new WP_Error('json_decoding_error', 'JSON decoding failed: ' . json_last_error_msg()); } return $result; }
#5
follow-up:
↓ 7
@
3 years ago
Just saw: The PHP docs for json_validate() discourage its use when json_encode() is used afterwards.
Caution
Calling json_validate() immediately before json_decode() will unnecessarily parse the string twice, as json_decode() implicitly performs validation during decoding.
json_validate() should therefore only be used if the decode JSON payload is not immediately used and knowing whether the string contains valid JSON is needed.
So, adding this before every json_encode() (via a wrapper function) is probably not needed, but could instead be done selectively in places where a JSON string is forwarded in unmodified form, or where that DOS attach vector exists (with user-supplied data, for example).
#6
@
3 years ago
I checked every use of json_decode() and I only saw one that requires protection from DOS attack vector: the REST API controller.
In WP_REST_Request class, parse_json_params() method it parses the body for every request to the REST API, so well exposed to attacks.
I suggest adding a json_validate() just before.
Submitted a patch with it.
#7
in reply to: ↑ 5
@
3 years ago
As mentioned in https://core.trac.wordpress.org/ticket/59234#comment:5 the PHP docs say that what this ticket suggests should not be done.
I think this ticket can be marked as closed, bc there isn't really a way forward for it, is there?
#8
@
3 years ago
- Keywords dev-feedback added
- Milestone 6.5 → Future Release
Changing the milestone for now but yeah it sounds like a wontfix candidate.
#9
@
18 months ago
- Milestone Future Release
- Resolution → wontfix
- Status new → closed
Closing this off as per the comments above.
Calling json_validate() immediately before json_decode() will unnecessarily parse the string twice, as json_decode() implicitly performs validation during decoding.
Perform_json_validate_before_json_decode_on_REST_API_request_data.patch proposes calling json_validate() when parsing JSON parameters, but this also immediately precedes a call to json_decode() making it redundant.
If there were a way to introduce a performant userland implementation of json_validate() this would make sense as an enhancement for sites running PHP 7, but there isn't.
If a particular scenario is introduced or identified where json_validate() would make sense then that can be addressed as and when.
Cheers!
This ticket was mentioned in PR #12945 on WordPress/wordpress-develop by @juanmaguitar.
5 weeks ago
#10
- Keywords has-patch added; needs-patch removed
Opened from the WordPress Contributor Toolkit.
@juanmaguitar commented on PR #12945:
5 weeks ago
#11
Test of contributor tooling (WordPress/experimental-wp-dev-env#167) — closing, no review needed.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)
Thanks for opening this ticket. I have a few thoughts on this:
wp_json_decodefunction (which I actually did, that lead me to these points), we will have to throw an exception to handle errors. This is becausefalseitself is a valid return value, now we are overstepping the general use case of "decode" to "validate and decode".json_validate_and_json_decodeinside the newwp_json_decode, wouldn't that be a performance degradation for valid data?json_validatefunction? That way, we are giving the choice to the programmer if we are dealing with potentially invalid JSON. For PHP 8.3, we don't have a performance penalty, but at least now the choice is explicit?