Skip to content

Commit 79fe9e1

Browse files
committed
GoogleBot is allowed to replay expired but correctly signed queries #10030
Otherwise, GoogleBot cannot crawl our websites correctly and generate a few `Signed query is expired` errors per day.
1 parent e3c58b7 commit 79fe9e1

File tree

2 files changed

+279
-3
lines changed

2 files changed

+279
-3
lines changed

src/Middleware/SignedQueryMiddleware.php

Lines changed: 252 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,22 @@ private function verify(ServerRequestInterface $request): ServerRequestInterface
6666
$timestamp = $m['timestamp'];
6767
$hash = $m['hash'];
6868

69-
$this->verifyTimestamp($timestamp);
69+
$this->verifyTimestamp($request, $timestamp);
7070

7171
return $this->verifyHash($request, $timestamp, $hash);
7272
}
7373

7474
throw new Exception('Invalid `X-Signature` HTTP header in signed query', 403);
7575
}
7676

77-
private function verifyTimestamp(string $timestamp): void
77+
private function verifyTimestamp(ServerRequestInterface $request, string $timestamp): void
7878
{
7979
$now = Chronos::now()->timestamp;
8080
$leeway = 15 * 900; // 15 minutes
8181
$past = $now - $leeway;
8282
$future = $now + $leeway;
83-
if ($timestamp < $past || $timestamp > $future) {
83+
$isExpired = $timestamp < $past || $timestamp > $future;
84+
if ($isExpired && !$this->isGoogleBot($request)) {
8485
throw new Exception('Signed query is expired', 403);
8586
}
8687
}
@@ -140,4 +141,252 @@ private function isAllowedIp(ServerRequestInterface $request): bool
140141

141142
return IPRange::matches($remoteAddress, $this->allowedIps);
142143
}
144+
145+
private function isGoogleBot(ServerRequestInterface $request): bool
146+
{
147+
$remoteAddress = $request->getServerParams()['REMOTE_ADDR'] ?? '';
148+
149+
if (!$remoteAddress || !is_string($remoteAddress)) {
150+
return false;
151+
}
152+
153+
// Source is https://developers.google.com/search/apis/ipranges/googlebot.json
154+
$googleBotIps = [
155+
'2001:4860:4801:10::/64',
156+
'2001:4860:4801:11::/64',
157+
'2001:4860:4801:12::/64',
158+
'2001:4860:4801:13::/64',
159+
'2001:4860:4801:14::/64',
160+
'2001:4860:4801:15::/64',
161+
'2001:4860:4801:16::/64',
162+
'2001:4860:4801:17::/64',
163+
'2001:4860:4801:18::/64',
164+
'2001:4860:4801:19::/64',
165+
'2001:4860:4801:1a::/64',
166+
'2001:4860:4801:1b::/64',
167+
'2001:4860:4801:1c::/64',
168+
'2001:4860:4801:1d::/64',
169+
'2001:4860:4801:1e::/64',
170+
'2001:4860:4801:20::/64',
171+
'2001:4860:4801:21::/64',
172+
'2001:4860:4801:22::/64',
173+
'2001:4860:4801:23::/64',
174+
'2001:4860:4801:24::/64',
175+
'2001:4860:4801:25::/64',
176+
'2001:4860:4801:26::/64',
177+
'2001:4860:4801:27::/64',
178+
'2001:4860:4801:28::/64',
179+
'2001:4860:4801:29::/64',
180+
'2001:4860:4801:2::/64',
181+
'2001:4860:4801:2a::/64',
182+
'2001:4860:4801:2b::/64',
183+
'2001:4860:4801:2c::/64',
184+
'2001:4860:4801:2d::/64',
185+
'2001:4860:4801:2e::/64',
186+
'2001:4860:4801:2f::/64',
187+
'2001:4860:4801:30::/64',
188+
'2001:4860:4801:31::/64',
189+
'2001:4860:4801:32::/64',
190+
'2001:4860:4801:33::/64',
191+
'2001:4860:4801:34::/64',
192+
'2001:4860:4801:35::/64',
193+
'2001:4860:4801:36::/64',
194+
'2001:4860:4801:37::/64',
195+
'2001:4860:4801:38::/64',
196+
'2001:4860:4801:39::/64',
197+
'2001:4860:4801:3::/64',
198+
'2001:4860:4801:3a::/64',
199+
'2001:4860:4801:3b::/64',
200+
'2001:4860:4801:3c::/64',
201+
'2001:4860:4801:3d::/64',
202+
'2001:4860:4801:3e::/64',
203+
'2001:4860:4801:40::/64',
204+
'2001:4860:4801:41::/64',
205+
'2001:4860:4801:42::/64',
206+
'2001:4860:4801:43::/64',
207+
'2001:4860:4801:44::/64',
208+
'2001:4860:4801:45::/64',
209+
'2001:4860:4801:46::/64',
210+
'2001:4860:4801:47::/64',
211+
'2001:4860:4801:48::/64',
212+
'2001:4860:4801:49::/64',
213+
'2001:4860:4801:4a::/64',
214+
'2001:4860:4801:50::/64',
215+
'2001:4860:4801:51::/64',
216+
'2001:4860:4801:53::/64',
217+
'2001:4860:4801:54::/64',
218+
'2001:4860:4801:55::/64',
219+
'2001:4860:4801:60::/64',
220+
'2001:4860:4801:61::/64',
221+
'2001:4860:4801:62::/64',
222+
'2001:4860:4801:63::/64',
223+
'2001:4860:4801:64::/64',
224+
'2001:4860:4801:65::/64',
225+
'2001:4860:4801:66::/64',
226+
'2001:4860:4801:67::/64',
227+
'2001:4860:4801:68::/64',
228+
'2001:4860:4801:69::/64',
229+
'2001:4860:4801:6a::/64',
230+
'2001:4860:4801:6b::/64',
231+
'2001:4860:4801:6c::/64',
232+
'2001:4860:4801:6d::/64',
233+
'2001:4860:4801:6e::/64',
234+
'2001:4860:4801:6f::/64',
235+
'2001:4860:4801:70::/64',
236+
'2001:4860:4801:71::/64',
237+
'2001:4860:4801:72::/64',
238+
'2001:4860:4801:73::/64',
239+
'2001:4860:4801:74::/64',
240+
'2001:4860:4801:75::/64',
241+
'2001:4860:4801:76::/64',
242+
'2001:4860:4801:77::/64',
243+
'2001:4860:4801:78::/64',
244+
'2001:4860:4801:79::/64',
245+
'2001:4860:4801:80::/64',
246+
'2001:4860:4801:81::/64',
247+
'2001:4860:4801:82::/64',
248+
'2001:4860:4801:83::/64',
249+
'2001:4860:4801:84::/64',
250+
'2001:4860:4801:85::/64',
251+
'2001:4860:4801:86::/64',
252+
'2001:4860:4801:87::/64',
253+
'2001:4860:4801:88::/64',
254+
'2001:4860:4801:90::/64',
255+
'2001:4860:4801:91::/64',
256+
'2001:4860:4801:92::/64',
257+
'2001:4860:4801:93::/64',
258+
'2001:4860:4801:c::/64',
259+
'2001:4860:4801:f::/64',
260+
'192.178.5.0/27',
261+
'34.100.182.96/28',
262+
'34.101.50.144/28',
263+
'34.118.254.0/28',
264+
'34.118.66.0/28',
265+
'34.126.178.96/28',
266+
'34.146.150.144/28',
267+
'34.147.110.144/28',
268+
'34.151.74.144/28',
269+
'34.152.50.64/28',
270+
'34.154.114.144/28',
271+
'34.155.98.32/28',
272+
'34.165.18.176/28',
273+
'34.175.160.64/28',
274+
'34.176.130.16/28',
275+
'34.22.85.0/27',
276+
'34.64.82.64/28',
277+
'34.65.242.112/28',
278+
'34.80.50.80/28',
279+
'34.88.194.0/28',
280+
'34.89.10.80/28',
281+
'34.89.198.80/28',
282+
'34.96.162.48/28',
283+
'35.247.243.240/28',
284+
'66.249.64.0/27',
285+
'66.249.64.128/27',
286+
'66.249.64.160/27',
287+
'66.249.64.192/27',
288+
'66.249.64.224/27',
289+
'66.249.64.32/27',
290+
'66.249.64.64/27',
291+
'66.249.64.96/27',
292+
'66.249.65.0/27',
293+
'66.249.65.160/27',
294+
'66.249.65.192/27',
295+
'66.249.65.224/27',
296+
'66.249.65.32/27',
297+
'66.249.65.64/27',
298+
'66.249.65.96/27',
299+
'66.249.66.0/27',
300+
'66.249.66.128/27',
301+
'66.249.66.160/27',
302+
'66.249.66.192/27',
303+
'66.249.66.32/27',
304+
'66.249.66.64/27',
305+
'66.249.66.96/27',
306+
'66.249.68.0/27',
307+
'66.249.68.32/27',
308+
'66.249.68.64/27',
309+
'66.249.69.0/27',
310+
'66.249.69.128/27',
311+
'66.249.69.160/27',
312+
'66.249.69.192/27',
313+
'66.249.69.224/27',
314+
'66.249.69.32/27',
315+
'66.249.69.64/27',
316+
'66.249.69.96/27',
317+
'66.249.70.0/27',
318+
'66.249.70.128/27',
319+
'66.249.70.160/27',
320+
'66.249.70.192/27',
321+
'66.249.70.224/27',
322+
'66.249.70.32/27',
323+
'66.249.70.64/27',
324+
'66.249.70.96/27',
325+
'66.249.71.0/27',
326+
'66.249.71.128/27',
327+
'66.249.71.160/27',
328+
'66.249.71.192/27',
329+
'66.249.71.224/27',
330+
'66.249.71.32/27',
331+
'66.249.71.64/27',
332+
'66.249.71.96/27',
333+
'66.249.72.0/27',
334+
'66.249.72.128/27',
335+
'66.249.72.160/27',
336+
'66.249.72.192/27',
337+
'66.249.72.224/27',
338+
'66.249.72.32/27',
339+
'66.249.72.64/27',
340+
'66.249.72.96/27',
341+
'66.249.73.0/27',
342+
'66.249.73.128/27',
343+
'66.249.73.160/27',
344+
'66.249.73.192/27',
345+
'66.249.73.224/27',
346+
'66.249.73.32/27',
347+
'66.249.73.64/27',
348+
'66.249.73.96/27',
349+
'66.249.74.0/27',
350+
'66.249.74.128/27',
351+
'66.249.74.32/27',
352+
'66.249.74.64/27',
353+
'66.249.74.96/27',
354+
'66.249.75.0/27',
355+
'66.249.75.128/27',
356+
'66.249.75.160/27',
357+
'66.249.75.192/27',
358+
'66.249.75.224/27',
359+
'66.249.75.32/27',
360+
'66.249.75.64/27',
361+
'66.249.75.96/27',
362+
'66.249.76.0/27',
363+
'66.249.76.128/27',
364+
'66.249.76.160/27',
365+
'66.249.76.192/27',
366+
'66.249.76.224/27',
367+
'66.249.76.32/27',
368+
'66.249.76.64/27',
369+
'66.249.76.96/27',
370+
'66.249.77.0/27',
371+
'66.249.77.128/27',
372+
'66.249.77.160/27',
373+
'66.249.77.192/27',
374+
'66.249.77.224/27',
375+
'66.249.77.32/27',
376+
'66.249.77.64/27',
377+
'66.249.77.96/27',
378+
'66.249.78.0/27',
379+
'66.249.78.32/27',
380+
'66.249.79.0/27',
381+
'66.249.79.128/27',
382+
'66.249.79.160/27',
383+
'66.249.79.192/27',
384+
'66.249.79.224/27',
385+
'66.249.79.32/27',
386+
'66.249.79.64/27',
387+
'66.249.79.96/27',
388+
];
389+
390+
return IPRange::matches($remoteAddress, $googleBotIps);
391+
}
143392
}

tests/Middleware/SignedQueryMiddlewareTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,5 +203,32 @@ public static function dataProviderQuery(): iterable
203203
'Invalid signed query',
204204
'1.2.3.4',
205205
];
206+
207+
yield 'no header, even GoogleBot is rejected, because GoogleBot should not forge new requests but only (re)play existing ones' => [
208+
[$key1],
209+
'{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
210+
null,
211+
'',
212+
'Missing `X-Signature` HTTP header in signed query',
213+
'66.249.70.134',
214+
];
215+
216+
yield 'too much in the past, but GoogleBot is allowed to replay old requests' => [
217+
[$key1],
218+
'{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
219+
null,
220+
'v1.1577951099.20177a7face4e05a75c4b2e41bc97a8225f420f5b7bb1709dd5499821dba0807',
221+
'',
222+
'66.249.70.134',
223+
];
224+
225+
yield 'too much in the past and invalid signature, even GoogleBot is rejected, because GoogleBot should not modify queries and their signatures' => [
226+
[$key1],
227+
'{"operationName":"CurrentUser","variables":{},"query":"query CurrentUser { viewer { id }}',
228+
null,
229+
'v1.1577951099' . str_repeat('a', 64),
230+
'Invalid `X-Signature` HTTP header in signed query',
231+
'66.249.70.134',
232+
];
206233
}
207234
}

0 commit comments

Comments
 (0)