Skip to content

Commit 5466b4b

Browse files
authored
Merge pull request #21 from SoapBox/change/validate-timestamp-format
[Change] Introduces Request Timestamp Validation
2 parents 853f2b1 + 7b198b9 commit 5466b4b

File tree

5 files changed

+118
-8
lines changed

5 files changed

+118
-8
lines changed

src/Helpers.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace SoapBox\SignedRequests;
4+
5+
use DateTime;
6+
7+
class Helpers
8+
{
9+
/**
10+
* Verifies if the provided date string is in the given format
11+
*
12+
* @param string $datetime
13+
* @param string $format
14+
*
15+
* @return boolean
16+
*/
17+
public static function verifyDateTime(string $datetime, string $format): bool
18+
{
19+
$formatted = DateTime::createFromFormat($format, $datetime);
20+
21+
$errors = DateTime::getLastErrors();
22+
23+
if (!empty($errors['warning_count'])) {
24+
return false;
25+
}
26+
27+
return $formatted !== false;
28+
}
29+
}

src/Requests/Generator.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@ public function sign(Request $request) : Request
4747
$key = $this->configuration->getSigningKey();
4848

4949
$request = $request->withHeader('X-SIGNED-ID', (string) Uuid::uuid4());
50-
$request = $request->withHeader('X-SIGNED-TIMESTAMP', (string) Carbon::now());
50+
$request = $request->withHeader(
51+
'X-SIGNED-TIMESTAMP',
52+
Carbon::now()->format('Y-m-d H:i:s')
53+
);
5154

5255
$signature = new Signature(new Payload($request), $algorithm, $key);
5356

src/Requests/Verifier.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Carbon\Carbon;
66
use Illuminate\Http\Request;
7+
use SoapBox\SignedRequests\Helpers;
78
use SoapBox\SignedRequests\Signature;
89

910
class Verifier
@@ -185,10 +186,12 @@ public function isValid(string $key) : bool
185186
*/
186187
public function isExpired(int $tolerance) : bool
187188
{
188-
$issuedAt =
189-
Carbon::parse($this->headers->get('X-SIGNED-TIMESTAMP', '1901-01-01 12:00:00'));
189+
$timestamp = $this->headers->get('X-SIGNED-TIMESTAMP', '1901-01-01 12:00:00');
190+
$issuedAt = Carbon::parse($timestamp);
190191

191-
return Carbon::now()->diffInSeconds($issuedAt) > $tolerance;
192+
$isValid = Helpers::verifyDateTime($timestamp, 'Y-m-d H:i:s');
193+
194+
return !$isValid || Carbon::now()->diffInSeconds($issuedAt) > $tolerance;
192195
}
193196

194197
/**

tests/HelpersTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Tests;
4+
5+
use Carbon\Carbon;
6+
use SoapBox\SignedRequests\Helpers;
7+
8+
class HelpersTest extends TestCase
9+
{
10+
/**
11+
* @test
12+
*/
13+
public function it_correctly_verifies_that_the_provided_datetime_string_is_in_the_given_format()
14+
{
15+
$datetime = Carbon::parse('2001-01-31 12:11:18');
16+
17+
$this->assertTrue(Helpers::verifyDateTime((string) $datetime, 'Y-m-d H:i:s'));
18+
$this->assertTrue(Helpers::verifyDateTime($datetime->format('Y-m-d H:i:s'), 'Y-m-d H:i:s'));
19+
$this->assertTrue(Helpers::verifyDateTime($datetime->format('Y-m-d H:i:s.u'), 'Y-m-d H:i:s.u'));
20+
$this->assertTrue(Helpers::verifyDateTime($datetime->format('Y-m-d'), 'Y-m-d'));
21+
$this->assertTrue(Helpers::verifyDateTime($datetime->format('Y-d-m H:i:s'), 'Y-d-m H:i:s'));
22+
23+
$this->assertFalse(Helpers::verifyDateTime($datetime->format('Y-m-d H:i:s.u'), 'Y-m-d H:i:s'));
24+
$this->assertFalse(Helpers::verifyDateTime($datetime->format('Y-m-d H-i-s'), 'Y-m-d H:i:s.u'));
25+
$this->assertFalse(Helpers::verifyDateTime($datetime->format('Y-m-d H:i:s'), 'Y-d-m H:i:s'));
26+
$this->assertFalse(Helpers::verifyDateTime($datetime->format('Y-m-d H:i:s'), 'Y-m-d'));
27+
$this->assertFalse(Helpers::verifyDateTime($datetime->format('Y-m-d H:i:s'), 'Y-m-d-H:i:s.u'));
28+
}
29+
}

