WordPress-Coding-Standards: Don't require sanitization with wp_verify_nonce()
Don’t require sanitization (wp_unslash, sanitize_text_field) with wp_verify_nonce().
cc @mjangda
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 6
- Comments: 32 (9 by maintainers)
This has come up again for us at VIPCS.
Use case:
Currently that throws a:
Let’s look at the definition of
wp_verify_nonce():Taken with a consideration that
$noncemay contain a value that could be considered unsafe, we can see that:false.hash_equals()), and may return1. At worse§, our unsafe string fails to be equal to the generated hash - no harm done.hash_equals()). At worse§, the unsafe string again fails to equal the generated hash.$nonceis passed as the second arg ofdo_action().§ For 3. and 4., the only way for the nonce to be verified when it shouldn’t would be for the the nonce generation to have been plugged such that the generated nonce was also an unsafe string. This seems unlikely and would imply bigger problems anyway.
So, we don’t store the first arg
wp_verify_nonce()in the database, nor do we output it. In fact, it’s only used for a comparison, and we already have code that doesn’t trigger this violation for simple comparisons - it just doesn’t include a comparison usinghash_equals().All told, I think I’m happy that a sanitization wrapper is not needed for the first arg of
wp_verify_nonce().I think the balance would be that the majority of installs don’t plug this function.
Instead of looking at
wp_verify_nonce()calls, can we look for instances of that function being plugged, and add a Warning there that the first arg should only be used in comparisons, ideally withhash_equals()?@sybrew WordPress slashes all user input. Failing to unslash will be result in an incorrect value for custom implementations containing characters that get slashed.
Having considered this, I think user input passed to
wp_verify_nonce()should requirewp_unslash()but is probably unique in that it should not be sanitized.Consider the situation in which the valid value is
correctand the user input is!correct. As the user input is not correct, the nonce verification should fail.Sanitizing the user input with
sanitize_key()will result in the incorrect value being converted to the correct value, allowing the nonce check to pass:Recommending
sanitize_keyalso fails to account for systems in which the nonce function has been plugged and replaced with a more complex value than WordPress uses, for example a system that is case sensitive or uses symbols:In the case of WordPress’s default functions, the user value is passed directly to
hash_equals(). In the event a replacement system has been written that uses a proper nonce (rather than a time based cross site protection token, like WordPress) and requires checking a value in the database, then the developer of that system should be responsible for preparing the SQL statement.It’s easy enough to ignore or add
sanitize_key(), as is the suggested resolution. But would like some more info in the error message specific towp_verify_nonce()instead of searching for an answer because it’s not really documented except in tickets (AFAIK).+1 for either removing or making it more informative to suggest using
sanitize_key()Personally I think the warning can be ignored - as @GaryJones explained, the
wp_verify_nonce()function already does sufficient sanitation.However, also @JDGrimes gave a perfect solution for this: Use
sanitize_key()to sanitize/escape the value.It’s true, that
sanitize_key()will convert the result to lowercase. That’s perfectly fine, becausewp_generate_nonce()creates a nonce usingwp_hash(), which returns ahash_hmac()value.So, using the
sanitize_key()function AND thewp_generate_nonce()function both return lowercase strings and can be used together.Also, in another issue (raised by a WP Core member) the case was already discussed and closed without any change: https://github.com/WordPress/WordPress-Coding-Standards/issues/414
TL;DR;
Use
sanitize_key()to “fix” that warning. It’s safe. It’s the official recommendation.https://vip.wordpress.com/documentation/vip-go/code-review-blockers-warnings-notices/#validation-sanitization-and-escaping is likely the link you’re looking for (with further links).
I don’t think we should impose this rule on thousands of WordPress developers, of which most aren’t using these rules, for the one developer that wishes to do something arbitrary with the
$noncevalue whilst assuming it is safe.Implying that we should all follow these rules so that the one developer can assume this is secure is a wrong design pattern. It would invoke security issues that should instead be handled at the moment it’s critically used, like during storage, evaluation, and output.
With this in mind, satirically, why aren’t we forced escape each and every string when we use it?
Wrong:
Correct:
The standard (reasonable) rules should already be applicable when creating functions. We shouldn’t ever trust input data unless it’s from a controlled source. Pluggable functions aren’t such a source, they’re arbitrary, and any other developer can call them.
A great example in this is
get_avatar(), which clearly shows it doesn’t trust its input ($args): https://core.trac.wordpress.org/browser/tags/5.1.1/src/wp-includes/pluggable.php#L2631More pluggable examples can be found in methods such as
wp_redirect(),cache_users(),get_userdata(), which all assume that their input isn’t secure down the line.As for
sanitize_key(): This runsstrtolower(), and thus can cause issues with the pluggable implementation of the code, where the creator of the$noncevalue expects capital characters.To conclude, the creator of the pluggable functions should be held responsible for handling the validity, sanity, and security of the raw inputs. Every twist we add to this chain will ripple as issues down the line.
Why, specifically? It is a pluggable function, we have no idea what it is going to do with the value you pass it. So I think it is safer to sanitize it first.
As a side note, you can use
sanitize_key()and avoid thewp_unslash()call.