PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta
16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / vendor / wp-php-toolkit / reprint-exporter / src / class-hmac-client.php
jetpack / vendor / wp-php-toolkit / reprint-exporter / src Last commit date
class-file-tree-producer.php 6 days ago class-hmac-client.php 6 days ago class-hmac-server.php 6 days ago class-http-server.php 6 days ago class-mysql-dump-producer.php 6 days ago class-pdo-polyfill.php 6 days ago class-sqlite-driver-pdo.php 6 days ago class-staged-artifacts.php 6 days ago class-staged-endpoints.php 6 days ago class-staged-push-stream-protocol.php 6 days ago class-wpdb-driver-pdo.php 6 days ago export.php 6 days ago utils.php 6 days ago
class-hmac-client.php
194 lines
1 <?php
2
3 /**
4 * HMAC Client for the Site Export API.
5 *
6 * This class generates the required HMAC signatures for authenticating
7 * requests to the Site Export API. The importing side uses this to sign
8 * all outgoing requests.
9 *
10 * Usage:
11 * $client = new Site_Export_HMAC_Client($shared_secret);
12 * $headers = $client->get_auth_headers($request_body);
13 * // Add $headers to your HTTP request
14 *
15 * Usage with curl:
16 *
17 * ```php
18 * // 1. First time: Generate and display a secret for the user
19 * $secret = Site_Export_HMAC_Client::generate_secret();
20 * echo "Please enter this secret in the Site Export plugin settings:\n";
21 * echo $secret . "\n";
22 *
23 * // 2. For each request: Create client and sign requests
24 * $client = new Site_Export_HMAC_Client($secret);
25 *
26 * // For GET requests:
27 * $ch = curl_init('https://example.com/?reprint-api&endpoint=file_index&directory=/var/www/html');
28 * $client->sign_curl_request($ch, '');
29 * curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
30 * $response = curl_exec($ch);
31 *
32 * // For POST requests with JSON body:
33 * $body = json_encode(['paths' => ['/wp-content/uploads/image.jpg']]);
34 * $ch = curl_init('https://example.com/?reprint-api&endpoint=file_fetch');
35 * $client->sign_curl_request($ch, $body);
36 * curl_setopt($ch, CURLOPT_POST, true);
37 * curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
38 * curl_setopt($ch, CURLOPT_HTTPHEADER, [
39 * 'Content-Type: application/json',
40 * // Auth headers are added by sign_curl_request
41 * ]);
42 * $response = curl_exec($ch);
43 * ```
44 */
45 class Site_Export_HMAC_Client {
46
47 /**
48 * Value of the X-Auth-Content-Hash header when the request body is
49 * deliberately not signed: this literal string stands where a body hash
50 * would otherwise be. Must match Site_Export_HMAC_Server::UNSIGNED_PAYLOAD.
51 */
52 public const UNSIGNED_PAYLOAD = 'UNSIGNED-PAYLOAD';
53
54 /** @var string */
55 private $secret;
56
57 public function __construct(string $secret) {
58 $this->secret = $secret;
59 }
60
61 /** Returns a hex-encoded random secret (64 chars = 256 bits by default). */
62 public static function generate_secret(int $length = 32): string {
63 return bin2hex(random_bytes($length));
64 }
65
66 /** @return string Hex-encoded 16-byte nonce. */
67 public function generate_nonce(): string {
68 return bin2hex(random_bytes(16));
69 }
70
71 /** @return string Microsecond-precision Unix timestamp. */
72 public function get_timestamp(): string {
73 return sprintf('%.6f', microtime(true));
74 }
75
76 /**
77 * Compute the HMAC signature for a request.
78 *
79 * The signature covers a SHA-256 hash of the body rather than the raw
80 * bytes. This avoids having to predict the exact encoding that libcurl
81 * will produce for multipart/form-data uploads while still binding the
82 * request to a digest: the server verifies the timestamp, nonce, and HMAC
83 * over X-Auth-Content-Hash before it computes or compares any body hash.
84 *
85 * This is intended for small command requests such as preflight or plan
86 * confirmation. Large data transfers
87 * should use an authenticated session and per-chunk hashes instead of
88 * HMAC-signing one large request body.
89 *
90 * Signature = HMAC-SHA256(nonce + timestamp + SHA256(body), secret)
91 *
92 * @param string $nonce Random nonce for this request
93 * @param string $timestamp Request timestamp
94 * @param string $content_hash Hex SHA-256 hash of the request body
95 * @return string Hex-encoded HMAC signature
96 */
97 public function compute_signature(string $nonce, string $timestamp, string $content_hash = ''): string {
98 if ($content_hash === '') {
99 $content_hash = hash('sha256', '');
100 }
101 $message = $nonce . $timestamp . $content_hash;
102 return hash_hmac('sha256', $message, $this->secret);
103 }
104
105 /** Returns all four X-Auth-* headers for a single request. */
106 public function get_auth_headers(string $body = ''): array {
107 $nonce = $this->generate_nonce();
108 $timestamp = $this->get_timestamp();
109 $content_hash = hash('sha256', $body);
110 $signature = $this->compute_signature($nonce, $timestamp, $content_hash);
111
112 return [
113 'X-Auth-Signature' => $signature,
114 'X-Auth-Nonce' => $nonce,
115 'X-Auth-Timestamp' => $timestamp,
116 'X-Auth-Content-Hash' => $content_hash,
117 ];
118 }
119
120 /**
121 * Returns X-Auth-* headers for a request whose body is not signed.
122 *
123 * The signature covers the nonce, the timestamp, the method, and the
124 * request target instead of a body hash, so a body of any size streams
125 * through without either side hashing it, and captured auth headers still cannot be reused for a
126 * different endpoint or method. Protecting the body from tampering is
127 * TLS's job — over --force-http a tampered body would be accepted,
128 * which is what that flag's help text warns about.
129 *
130 * Signature = HMAC-SHA256(nonce + timestamp + "UNSIGNED-PAYLOAD\n" + METHOD + "\n" + target, secret)
131 *
132 * @param string $method Uppercased into the signature (GET, POST, ...).
133 * @param string $url Full request URL; only path and query are signed.
134 */
135 public function get_envelope_auth_headers(string $method, string $url): array {
136 $nonce = $this->generate_nonce();
137 $timestamp = $this->get_timestamp();
138 $message = $nonce . $timestamp . self::UNSIGNED_PAYLOAD . "\n" . strtoupper($method) . "\n" . self::request_target($url);
139
140 return [
141 'X-Auth-Signature' => hash_hmac('sha256', $message, $this->secret),
142 'X-Auth-Nonce' => $nonce,
143 'X-Auth-Timestamp' => $timestamp,
144 'X-Auth-Content-Hash' => self::UNSIGNED_PAYLOAD,
145 ];
146 }
147
148 /**
149 * Normalizes a URL to the "path?query" form both sides sign — the same
150 * shape PHP exposes as $_SERVER['REQUEST_URI'] on the receiving end.
151 */
152 public static function request_target(string $url): string {
153 $path = parse_url($url, PHP_URL_PATH);
154 $query = parse_url($url, PHP_URL_QUERY);
155 $target = is_string($path) && $path !== '' ? $path : '/';
156
157 return is_string($query) && $query !== '' ? $target . '?' . $query : $target;
158 }
159
160 /** Returns auth headers formatted for CURLOPT_HTTPHEADER (["Name: value", ...]). */
161 public function get_curl_headers(string $body = ''): array {
162 $headers = $this->get_auth_headers($body);
163 $curl_headers = [];
164 foreach ($headers as $name => $value) {
165 $curl_headers[] = "{$name}: {$value}";
166 }
167 return $curl_headers;
168 }
169
170 /** Sets CURLOPT_HTTPHEADER with auth headers on a cURL handle. */
171 public function sign_curl_request($ch, string $body = ''): void {
172 curl_setopt($ch, CURLOPT_HTTPHEADER, $this->get_curl_headers($body));
173 }
174
175 /** Returns a stream context with auth headers, for use with file_get_contents(). */
176 public function create_stream_context(string $body = '', string $method = 'GET', array $extra = []) {
177 $headers = $this->get_auth_headers($body);
178 $header_string = '';
179 foreach ($headers as $name => $value) {
180 $header_string .= "{$name}: {$value}\r\n";
181 }
182
183 $options = [
184 'http' => array_merge([
185 'method' => $method,
186 'header' => $header_string,
187 'content' => $body,
188 ], $extra['http'] ?? []),
189 ];
190
191 return stream_context_create($options);
192 }
193 }
194