tests/Middlewares/Laravel/VerifySignatureTest.php

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ public function it_throws_an_invalid_signature_exception_if_the_request_is_not_v
8686

8787
$request = new Request();
8888

89-
$this->middleware->handle($request, function () { });
89+
$this->middleware->handle($request, function () {
90+
});
9091
}
9192

9293
/**
@@ -169,7 +170,8 @@ public function it_throws_an_expired_request_exception_if_the_timestamp_on_the_r
169170
$request = new Request($query, $request, $attributes, $cookies, $files, $server, 'a');
170171
$request->headers->set('signature', (string) new Signature(new Payload($request), 'sha256', 'key'));
171172

172-
$this->middleware->handle($request, function () { });
173+
$this->middleware->handle($request, function () {
174+
});
173175
}
174176

175177
/**
@@ -257,7 +259,8 @@ public function it_should_throw_an_expired_request_exception_if_the_request_id_h
257259
$request = new Request($query, $request, $attributes, $cookies, $files, $server, 'a');
258260
$request->headers->set('signature', (string) new Signature(new Payload($request), 'sha256', 'key'));
259261

260-
$this->middleware->handle($request, function () { });
262+
$this->middleware->handle($request, function () {
263+
});
261264
}
262265

263266
/**
@@ -303,6 +306,49 @@ public function it_should_throw_an_expired_request_exception_if_the_same_request
303306
$this->assertTrue(true);
304307
});
305308

306-
$this->middleware->handle($request, function () { });
309+
$this->middleware->handle($request, function () {
310+
});
311+
}
312+
313+
/**
314+
* @test
315+
* @expectedException \SoapBox\SignedRequests\Exceptions\ExpiredRequestException
316+
*/
317+
public function it_throws_an_expired_request_exception_if_the_timestamp_on_the_request_does_not_have_the_correct_format()
318+
{
319+
$id = (string) Uuid::uuid4();
320+
321+
$this->configurations->shouldReceive('get')
322+
->with('signed-requests.headers.signature')
323+
->andReturn('signature');
324+
325+
$this->configurations->shouldReceive('get')
326+
->with('signed-requests.headers.algorithm')
327+
->andReturn('algorithm');
328+
329+
$this->configurations->shouldReceive('get')
330+
->with('signed-requests.key')
331+
->andReturn('key');
332+
333+
$this->configurations->shouldReceive('get')
334+
->with('signed-requests.request-replay.allow')
335+
->andReturn(false);
336+
337+
$query = [];
338+
$request = [];
339+
$attributes = [];
340+
$cookies = [];
341+
$files = [];
342+
$server = [
343+
'HTTP_X-SIGNED-ID' => $id,
344+
'HTTP_X-SIGNED-TIMESTAMP' => Carbon::now()->addSeconds(10)->format('Y-m-d'),
345+
'HTTP_ALGORITHM' => 'sha256'
346+
];
347+
348+
$request = new Request($query, $request, $attributes, $cookies, $files, $server, 'a');
349+
$request->headers->set('signature', (string) new Signature(new Payload($request), 'sha256', 'key'));
350+
351+
$this->middleware->handle($request, function () {
352+
});
307353
}
308354
}

0 commit comments

Comments
 (0)