Automattic /
jetpack
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | use \Automattic\Jetpack\Connection\Manager as Connection_Manager; |
||
| 4 | |||
| 5 | class Jetpack_Signature { |
||
| 6 | public $token; |
||
| 7 | public $secret; |
||
| 8 | public $current_request_url; |
||
| 9 | |||
| 10 | function __construct( $access_token, $time_diff = 0 ) { |
||
| 11 | $secret = explode( '.', $access_token ); |
||
| 12 | if ( 2 != count( $secret ) ) { |
||
| 13 | return; |
||
| 14 | } |
||
| 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 WP_Error( 'invalid_scheme', 'Invalid URL scheme' ); |
||
|
0 ignored issues
–
show
|
|||
| 26 | } |
||
| 27 | } else { |
||
| 28 | if ( is_ssl() ) { |
||
| 29 | $scheme = 'https'; |
||
| 30 | } else { |
||
| 31 | $scheme = 'http'; |
||
| 32 | } |
||
| 33 | } |
||
| 34 | |||
| 35 | $host_port = isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) ? $_SERVER['HTTP_X_FORWARDED_PORT'] : $_SERVER['SERVER_PORT']; |
||
| 36 | |||
| 37 | $connection = new Connection_Manager(); |
||
| 38 | /** |
||
| 39 | * Note: This port logic is tested in the Jetpack_Cxn_Tests->test__server_port_value() test. |
||
| 40 | * Please update the test if any changes are made in this logic. |
||
| 41 | */ |
||
| 42 | if ( is_ssl() ) { |
||
| 43 | // 443: Standard Port |
||
| 44 | // 80: Assume we're behind a proxy without X-Forwarded-Port. Hardcoding "80" here means most sites |
||
| 45 | // with SSL termination proxies (self-served, Cloudflare, etc.) don't need to fiddle with |
||
| 46 | // the JETPACK_SIGNATURE__HTTPS_PORT constant. The code also implies we can't talk to a |
||
| 47 | // site at https://example.com:80/ (which would be a strange configuration). |
||
| 48 | // JETPACK_SIGNATURE__HTTPS_PORT: Set this constant in wp-config.php to the back end webserver's port |
||
| 49 | // if the site is behind a proxy running on port 443 without |
||
| 50 | // X-Forwarded-Port and the back end's port is *not* 80. It's better, |
||
| 51 | // though, to configure the proxy to send X-Forwarded-Port. |
||
| 52 | $https_port = defined( 'JETPACK_SIGNATURE__HTTPS_PORT' ) ? JETPACK_SIGNATURE__HTTPS_PORT : 443; |
||
| 53 | $port = in_array( $host_port, array( 443, 80, $https_port ) ) ? '' : $host_port; |
||
| 54 | } else { |
||
| 55 | // 80: Standard Port |
||
| 56 | // JETPACK_SIGNATURE__HTTPS_PORT: Set this constant in wp-config.php to the back end webserver's port |
||
| 57 | // if the site is behind a proxy running on port 80 without |
||
| 58 | // X-Forwarded-Port. It's better, though, to configure the proxy to |
||
| 59 | // send X-Forwarded-Port. |
||
| 60 | $http_port = defined( 'JETPACK_SIGNATURE__HTTP_PORT' ) ? JETPACK_SIGNATURE__HTTP_PORT : 80; |
||
| 61 | $port = in_array( $host_port, array( 80, $http_port ) ) ? '' : $host_port; |
||
| 62 | } |
||
| 63 | |||
| 64 | $this->current_request_url = "{$scheme}://{$_SERVER['HTTP_HOST']}:{$port}" . stripslashes( $_SERVER['REQUEST_URI'] ); |
||
| 65 | |||
| 66 | if ( array_key_exists( 'body', $override ) && ! empty( $override['body'] ) ) { |
||
| 67 | $body = $override['body']; |
||
| 68 | } elseif ( 'POST' == strtoupper( $_SERVER['REQUEST_METHOD'] ) ) { |
||
| 69 | $body = isset( $GLOBALS['HTTP_RAW_POST_DATA'] ) ? $GLOBALS['HTTP_RAW_POST_DATA'] : null; |
||
| 70 | |||
| 71 | // Convert the $_POST to the body, if the body was empty. This is how arrays are hashed |
||
| 72 | // and encoded on the Jetpack side. |
||
| 73 | if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { |
||
| 74 | if ( empty( $body ) && is_array( $_POST ) && count( $_POST ) > 0 ) { |
||
| 75 | $body = $_POST; |
||
| 76 | } |
||
| 77 | } |
||
| 78 | } elseif ( 'PUT' == strtoupper( $_SERVER['REQUEST_METHOD'] ) ) { |
||
| 79 | // This is a little strange-looking, but there doesn't seem to be another way to get the PUT body |
||
| 80 | $raw_put_data = file_get_contents( 'php://input' ); |
||
| 81 | parse_str( $raw_put_data, $body ); |
||
| 82 | |||
| 83 | if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { |
||
| 84 | $put_data = json_decode( $raw_put_data, true ); |
||
| 85 | if ( is_array( $put_data ) && count( $put_data ) > 0 ) { |
||
| 86 | $body = $put_data; |
||
| 87 | } |
||
| 88 | } |
||
| 89 | } else { |
||
| 90 | $body = null; |
||
| 91 | } |
||
| 92 | |||
| 93 | if ( empty( $body ) ) { |
||
| 94 | $body = null; |
||
| 95 | } |
||
| 96 | |||
| 97 | $a = array(); |
||
| 98 | foreach ( array( 'token', 'timestamp', 'nonce', 'body-hash' ) as $parameter ) { |
||
| 99 | if ( isset( $override[ $parameter ] ) ) { |
||
| 100 | $a[ $parameter ] = $override[ $parameter ]; |
||
| 101 | } else { |
||
| 102 | $a[ $parameter ] = isset( $_GET[ $parameter ] ) ? stripslashes( $_GET[ $parameter ] ) : ''; |
||
| 103 | } |
||
| 104 | } |
||
| 105 | |||
| 106 | $method = isset( $override['method'] ) ? $override['method'] : $_SERVER['REQUEST_METHOD']; |
||
| 107 | return $this->sign_request( $a['token'], $a['timestamp'], $a['nonce'], $a['body-hash'], $method, $this->current_request_url, $body, true ); |
||
| 108 | } |
||
| 109 | |||
| 110 | // body_hash v. body-hash is annoying. Refactor to accept an array? |
||
| 111 | function sign_request( $token = '', $timestamp = 0, $nonce = '', $body_hash = '', $method = '', $url = '', $body = null, $verify_body_hash = true ) { |
||
| 112 | if ( ! $this->secret ) { |
||
| 113 | return new WP_Error( 'invalid_secret', 'Invalid secret' ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_secret'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 114 | } |
||
| 115 | |||
| 116 | if ( ! $this->token ) { |
||
| 117 | return new WP_Error( 'invalid_token', 'Invalid token' ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_token'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 118 | } |
||
| 119 | |||
| 120 | list( $token ) = explode( '.', $token ); |
||
| 121 | |||
| 122 | $signature_details = compact( 'token', 'timestamp', 'nonce', 'body_hash', 'method', 'url' ); |
||
| 123 | |||
| 124 | if ( 0 !== strpos( $token, "$this->token:" ) ) { |
||
| 125 | return new WP_Error( 'token_mismatch', 'Incorrect token', compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'token_mismatch'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 126 | } |
||
| 127 | |||
| 128 | // If we got an array at this point, let's encode it, so we can see what it looks like as a string. |
||
| 129 | if ( is_array( $body ) ) { |
||
| 130 | if ( count( $body ) > 0 ) { |
||
| 131 | $body = json_encode( $body ); |
||
| 132 | |||
| 133 | } else { |
||
| 134 | $body = ''; |
||
| 135 | } |
||
| 136 | } |
||
| 137 | |||
| 138 | $required_parameters = array( 'token', 'timestamp', 'nonce', 'method', 'url' ); |
||
| 139 | if ( ! is_null( $body ) ) { |
||
| 140 | $required_parameters[] = 'body_hash'; |
||
| 141 | if ( ! is_string( $body ) ) { |
||
| 142 | return new WP_Error( 'invalid_body', 'Body is malformed.', compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_body'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 143 | } |
||
| 144 | } |
||
| 145 | |||
| 146 | foreach ( $required_parameters as $required ) { |
||
| 147 | if ( ! is_scalar( $$required ) ) { |
||
| 148 | return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', str_replace( '_', '-', $required ) ), compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_signature'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 149 | } |
||
| 150 | |||
| 151 | View Code Duplication | if ( ! strlen( $$required ) ) { |
|
| 152 | return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is missing.', str_replace( '_', '-', $required ) ), compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_signature'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 153 | } |
||
| 154 | } |
||
| 155 | |||
| 156 | if ( empty( $body ) ) { |
||
| 157 | if ( $body_hash ) { |
||
| 158 | return new WP_Error( 'invalid_body_hash', 'Invalid body hash for empty body.', compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_body_hash'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 159 | } |
||
| 160 | } else { |
||
| 161 | $connection = new Connection_Manager(); |
||
| 162 | if ( $verify_body_hash && $connection->sha1_base64( $body ) !== $body_hash ) { |
||
| 163 | return new WP_Error( 'invalid_body_hash', 'The body hash does not match.', compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_body_hash'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 164 | } |
||
| 165 | } |
||
| 166 | |||
| 167 | $parsed = parse_url( $url ); |
||
| 168 | if ( ! isset( $parsed['host'] ) ) { |
||
| 169 | return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'url' ), compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_signature'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 170 | } |
||
| 171 | |||
| 172 | if ( ! empty( $parsed['port'] ) ) { |
||
| 173 | $port = $parsed['port']; |
||
| 174 | } else { |
||
| 175 | if ( 'http' == $parsed['scheme'] ) { |
||
| 176 | $port = 80; |
||
| 177 | } elseif ( 'https' == $parsed['scheme'] ) { |
||
| 178 | $port = 443; |
||
| 179 | } else { |
||
| 180 | return new WP_Error( 'unknown_scheme_port', "The scheme's port is unknown", compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'unknown_scheme_port'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 181 | } |
||
| 182 | } |
||
| 183 | |||
| 184 | View Code Duplication | if ( ! ctype_digit( "$timestamp" ) || 10 < strlen( $timestamp ) ) { // If Jetpack is around in 275 years, you can blame mdawaffe for the bug. |
|
| 185 | return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'timestamp' ), compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_signature'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 186 | } |
||
| 187 | |||
| 188 | $local_time = $timestamp - $this->time_diff; |
||
| 189 | View Code Duplication | if ( $local_time < time() - 600 || $local_time > time() + 300 ) { |
|
| 190 | return new WP_Error( 'invalid_signature', 'The timestamp is too old.', compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_signature'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 191 | } |
||
| 192 | |||
| 193 | View Code Duplication | if ( 12 < strlen( $nonce ) || preg_match( '/[^a-zA-Z0-9]/', $nonce ) ) { |
|
| 194 | return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'nonce' ), compact( 'signature_details' ) ); |
||
|
0 ignored issues
–
show
The call to
WP_Error::__construct() has too many arguments starting with 'invalid_signature'.
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the Loading history...
|
|||
| 195 | } |
||
| 196 | |||
| 197 | $normalized_request_pieces = array( |
||
| 198 | $token, |
||
| 199 | $timestamp, |
||
| 200 | $nonce, |
||
| 201 | $body_hash, |
||
| 202 | strtoupper( $method ), |
||
| 203 | strtolower( $parsed['host'] ), |
||
| 204 | $port, |
||
| 205 | $parsed['path'], |
||
| 206 | // Normalized Query String |
||
| 207 | ); |
||
| 208 | |||
| 209 | $normalized_request_pieces = array_merge( $normalized_request_pieces, $this->normalized_query_parameters( isset( $parsed['query'] ) ? $parsed['query'] : '' ) ); |
||
| 210 | $flat_normalized_request_pieces = array(); |
||
| 211 | foreach ( $normalized_request_pieces as $piece ) { |
||
| 212 | if ( is_array( $piece ) ) { |
||
| 213 | foreach ( $piece as $subpiece ) { |
||
| 214 | $flat_normalized_request_pieces[] = $subpiece; |
||
| 215 | } |
||
| 216 | } else { |
||
| 217 | $flat_normalized_request_pieces[] = $piece; |
||
| 218 | } |
||
| 219 | } |
||
| 220 | $normalized_request_pieces = $flat_normalized_request_pieces; |
||
| 221 | |||
| 222 | $normalized_request_string = join( "\n", $normalized_request_pieces ) . "\n"; |
||
| 223 | |||
| 224 | return base64_encode( hash_hmac( 'sha1', $normalized_request_string, $this->secret, true ) ); |
||
| 225 | } |
||
| 226 | |||
| 227 | function normalized_query_parameters( $query_string ) { |
||
| 228 | parse_str( $query_string, $array ); |
||
| 229 | if ( get_magic_quotes_gpc() ) { |
||
| 230 | $array = stripslashes_deep( $array ); |
||
| 231 | } |
||
| 232 | |||
| 233 | unset( $array['signature'] ); |
||
| 234 | |||
| 235 | $names = array_keys( $array ); |
||
| 236 | $values = array_values( $array ); |
||
| 237 | |||
| 238 | $names = array_map( array( $this, 'encode_3986' ), $names ); |
||
| 239 | $values = array_map( array( $this, 'encode_3986' ), $values ); |
||
| 240 | |||
| 241 | $pairs = array_map( array( $this, 'join_with_equal_sign' ), $names, $values ); |
||
| 242 | |||
| 243 | sort( $pairs ); |
||
| 244 | |||
| 245 | return $pairs; |
||
| 246 | } |
||
| 247 | |||
| 248 | function encode_3986( $string_or_array ) { |
||
| 249 | if ( is_array( $string_or_array ) ) { |
||
| 250 | return array_map( array( $this, 'encode_3986' ), $string_or_array ); |
||
| 251 | } |
||
| 252 | |||
| 253 | $string_or_array = rawurlencode( $string_or_array ); |
||
| 254 | return str_replace( '%7E', '~', $string_or_array ); // prior to PHP 5.3, rawurlencode was RFC 1738 |
||
| 255 | } |
||
| 256 | |||
| 257 | function join_with_equal_sign( $name, $value ) { |
||
| 258 | if ( is_array( $value ) ) { |
||
| 259 | $result = array(); |
||
| 260 | foreach ( $value as $array_key => $array_value ) { |
||
| 261 | $result[] = $name . '[' . $array_key . ']' . '=' . $array_value; |
||
| 262 | } |
||
| 263 | return $result; |
||
| 264 | } |
||
| 265 | return "{$name}={$value}"; |
||
| 266 | } |
||
| 267 | } |
||
| 268 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.