Opened 5 weeks ago
Last modified 3 weeks ago
#65052 assigned defect (bug)
Nonce check order flaw in post-quickdraft-save
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | Awaiting Review | Priority: | normal |
| Severity: | normal | Version: | trunk |
| Component: | Security | Keywords: | has-patch needs-testing has-test-info has-unit-tests |
| Focuses: | Cc: |
Attachments (2)
Change History (10)
This ticket was mentioned in PR #11538 on WordPress/wordpress-develop by @rajeshcp.
5 weeks ago
#1
This ticket was mentioned in PR #11539 on WordPress/wordpress-develop by @rajeshcp.
5 weeks ago
#2
$_REQUESTpost_ID? is used to load a post object before the referer is actually checked on line 93. A
crafted request can cause a database lookup on an arbitrary post_ID before authorization. $_REQUEST_wpnonce? is
also accessed without checking key existence.
Trac ticket: https://core.trac.wordpress.org/ticket/65052
Fixes #65052
## Use of AI Tools
#3
@
3 weeks ago
- Severity changed from major to normal
A crafted request can cause a database lookup on an arbitrary post_ID before authorization.
This is not a security issue. Enumerating post IDs can be done with the REST API.
If it were a security issue, especially a major one, it should not be reported in Trac but rather in the WordPress HackerOne.
#4
@
3 weeks ago
See also: Reporting Security Vulnerabilities
#5
@
3 weeks ago
Test Report for Ticket #65052
Environment
OS: Windows (XAMPP)
PHP: 8.2.x
WordPress: 7.1-alpha (trunk)
Testing Area: Dashboard Quick Draft (wp-admin/post.php)
The Problem
In the original code, the resource (Post object) is fetched via get_post() before the Nonce is verified or the Post ID is validated. This leads to:
Unnecessary Database Lookups: Database is queried even if the request is unauthorized.
PHP Warnings: In PHP 8.2+, providing a non-existent post_ID leads to a Fatal-level Warning when attempting to access properties on a null object later in the execution flow.
Test Results (Before Patch)
Test 1 valid nonce & valid post id:
jQuery.post( ajaxurl.replace('admin-ajax.php', 'post.php'), {
action: 'post-quickdraft-save',
_wpnonce: 'valid_nonce_here', // valid nonce
post_ID: 1 // valid id
});
Test 1 result:
[23-Apr-2026 03:02:52 UTC] DEBUG: get_post() was called for ID: 1
Test 2 invalid nonce & valid post id:
jQuery.post( ajaxurl.replace('admin-ajax.php', 'post.php'), {
action: 'post-quickdraft-save',
_wpnonce: 'wrong_nonce_here', // wrong_nonce
post_ID: 1 // valid id
});
Test 2 result:
[23-Apr-2026 03:02:58 UTC] error_msg: Unable to submit this form, please refresh and try again.
Test 3 valid nonce & invalid post id:
jQuery.post( ajaxurl.replace('admin-ajax.php', 'post.php'), {
action: 'post-quickdraft-save',
_wpnonce: 'valid_nonce_here', // valid nonce
post_ID: 999999 // invalid id
});
Test 3 result:
[23-Apr-2026 03:03:53 UTC] DEBUG: get_post() was called for ID: 999999
[23-Apr-2026 03:03:53 UTC] PHP Warning: Attempt to read property "post_type" on null in C:\xampp\htdocs\wp-core\src\wp-admin\post.php on line 103
Suggested patch
<?php case 'post-quickdraft-save': // Check nonce and capabilities. $nonce = $_REQUEST['_wpnonce']; // patch-ticket: 65052-begin $post_id = absint( $_REQUEST['post_ID'] ?? 0 ); $post = $post_id ? get_post( $post_id ) : null; // patch-ticket: 65052-end $error_msg = false; // For output of the Quick Draft dashboard widget. require_once ABSPATH . 'wp-admin/includes/dashboard.php'; if ( ! $post || ! wp_verify_nonce( $nonce, 'add-post' ) ) { $error_msg = __( 'Unable to submit this form, please refresh and try again.' ); } if ( ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) ) { exit; } if ( $error_msg ) { //check ticket: 65052 Nonce check order flaw in post-quickdraft-save //error_log( "error_msg: " . ($error_msg ?? 'none') ); return wp_dashboard_quick_press( $error_msg ); } //check ticket: 65052 Nonce check order flaw in post-quickdraft-save //error_log( "DEBUG: get_post() was called for ID: " . ($_REQUEST['post_ID'] ?? 'none') ); check_admin_referer( 'add-' . $post->post_type );
Test Results (After Patch)
Test 3 valid nonce & invalid post id:
jQuery.post( ajaxurl.replace('admin-ajax.php', 'post.php'), {
action: 'post-quickdraft-save',
_wpnonce: 'valid_nonce_here', // valid nonce
post_ID: 999999 // invalid id
});
Test 3 result:
[23-Apr-2026 03:07:42 UTC] error_msg: Unable to submit this form, please refresh and try again.
Verdict
The optimized patch addresses the security flaw and improves system reliability for PHP 8.2+ environments.
Summary
Strict Validation. Ensuring that get_post() is handled safely by using absint() and a ternary check for $post.
Logic Ordering. Nonce and resource existence are verified immediately.
Stability. Preventing the attempt to read property on null warning in PHP 8.2, which was previously triggered by a valid nonce but an invalid ID.
This ticket was mentioned in PR #11637 on WordPress/wordpress-develop by @liaison.
3 weeks ago
#7
Description
This PR addresses a logic flaw in wp-admin/post.php during the post-quickdraft-save action. Previously, the code attempted to fetch the post object and require dashboard includes before verifying the nonce or checking user capabilities.
Changes included in this PR:
Reordered Logic: Nonce and parameter validation now occur at the beginning of the switch case, ensuring unauthorized or malformed requests are rejected before any heavy processing (like get_post()) occurs.
PHP 8.2+ Compatibility: Implemented null coalescing operators (??) when accessing $_REQUEST parameters to prevent Undefined array key notices.
Unit Tests: Added PHPUnit test cases in Tests_Admin_Post_QuickDraftSave to verify that invalid nonces or missing parameters are handled correctly without triggering PHP warnings.
Trac ticket: https://core.trac.wordpress.org/ticket/65052
Use of AI Tools
AI assistance: Yes
Tool(s): Gemini
Model(s): Gemini 3 Flash
Used for: Refining the PHPUnit test structure and drafting the technical description for the Pull Request. The final code logic, security implications, and test assertions were reviewed and verified by me to ensure compliance with WordPress Coding Standards.
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

$_REQUESTpost_ID? is used to load a post object before the referer is actually checked on line 93. A
Trac ticket: https://core.trac.wordpress.org/ticket/65052
Fixes #65052
## Use of AI Tools