Skip to main content

Security

When receiving an event notification request, it is very important to validate whether the request really came from Kobana and was not forged by a third party.

Intrusion Scenario

Let's consider that the integration you are developing is between Kobana and an e-commerce system. When receiving the bank_billet.paid event, which occurs when a boleto is paid, the e-commerce system releases the order for merchandise delivery to be made.

Imagine that a hacker discovers the URL of the e-commerce system that receives notifications and sends a forged notification, as if Kobana was sending it. In this case, the boleto being paid event did not happen, but your system will release the order anyway.

How to Protect Yourself

To protect yourself from this type of attack, it is necessary to implement validation before processing all requests, which certifies that the request was sent by Kobana.

All requests made by Kobana come with a signature in the X-Kobana-Signature header. The signature is an encrypted string based on the content of the request and the webhook Secret Key.

To validate whether the request is genuine, you need to generate the signature and compare it with the signature in the request header. If the received signature equals the generated signature, the request is valid and secure.

A hacker, without access to the Secret Key, cannot generate the signature and consequently cannot forge the request.

It is very important to keep the Secret Key safe, i.e., not putting it in the source code of the system. It is recommended to store it as an environment variable on the production server or in a secure and encrypted configuration system.

Anyone with access to the Secret Key is able to forge requests as if they came from Kobana. If you believe the Secret Key has leaked in any way, it is advisable to renew the key on the webhook data display page.

Webhook Secret Key

To get the Secret Key of the webhook, go to the Webhooks page in the Integrations -> Webhooks -> Accounts menu and select the webhook in question.

Click copy on the blue button.

Code Examples

# The example below is using the minimalist framework in Ruby,
# called [Sinatra](http://www.sinatrarb.com/).

require 'sinatra'
require 'json'

post '/callbacks/kobana' do
verify_signature

payload = JSON.parse(request_body)
"Event Code: #{payload['event_code']}"
end

def request_body
@request_body ||= request.body.read.to_s
end

def secret_key
ENV['WEBHOOK_SECRET_KEY']
end

def signature_from_request
request.env['HTTP_X_KOBANA_SIGNATURE'].split('=').last
end

def generated_signature
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret_key, request_body)
end

def verify_signature
return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature_from_request, generated_signature)
end

Some observations are important:

  • The signature is generated following the HMAC standard with SHA256;
  • The signature sent in X_KOBANA_SIGNATURE always starts with sha256= and the value after the = should be used in the comparison between our key and the key that will be generated by you;
  • The signature must be generated using the webhook Secret Key, which is individual and unique per webhook and per environment (Sandbox or Production), and the content (body) of the POST request sent in RAW (without any pre-processing by your server or lib) (request.body);
  • The Secret Key should not be hard-coded in the source code and it is recommended that it be stored in an environment variable;
  • It is not recommended to use the == operator to compare the received signature and the generated signature. Methods like Rack::Utils.secure_compare perform a secure comparison against some types of timing attacks. Research how to do secure comparison in the language you are using.