Completed
Push — master-stable ( 01e321...4e4f59 )
by
unknown
18:01 queued 08:37
created

class.jetpack-signature.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
defined( 'JETPACK_SIGNATURE__HTTP_PORT'  ) or define( 'JETPACK_SIGNATURE__HTTP_PORT' , 80  );
4
defined( 'JETPACK_SIGNATURE__HTTPS_PORT' ) or define( 'JETPACK_SIGNATURE__HTTPS_PORT', 443 );
5
defined( 'JETPACK__WPCOM_JSON_API_HOST' )  or define( 'JETPACK__WPCOM_JSON_API_HOST', 'public-api.wordpress.com' );
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
6
7
class Jetpack_Signature {
8
	public $token;
9
	public $secret;
10
11
	function __construct( $access_token, $time_diff = 0 ) {
12
		$secret = explode( '.', $access_token );
13
		if ( 2 != count( $secret ) )
14
			return;
15
16
		$this->token  = $secret[0];
17
		$this->secret = $secret[1];
18
		$this->time_diff = $time_diff;
19
	}
20
21
	function sign_current_request( $override = array() ) {
22
		if ( isset( $override['scheme'] ) ) {
23
			$scheme = $override['scheme'];
24
			if ( !in_array( $scheme, array( 'http', 'https' ) ) ) {
25
				return new Jetpack_Error( 'invalid_sheme', 'Invalid URL scheme' );
26
			}
27
		} else {
28
			if ( is_ssl() ) {
29
				$scheme = 'https';
30
			} else {
31
				$scheme = 'http';
32
			}
33
		}
34
35
		if ( is_ssl() ) {
36
			$port = JETPACK_SIGNATURE__HTTPS_PORT == $_SERVER['SERVER_PORT'] ? '' : $_SERVER['SERVER_PORT'];
37
		} else {
38
			$port = JETPACK_SIGNATURE__HTTP_PORT  == $_SERVER['SERVER_PORT'] ? '' : $_SERVER['SERVER_PORT'];
39
		}
40
41
		$url = "{$scheme}://{$_SERVER['HTTP_HOST']}:{$port}" . stripslashes( $_SERVER['REQUEST_URI'] );
42
43
		if ( array_key_exists( 'body', $override ) && !is_null( $override['body'] ) ) {
44
			$body = $override['body'];
45
		} else if ( 'POST' == strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
46
			$body = isset( $GLOBALS['HTTP_RAW_POST_DATA'] ) ? $GLOBALS['HTTP_RAW_POST_DATA'] : null;
47
		} else {
48
			$body = null;
49
		}
50
51
		$a = array();
52
		foreach ( array( 'token', 'timestamp', 'nonce', 'body-hash' ) as $parameter ) {
53
			if ( isset( $override[$parameter] ) ) {
54
				$a[$parameter] = $override[$parameter];
55
			} else {
56
				$a[$parameter] = isset( $_GET[$parameter] ) ? stripslashes( $_GET[$parameter] ) : '';
57
			}
58
		}
59
60
		$method = isset( $override['method'] ) ? $override['method'] : $_SERVER['REQUEST_METHOD'];
61
		return $this->sign_request( $a['token'], $a['timestamp'], $a['nonce'], $a['body-hash'], $method, $url, $body, true );
62
	}
63
64
	// body_hash v. body-hash is annoying.  Refactor to accept an array?
65
	function sign_request( $token = '', $timestamp = 0, $nonce = '', $body_hash = '', $method = '', $url = '', $body = null, $verify_body_hash = true ) {
66
		if ( !$this->secret ) {
67
			return new Jetpack_Error( 'invalid_secret', 'Invalid secret' );
68
		}
69
70
		if ( !$this->token ) {
71
			return new Jetpack_Error( 'invalid_token', 'Invalid token' );
72
		}
73
74
		list( $token ) = explode( '.', $token );
75
76
		if ( 0 !== strpos( $token, "$this->token:" ) ) {
77
			return new Jetpack_Error( 'token_mismatch', 'Incorrect token' );
78
		}
79
80
		$required_parameters = array( 'token', 'timestamp', 'nonce', 'method', 'url' );
81 View Code Duplication
		if ( !is_null( $body ) ) {
82
			$required_parameters[] = 'body_hash';
83
			if ( !is_string( $body ) ) {
84
				return new Jetpack_Error( 'invalid_body', 'Body is malformed.' );
85
			}
86
		}
87
88
		foreach ( $required_parameters as $required ) {
89 View Code Duplication
			if ( !is_scalar( $$required ) ) {
90
				return new Jetpack_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', str_replace( '_', '-', $required ) ) );
91
			}
92
93 View Code Duplication
			if ( !strlen( $$required ) ) {
94
				return new Jetpack_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is missing.', str_replace( '_', '-', $required ) ) );
95
			}
96
		}
97
98
		if ( is_null( $body ) ) {
99
			if ( $body_hash ) {
100
				return new Jetpack_Error( 'invalid_body_hash', 'The body hash does not match.' );
101
			}
102
		} else {
103
			if ( $verify_body_hash && jetpack_sha1_base64( $body ) !== $body_hash ) {
104
				return new Jetpack_Error( 'invalid_body_hash', 'The body hash does not match.' );
105
			}
106
		}
107
108
		$parsed = parse_url( $url );
109
		if ( !isset( $parsed['host'] ) ) {
110
			return new Jetpack_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'url' ) );
111
		}
