Completed
Push — try/block-editor-iframe ( 3d6b0b...27531a )
by
unknown
11:03 queued 04:29
created

wordpress-com-block-editor-iframe.php ➔ jetpack_verify_frame_nonce()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 8
nop 2
dl 0
loc 43
rs 8.6097
c 0
b 0
f 0
1
<?php
2
/**
3
 * WordPress.com Block Editor Iframe
4
 * Allow new block editor posts to be composed on WordPress.com.
5
 * This is auto-loaded as of Jetpack v7.4 for sites connected to WordPress.com only.
6
 *
7
 * @package Jetpack
8
 */
9
10
/**
11
 * Prevents frame options header from firing if this is a whitelisted iframe request.
12
 */
13
function jetpack_disable_send_frame_options_header() {
14
	if ( jetpack_framing_allowed() ) {
15
		remove_action( 'admin_init', 'send_frame_options_header' );
16
	}
17
}
18
add_action( 'admin_init', 'jetpack_disable_send_frame_options_header', 9 );
19
20
/**
21
 * Adds custom admin body class if this is a whitelisted iframe request.
22
 *
23
 * @param string $classes Admin body classes.
24
 * @return string
25
 */
26
function jetpack_add_iframed_body_class( $classes ) {
27
	if ( jetpack_framing_allowed() ) {
28
		$classes .= ' is-iframed ';
29
	}
30
31
	return $classes;
32
}
33
add_filter( 'admin_body_class', 'jetpack_add_iframed_body_class' );
34
35
/**
36
 * Checks whether this is a whitelisted iframe request.
37
 *
38
 * @return bool
39
 */
40
function jetpack_framing_allowed() {
41
	if ( empty( $_GET['frame-nonce'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
42
		return false;
43
	}
44
45
	$verified = jetpack_verify_frame_nonce(
46
		wp_slash( $_GET['frame-nonce'] ),  // phpcs:ignore WordPress.Security.NonceVerification
47
		'frame-' . Jetpack_Options::get_option( 'id' )
48
	);
49
50
	if ( $verified && ! defined( 'IFRAME_REQUEST' ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $verified of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
51
		define( 'IFRAME_REQUEST', true );
52
	}
53
54
	return (bool) $verified;
55
}
56
57
/**
58
 * Verify that correct nonce was used with time limit.
59
 *
60
 * The user is given an amount of time to use the token, so therefore, since the
61
 * UID and $action remain the same, the independent variable is the time.
62
 *
63
 * @param string     $nonce  Nonce that was used in the form to verify.
64
 * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
65
 * @return false|int False if the nonce is invalid, 1 if the nonce is valid and generated between
66
 *                   0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
67
 */
68
function jetpack_verify_frame_nonce( $nonce, $action = -1 ) {
69
	$nonce = (string) $nonce;
70
	if ( empty( $nonce ) ) {
71
		return false;
72
	}
73
74
	$user    = wp_get_current_user();
75
	$user_id = (int) get_user_meta( $user->ID, 'wpcom_user_id', true );
76
	if ( ! $user_id ) {
77
		$user_data = Jetpack::get_connected_user_data( $user->ID );
78
		if ( ! $user_data ) {
79
			return false;
80
		}
81
82
		$user_id = $user_data['ID'];
83
		update_user_meta( $user->ID, 'wpcom_user_id', $user_id );
84
	}
85
86
	$i = wp_nonce_tick();
87
	add_filter( 'salt', 'jetpack_filter_salt' );
88
89
	// Nonce generated 0-12 hours ago.
90
	$expected = substr( wp_hash( $i . $action . $user_id, 'nonce' ), -12, 10 );
91
	if ( hash_equals( $expected, $nonce ) ) {
92
		remove_filter( 'salt', 'jetpack_filter_salt' );
93
		return 1;
94
	}
95
96
	// Nonce generated 12-24 hours ago.
97
	$expected = substr( wp_hash( ( $i - 1 ) . $action . $user_id, 'nonce' ), -12, 10 );
98
	if ( hash_equals( $expected, $nonce ) ) {
99
		remove_filter( 'salt', 'jetpack_filter_salt' );
100
		return 2;
101
	}
102
103
	remove_filter( 'salt', 'jetpack_filter_salt' );
104
105
	/** This filter is documented in wp-includes/pluggable.php */
106
	do_action( 'wp_verify_nonce_failed', $nonce, $action, $user, '' );
107
108
	// Invalid nonce.
109
	return false;
110
}
111
112
/**
113
 * Filters `wp_salt()` to use the user token secret.
114
 *
115
 * @return string
116
 */
117
function jetpack_filter_salt() {
118
	$token = Jetpack_Data::get_access_token( get_current_user_id() );
119
	return $token->secret;
120
}
121