soapbox / signed-requests
A wrapper to add the ability to accept signed requests to a Laravel project.
Installs: 49 622
Dependents: 2
Suggesters: 0
Security: 0
Stars: 6
Watchers: 14
Forks: 1
Open Issues: 3
Requires
- php: ^7.3 || ^8.0
- guzzlehttp/guzzle: ^7.2
- illuminate/http: ^8.12
- illuminate/support: ^8.12
- nesbot/carbon: ^2.13
- ramsey/uuid: ^4.0
Requires (Dev)
- jshayes/fake-requests: dev-master
- mockery/mockery: ^1.4
- orchestra/testbench: ^6.3
- php-coveralls/php-coveralls: ^2.4
- phpunit/phpunit: ^9.3
README
A wrapper to add the ability to accept signed requests to a Laravel project.
Installation
Composer
composer require soapbox/signed-requests
Setup the Service Provider
Open config/app.php
and register the required service provider above your application providers.
'providers' => [ ... SoapBox\SignedRequests\ServiceProvider::class ... ]
Publish the Configuration
php artisan vendor:publish --provider 'SoapBox\SignedRequests\ServiceProvider'
Configuring your Environment
You will need to set the following details in your environment:
SIGNED_REQUEST_ALGORITHM= SIGNED_REQUEST_CACHE_PREFIX= SIGNED_REQUEST_SIGNATURE_HEADER= SIGNED_REQUEST_ALGORITHM_HEADER= SIGNED_REQUEST_KEY= SIGNED_REQUEST_ALLOW_REPLAYS= SIGNED_REQUEST_TOLERANCE_SECONDS=
Each of the settings above allows for a different level of configuration.
SIGNED_REQUEST_ALGORITHM
is the algorithm that will be used to generate / verify the signature. This is defaulted to usesha256
feel free to change this to anything thathash_hmac
accepts.SIGNED_REQUEST_CACHE_PREFIX
is the prefix to use for all the cache keys that will be generated. Here you can use the default if you're not planning on sharing a cache between multiple applications.SIGNED_REQUEST_SIGNATURE_HEADER
should be the request header that the signature will be included on,X-Signature
will be used by default.SIGNED_REQUEST_ALGORITHM_HEADER
should be the request header that the includes the algorithm used to sign the request.SIGNED_REQUEST_KEY
is the shared secret key between the application generating the requests, and the application consuming them. This value should not be publically available.SIGNED_REQUEST_ALLOW_REPLAYS
allows you to enable or disable replay attacks. By default replays are disabled.SIGNED_REQUEST_TOLERANCE_SECONDS
is the number of seconds that a request will be considered for. This setting allows for some time drift between servers and is only used when replays are disabled.
Setup the Middleware
Signed Requests includes a middleware to validate the signature of a request for your automatically. To get started, add the following middleware to the $routeMiddleware
property of your app/Http/Kernel.php
file.
'verify-signature' => \SoapBox\SignedRequests\Middlewares\Laravel\VerifySignature::class
Verify the Signature
The verify-signature
middleware may be assigned to a route to verify the signature of the incoming request to verify its authenticity:
Route::get('/fire', function () { return "You'll only see this if the signature of the request is valid!"; })->middleware('verify-signature');
Setting Up Additional Keys
You can also set up additional keys to use if you want different keys for different endpoints.
Add them to your environment:
CUSTOM_SIGNED_REQUEST_ALGORITHM= CUSTOM_SIGNED_REQUEST_CACHE_PREFIX= CUSTOM_SIGNED_REQUEST_SIGNATURE_HEADER= CUSTOM_SIGNED_REQUEST_ALGORITHM_HEADER= CUSTOM_SIGNED_REQUEST_KEY= CUSTOM_SIGNED_REQUEST_ALLOW_REPLAYS= CUSTOM_SIGNED_REQUEST_TOLERANCE_SECONDS=
Update the configuration in signed-requests.php
'default' => [ ... ], 'custom' => [ 'algorithm' => env('CUSTOM_SIGNED_REQUEST_ALGORITHM', 'sha256'), 'cache-prefix' => env('CUSTOM_SIGNED_REQUEST_CACHE_PREFIX', 'signed-requests'), 'headers' => [ 'signature' => env('CUSTOM_SIGNED_REQUEST_SIGNATURE_HEADER', 'X-Signature'), 'algorithm' => env('CUSTOM_SIGNED_REQUEST_ALGORITHM_HEADER', 'X-Signature-Algorithm') ], 'key' => env('CUSTOM_SIGNED_REQUEST_KEY', 'key'), 'request-replay' => [ 'allow' => env('CUSTOM_SIGNED_REQUEST_ALLOW_REPLAYS', false), 'tolerance' => env('CUSTOM_SIGNED_REQUEST_TOLERANCE_SECONDS', 30) ] ]
Set up your route to use the custom key. The param you pass must be the same name as the key you set in the configuration in signed-requests.php
Route::get('/fire', function () { return "You'll only see this if the signature of the request is valid!"; })->middleware('verify-signature:custom');
Signing Postman Requests
If you, like us, like to use postman to share your api internally you can use the following pre-request script to automatically sign your postman requests:
function guid() { function s4() { return Math.floor((1 + Math.random()) * 0x10000) .toString(16) .substring(1); } return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); } function getTimestamp() { var date = (new Date()).toISOString(); date = date.split("T"); date[1] = date[1].split(".")[0]; return date.join(' '); } postman.setEnvironmentVariable("x-signed-id", guid()); postman.setEnvironmentVariable("x-signed-timestamp", getTimestamp()); postman.setEnvironmentVariable("x-algorithm", "sha256"); var payload = { "id": postman.getEnvironmentVariable("x-signed-id"), "method": request.method, "timestamp": postman.getEnvironmentVariable("x-signed-timestamp"), "uri": request.url.replace("{{url}}", postman.getEnvironmentVariable("url")), "content": (Object.keys(request.data).length === 0) ? "" : JSON.stringify(JSON.parse(request.data)) }; var hash = CryptoJS.HmacSHA256(JSON.stringify(payload), postman.getEnvironmentVariable("key")); var signature = hash.toString(); postman.setEnvironmentVariable("x-signature", signature);
Note for this to work you'll have to setup your environment to have the following variables:
{{url}}
this is the base url to the api you'll be hitting.{{key}}
this is the shared secret key you'll be using in your environment.
All other environment variables that will be needed will be automatically generated by the above script.