112
113
		if ( $parsed['host'] === JETPACK__WPCOM_JSON_API_HOST ) {
114
			$parsed['host'] = 'public-api.wordpress.com';
115
		}
116
117
		if ( !empty( $parsed['port'] ) ) {
118
			$port = $parsed['port'];
119
		} else {
120
			if ( 'http' == $parsed['scheme'] ) {
121
				$port = 80;
122
			} else if ( 'https' == $parsed['scheme'] ) {
123
				$port = 443;
124
			} else {
125
				return new Jetpack_Error( 'unknown_scheme_port', "The scheme's port is unknown" );
126
			}
127
		}
128
129
		if ( !ctype_digit( "$timestamp" ) || 10 < strlen( $timestamp ) ) { // If Jetpack is around in 275 years, you can blame mdawaffe for the bug.
130
			return new Jetpack_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'timestamp' ) );
131
		}
132
133
		$local_time = $timestamp - $this->time_diff;
134
		if ( $local_time < time() - 600 || $local_time > time() + 300 ) {
135
			return new Jetpack_Error( 'invalid_signature', 'The timestamp is too old.' );
136
		}
137
138
		if ( 12 < strlen( $nonce ) || preg_match( '/[^a-zA-Z0-9]/', $nonce ) ) {
139
			return new Jetpack_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'nonce' ) );
140
		}
141
142
		$normalized_request_pieces = array(
143
			$token,
144
			$timestamp,
145
			$nonce,
146
			$body_hash,
147
			strtoupper( $method ),
148
			strtolower( $parsed['host'] ),
149
			$port,
150
			$parsed['path'],
151
			// Normalized Query String
152
		);
153
154
		$normalized_request_pieces = array_merge( $normalized_request_pieces, $this->normalized_query_parameters( isset( $parsed['query'] ) ? $parsed['query'] : '' ) );
155
156
		$normalized_request_string = join( "\n", $normalized_request_pieces ) . "\n";
157
158
		return base64_encode( hash_hmac( 'sha1', $normalized_request_string, $this->secret, true ) );
159
	}
160
161
	function normalized_query_parameters( $query_string ) {
162
		parse_str( $query_string, $array );
163
		if ( get_magic_quotes_gpc() )
164
			$array = stripslashes_deep( $array );
165
166
		unset( $array['signature'] );
167
168
		$names  = array_keys( $array );
169
		$values = array_values( $array );
170
171
		$names  = array_map( array( $this, 'encode_3986' ), $names  );
172
		$values = array_map( array( $this, 'encode_3986' ), $values );
173
174
		$pairs  = array_map( array( $this, 'join_with_equal_sign' ), $names, $values );
175
176
		sort( $pairs );
177
178
		return $pairs;
179
	}
180
181
	function encode_3986( $string ) {
182
		$string = rawurlencode( $string );
183
		return str_replace( '%7E', '~', $string ); // prior to PHP 5.3, rawurlencode was RFC 1738
184
	}
185
186
	function join_with_equal_sign( $name, $value ) {
187
		return "{$name}={$value}";
188
	}
189
}
190
191
function jetpack_sha1_base64( $text ) {
192
	return base64_encode( sha1( $text, true ) );
193
}
194