Webhooks are an ideal way to automatically send information from your Form Widget entries to an external server or service you control. It is important to ensure that your receiving server is configured so that it only accepts webhook requests from servers you trust.
Note: Implementing these security practices requires familiarity with server-side code. ShortStack's employees cannot provide support for specific configurations on your server(s).
The easiest way to secure your receiving server is to provide an optional secret key in your webhook settings.
Configure the webhook integration
- Open your Form Widget's webhook integration settings.
- Generate a new random string and paste it into the Secret Key field. Save this key for step 1 of the next section.
For example, you may copy/paste the output of this ruby command from your terminal:
ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'
- Save your webhook integration settings.
Configure your receiving server
ShortStack will use the secret key you provided to generate an encoded string that is sent to your server in the X-Ss-Signature request header. This encoded string (signature) is generated by combining the request body with the secret key you provide.
To validate the data sent to your server, you will generate your own signature and compare it to the signature in the X-Ss-Signature request header. ShortStack generates signatures using an HMAC hexdigest.
- Add an environment variable on your server to store your secret key. You can add a secret key to your server by typing the following into its terminal:
Never hardcode this key into your app or place the key into your version control system.
- Implement the signature verification on your web server. Note that the signature will always start with the text sha1=
The below example is written as a simple Sinatra-based web application.
require 'sinatra' # Listen for webhook events sent by POST requests at this server's '/webhook' path. # For example, this may be the configured Webhook URL within ShortStack: # http://www.example.com/webhook. Note that ShortStack cannot submit webhook requests # to locally hosted servers. # post '/webhook' do # The list entry data sent by ShortStack servers is located in the request body. # request.body.rewind entry_data = request.body.read # Create a signature using this receiving server's secret key and the # list entry's data it received. Note that the signature must be # prepended with "sha1=" # secret_key = ENV['SECRET_KEY'] digest = OpenSSL::Digest.new('sha1') hexdigest = OpenSSL::HMAC.hexdigest(digest, secret_key, entry_data) # The signature sent from ShortStack is prepended with 'sha1=', so your # signature must also be prepended with 'sha1=' for comparison # signature = 'sha1=' + hexdigest # Verify that the X-Ss-Signature header is encoded with the same # secret key that is stored on this receiving server by comparing # the 'signature' variable above against the signature received from # the ShortStack webhook request headers. If they do not match, this # server stops execution and returns a 500 error. # # Note that it is more secure to use a utility such as secure_compare # rather than the == operator # unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_SS_SIGNATURE']) return halt 403, "Signatures didn't match!" end # It is now safe to do something with the data. For example, print # the list entry's data to the console # puts entry_data end
Randomize the Endpoint's URL
Random URLs that are difficult to guess reduce (but do not eliminate) the likelihood of unauthorized individuals and applications discovering and abusing your receiving server. This implementation is not secure by itself, and is considered security through obscurity. Combine this method with IP filtering or the secret key method outlined above.
- Use a URL like this:
- Instead of a URL like this:
Whitelist requests from ShortStack's Campaign Server
Block all requests to your endpoint unless the request originates from this IP address: 220.127.116.11
We cannot provide exact instructions for IP filtering, but here are some places to start:
- PHP users will want to look into comparing $_SERVER['REMOTE_ADDR'] to the above IP address
- For Ruby users on rack-based systems, we recommend using the kickstarter/rack-attack ruby gem
- For other uses, please see the documentation for your web server, framework, or middleware