Make WordPress Core

Ticket #50510: secure-wp-nonces.php

File secure-wp-nonces.php, 3.2 KB (added by chaoix, 6 years ago)

Secure WP Nonces mu-plugin

Line 
1<?php
2/*
3Plugin Name: Secure WP Nonces
4Version: 1.0.0
5Author: Matthew Sigley
6Description: Reimplements the wp_create_nonce and wp_verify_nonce functions in a more secure way
7*/
8
9// Only override functions if none of them exist
10if( !function_exists( 'wp_create_nonce' ) && !function_exists( 'wp_verify_nonce' ) && !function_exists( 'wp_create_nonce_hash' ) ) {
11        function wp_create_nonce( $action = -1 ) {
12                $user = wp_get_current_user();
13                $uid  = (int) $user->ID;
14                if ( ! $uid ) {
15                        /** This filter is documented in wp-includes/pluggable.php */
16                        $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
17                }
18
19                $token = '';
20                if( function_exists( 'wp_get_session_token' ) )
21                        $token = wp_get_session_token();
22                $i = wp_nonce_tick();
23
24                $browser_id = $_SERVER['HTTP_USER_AGENT'];
25                $browser_id = hash( 'crc32b', $_SERVER['HTTP_USER_AGENT'] );
26
27                return wp_create_nonce_hash( $i, $action . '|' . $uid . '|' . $token );
28        }
29
30        // Amazon's S3 service uses a similar method for signing URLs
31        // https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
32        function wp_create_nonce_hash( $nonce_tick, $action ) {
33                $hash_algo = 'sha256';
34                $actions = explode( '|', $action ); //This allows plugin authors to add signing steps by providing a nonce action delimited by pipes |.
35                $num_actions = count( $actions );
36                $salt = wp_salt( 'nonce' );
37                $nonce = hash_hmac( $hash_algo, $nonce_tick, "SWPN{$salt}", true ); // The salt prefix allows for existing hashes to be invalidated if a change is made to the algorithm.
38
39                for( $i = 1; $i < $num_actions; $i++ ) {
40                        $value = (string) $value;
41                        if( $value !== '' )
42                                $nonce = hash_hmac( $hash_algo, $actions[$i], $nonce, true );
43                }
44
45                $nonce = hash_hmac( 'sha256', $actions[0], $nonce );
46                return $nonce;
47        }
48
49        function wp_verify_nonce( $nonce, $action = -1 ) {
50                $nonce = (string) $nonce;
51                $user  = wp_get_current_user();
52                $uid   = (int) $user->ID;
53                if ( ! $uid ) {
54                        /**
55                         * Filters whether the user who generated the nonce is logged out.
56                         *
57                         * @since 3.5.0
58                         *
59                         * @param int    $uid    ID of the nonce-owning user.
60                         * @param string $action The nonce action.
61                         */
62                        $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
63                }
64
65                if ( empty( $nonce ) ) {
66                        return false;
67                }
68
69                $token = '';
70                if( function_exists( 'wp_get_session_token' ) )
71                        $token = wp_get_session_token();
72                $i = wp_nonce_tick();
73
74                // Nonce generated 0-12 hours ago
75                $expected = wp_create_nonce_hash( $i, $action . '|' . $uid . '|' . $token );
76                if ( hash_equals( $expected, $nonce ) ) {
77                        return 1;
78                }
79
80                // Nonce generated 12-24 hours ago
81                $expected = wp_create_nonce_hash( ( $i - 1 ), $action . '|' . $uid . '|' . $token );
82                if ( hash_equals( $expected, $nonce ) ) {
83                        return 2;
84                }
85
86                /**
87                 * Fires when nonce verification fails.
88                 *
89                 * @since 4.4.0
90                 *
91                 * @param string     $nonce  The invalid nonce.
92                 * @param string|int $action The nonce action.
93                 * @param WP_User    $user   The current user object.
94                 * @param string     $token  The user's session token.
95                 */
96                do_action( 'wp_verify_nonce_failed', $nonce, $action, $user, $token );
97
98                // Invalid nonce
99                return false;
100        }
101}