Completed
Push — try/block-editor-iframe ( da7098...901ea1 )
by
unknown
08:03
created

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

Complexity

Conditions 7
Paths 7

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 7
nop 2
dl 0
loc 31
rs 8.4906
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
		$_GET['frame-nonce'],  // phpcs:ignore WordPress.Security.NonceVerification
47
		'frame-' . Jetpack_Options::get_option( 'id' )
48
	);
49
50
	if ( $verified && ! defined( 'IFRAME_REQUEST' ) ) {
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 $action Should give context to what is taking place and be the same when nonce was created.
65
 * @return boolean Whether the nonce is valid.
66
 */
67
function jetpack_verify_frame_nonce( $nonce, $action ) {
68
	if ( empty( $nonce ) ) {
69
		return false;
70
	}
71
72
	list( $expiration, $user_id, $hash ) = explode( ':', $nonce, 3 );
73
74
	$token = Jetpack_Data::get_access_token( $user_id );
75
	if ( ! $token ) {
76
		return false;
77
	}
78
79
	add_filter( 'salt', 'jetpack_filter_salt', 10, 2 );
80
	$expected_hash = wp_hash( "$expiration|$action|{$token->external_user_id}", 'jetpack_frame_nonce' );
81
	remove_filter( 'salt', 'jetpack_filter_salt' );
82
83
	if ( ! hash_equals( $hash, $expected_hash ) ) {
84
		return false;
85
	}
86
87
	if ( time() > $expiration ) {
88
		wp_die( 'Expired nonce.' );
89
	}
90
91
	$current_user_id = get_current_user_id();
92
	if ( ! $current_user_id || $current_user_id !== (int) $user_id ) {
93
		wp_die( 'User ID mismatch.' );
94
	}
95
96
	return true;
97
}
98
99
/**
100
 * Filters the WordPress salt.
101
 *
102
 * @param string $salt   Salt for the given scheme.
103
 * @param string $scheme Authentication scheme.
104
 * @return string
105
 */
106
function jetpack_filter_salt( $salt, $scheme ) {
107
	if ( 'jetpack_frame_nonce' === $scheme ) {
108
		$token = Jetpack_Data::get_access_token( get_current_user_id() );
109
110
		if ( $token ) {
111
			$salt = $token->secret;
112
		}
113
	}
114
115
	return $salt;
116
}
117