[wp-trac] [WordPress Trac] #23165: Admin validation errors on form nonce element IDs (_wpnonce)

WordPress Trac noreply at wordpress.org
Thu Jan 10 02:25:18 UTC 2013


#23165: Admin validation errors on form nonce element IDs (_wpnonce)
-----------------------------------+--------------------------
 Reporter:  bpetty                 |       Type:  defect (bug)
   Status:  new                    |   Priority:  normal
Milestone:  Awaiting Review        |  Component:  Validation
  Version:                         |   Severity:  normal
 Keywords:  needs-codex has-patch  |
-----------------------------------+--------------------------
 The `wp_nonce_field()` method has a design flaw in that the `name`
 parameter is optional, but its default value ("_wpnonce") is used for the
 field element `id`. This wouldn't be bad if only one form is used on each
 page, but that is rarely the case in the admin panel. Currently, this only
 results in harmless HTML validation errors in admin, and in very rare
 situations (depending on plugins installed), can result in javascript
 scope-related issues breaking forms. This is an issue that keeps being
 reported and fixed several times (see #5962, #6564, r14737 from #13383,
 and the latest [http://core.trac.wordpress.org/ticket/22712#comment:11
 #22712]) by simply specifying the name in all locations that don't at the
 time in core. There's at least three different approaches that I can see
 where we could prevent this from popping back up in the future and
 encourage better coding habits.

 == A: Require the Name Parameter ==

 The most obvious approach is to simply adds a `_doing_it_wrong()` call
 (though it could just be `_deprecated_argument()` instead) for any calls
 to `wp_nonce_field()` that don't specify the `name` parameter, forcing
 everyone to give nonce fields unique IDs (and names). A side-effect of
 this change is that it will also warn developers they are doing it wrong
 if they use `wp_nonce_field()` without the (also currently optional)
 `action` parameter, which is actually a good thing since the docs already
 warn that this is a security hole.

 There are 46 locations in core that would need updated with this change
 that make this mistake, and the fix for each of them with this change is
 exactly the same as what every other commit to fix this problem has always
 done: just specify the `name` to give a unique ID.

 The downside to this approach is that this pushes for an immediate change
 not only to all calls to `wp_nonce_field()`, but also their corresponding
 `check_admin_referer()` calls (to update the changed field name) in a
 single WP release, and considering how frequently it's called this way in
 core, I don't even have to look at plugins to see how often it happens
 there (it's going to be too many to count).

 This approach still results in an explicitly named ID that really doesn't
 need to be on the field in the first place in most locations.

 I gave this approach a shot anyway, and started on a patch just to see how
 much work it would be at least in core, but I started to find a few
 locations that could benefit from unique IDs, but similar field names. I
 also found myself already spending hours updating and testing just 9 of
 the 46 locations (unit tests don't cover this stuff) where I was forced to
 make changes to IDs that would be best left alone for now. I have attached
 this unfinished patch anyway if anyone is curious to see how it works,
 what it does, and to see what kinds of changes it ends up requiring.

 It's still possible to just pass "_wpnonce" as the `name` with this
 change, so the rest of the locations could simply use that to
 progressively work towards unique IDs, but I just don't see this as a good
 solution overall, and it seems like a lot of work for very little gain on
 a minor issue.

 == B: Use Action for ID or Remove ID Entirely ==

 Another option might be to remove ID entirely, or use (and require) the
 action parameter as the ID. This would improve results with unique IDs
 fixing most locations where `wp_nonce_field()` is used without requiring a
 change to each one individually for most uses (except for nonces used with
 ajax calls). All locations in core at least use `action` already, so the
 case for using `action` as ID instead seems better than using `name`.

 Unfortunately, either removing or using the action parameter for ID
 (changing the ID) would obviously break a few plugins that made the
 mistake of relying on the default `_wpnonce` (or the current `name` value)
 value for `id` from within JavaScript code for existing form endpoints. In
 the WP.org plugin repo, this appears to happen in the following plugins
 (searching up through plugins starting with 'r') for forms using the
 default value:

 {{{
 automatic-youtube-video-posts
 buddypress
 buddypress-backwards-compatibility
 canalblog-importer
 community-submitted-news
 eyes-only-plus
 facepress-ii
 globalfeed
 lazyest-gallery
 lazyest-maps
 maven-member
 pagely-reseller-management
 pluginbuddy-yourls
 post-event2
 qtranslate-separate-comments
 repostus
 ...
 }}}

 There's likely way more plugins that post to core forms using a non-
 default `name` that would break though as well, and even though I think ID
 should have never been added to the nonce field attributes and was never
 necessary in the first place, compatibility rules would need to be broken
 if this approach was used. This of course means this option is most likely
 not really an option at all.

 == C: Use an Option Array Parameter in Place of Name ==

 To keep this concise, this last approach basically involves changing
 `wp_nonce_field()` to:

 {{{
 function wp_nonce_field( $options = array() ) { ... }
 }}}

 The `options` parameter is a standard arguments array used with
 `wp_parse_args()`. This accomplishes not just one, but two different
 goals. First, if `$options` is not an array or the method was called with
 anything other than 1 parameter, we know this is an older call that needs
 to use the `name` parameter for both `name` and `id`, using
 `func_get_args()` for the original arguments, and can do so for full 100%
 backwards compatibility. Second, unrelated to this issue, the function has
 been refactored to adhere to coding standards in regards to
 [http://codex.wordpress.org/WordPress_Coding_Standards#Self-
 Explanatory_Flag_Values_for_Function_Arguments self-explanatory flag
 values] as far as the `referrer` and `echo` options are concerned.

 The `options` array can then be made to accept the following settings:

 - action: The same unique name included in the nonce hash as is the case
 now.
 - name: The field name (and *only* used for the name attribute). (Default:
 "_wpnonce")
 - id: The field ID if this really is still desired, and can even be
 different than the name, but by default, the field won't have an ID at
 all.
 - referrer: Whether to set the referer field for validation. (Default:
 true)
 - echo: Whether to display or return hidden form field. (Default: true)

 The only downside here is that each `wp_nonce_field()` call will be more
 verbose in most cases, especially if they do need to use an ID. For
 example, this:

 {{{
 wp_nonce_field('add-tag', '_wpnonce_add-tag');
 }}}

 Will now become this:

 {{{
 wp_nonce_field( array( 'action' => 'add-tag', 'name' => '_wpnonce_add-tag'
 ) );
 }}}

 I see plenty of benefits to this last solution though, and it requires
 very few changes up front, so I think it comes down to this approach
 ultimately. Please see attached patch.

-- 
Ticket URL: <http://core.trac.wordpress.org/ticket/23165>
WordPress Trac <http://core.trac.wordpress.org/>
WordPress blogging software


More information about the wp-trac mailing list