June 15, 2016

Automate your CloudFlare firewall with ThisData

In this tutorial we're going to learn how to use ThisData and CloudFlare together, to better protect your users. You'll respond to account compromises by blocking (or "challenging") further malicious activity.

Why? What is automated security?

Traditional response to compromised accounts is to wait for your user to email or ring you in a panic, after the damage has been done. Industry standard is moving towards automated security, where you remove your people from the equation as much as possible.

ThisData's login intelligence system will notice when something suspicious is going on, and notify your web app (and optionally your end user) in near-real-time. When your system receives that notification, an easy way to prevent further unauthorised activity is by blocking that website visitor. That's where CloudFlare comes in.

With ThisData and CloudFlare you can achieve a automated security response which is magnitudes faster, with a greatly reduced burden for your support and ops teams.

Let's do it!

Before you start you'll need a ThisData account and a CloudFlare account.

We'll learn about CloudFlare's access rules, then add some code to your app which listens and responds to ThisData webhooks by blocking malicious activity.

What are CloudFlare Access Rules?

CloudFlare is a service which can protect your web-based business from cyber attacks, as well as improve its performance. Part of their service works by inspecting each request to your site and evaluating it against a set of Access Rules. They're a way to allow, challenge, or block requests to your website based on the IP address of the browser making the request. "Challenging" a request can take the form of a CAPTCHA which a visitor must successfully complete, or an interstitial page they call "Javascript Challenge" which makes the browser perform some complicated calculations.

What's a webhook?

A webhook is how one system (like ThisData) talks to your system when an event happens. Usually it's a special URL set aside, and receives POST requests with a JSON payload containing relevant event details.

You can read more about ThisData's webhooks here, but I'll explain as we go :)

1) Listen for webhooks in your app

First, you'll need to write some code in your app to which ThisData can send its webhooks. For example, code which sits behind a URL like https://yourapp.com/webhooks/thisdata/receive. Different web frameworks implement this in different ways.

You'll also want to verify that the requests you receive actually come from ThisData. Otherwise anyone could send a request at it! This is achieved by having a shared secret between your app and ThisData, which ThisData will use to create an encoded signature. Pick something long and random.

If you're using Ruby on Rails, here's what your code might look like so far:

Tell your app to listen for requests at yourapp.com/thisdata/receive

/config/routes.rb

Rails.application.routes.draw do  
  # ... your other routes ...

  namespace :webhooks do
    post  'thisdata/receive'
  end
end  

Have a controller action, and verification of that webhook

/app/controllers/webhooks/thisdata.rb

class Webhooks::ThisdataController < ApplicationController  
  # We'll verify requests ourselves, and don't need Rails' CSRF-based checks
  protect_from_forgery with: :null_session
  skip_before_filter :verify_authenticity_token

  # Before we do anything, verify the signature is correct!
  before_filter :verify_request_signature

  # A shared secret, used to verify request legitimacy
  # IMPORTANT Don't actually use hardcoded secrets or credentials.
  WEBHOOK_SECRET = "564bfa2656d8"

  # Receives and responds to a webhook from ThisData
  def receive
    # This is where we take action, assuming the verification succeeds.
    # ... we'll do this soon ...
  end

  protected

  # Verify the ThisData webhook request signature
  def verify_request_signature
    body = request.body
    digest = OpenSSL::Digest.new('sha512')
    expected_signature = OpenSSL::HMAC.hexdigest(digest, WEBHOOK_SECRET, body)
    actual_signature = request.headers["X-Signature"].to_s

    if ActiveSupport::SecurityUtils.variable_size_secure_compare(
        expected_signature, 
        actual_signature
      )
      logger.info "ThisData webhook signature verified"
      true
    else
      logger.warn "ThisData webhook signature could not be verified"
      head :unauthorized
      false
    end
  end
end  

At this point we now our app listening for, and verifying the authenticity of, webhooks from ThisData. It's time to actually do something when we receive them!

2) Decide how to respond

We need to decide how to react to one of ThisData's webhooks. In the body of every webhook is an attribute called was_user, which represents the user's response to an alert. (Learn more about ThisData "Was This You?" notifications).

When unusual activity is first detected

ThisData will send a webhook when unusual activity is detected. This is the first opportunity to take action by creating a CloudFlare rule. The was_user attribute null, because the user hasn't responded yet. An appropriate reaction could be to force a challenge to visitors from that suspicious IP, as this will block bots and automated attacks.

When the user responds No or Yes

ThisData will send another webhook, again with was_user. If it's false, then the user has said "No, that activity was not me". This indicates their account has been compromised. An appropriate response would be to block all further activity from that IP address until the issue is resolved. (The downside is that will prevent that IP from accessing your site entirely - if it was a mistake the user mightn't be able to find out how to contact the support team).

If was_user is true, then all is well. The activity has a good chance of being legitimate. If you've placed a "challenge" rule for that IP address, you'll want to remove it.

Example

If you wanted to respond to all three cases, some pseudo-code might look like this:

if webhook.was_user == null  
  // ThisData has detected an anomaly. Let's tentatively challenging all requests from this IP.
  CloudFlare.createAccessRule(ip: webhook.ip, mode: "challenge")
else if webhook.was_user == false  
  // It was NOT the user. Account compromised. Immediately block access
  CloudFlare.createAccessRule(ip: webhook.ip, mode: "block")
else if webhook.was_user == true  
  // It *was* the user. Remove any previous rules
  CloudFlare.removeAccessRule(ip: webhook.ip)

3) Use CloudFlare's API to create an Access Rule

CloudFlare provide API access for creating, updating, and deleting Access Rules. We'll apply our rule to all the domains in our CloudFlare account, but you can do it on a per-domain basis if you want.

Here's an example where we respond to confirmed compromises by blocking that IP address. This means that as soon as a user confirms an account compromise, the attacker is immediately unable to make further requests to your app. Again it's using Rails, and the HTTParty gem.

/app/controllers/webhooks/thisdata.rb

class Webhooks::ThisdataController < ApplicationController  
  # ...
  before_filter :verify_request_signature

  # IMPORTANT! Don't actually use hardcoded secrets or credentials.
  WEBHOOK_SECRET           = "564bfa2656d8"
  CLOUDFLARE_EMAIL_ADDRESS = "[email protected]"
  CLOUDFLARE_API_KEY       = "8d6562afb465"

  # Receives and responds to a webhook from ThisData
  def receive
    payload = JSON.parse(response.body)

    if payload["was_user"] == false

      # It was NOT the user. Account compromised. Immediately block access
      url = "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules"
      headers = {
        "X-Auth-Email: #{CLOUDFLARE_EMAIL_ADDRESS}",
        "X-Auth-Key: #{CLOUDFLARE_API_TOKEN}",
        "Content-Type: application/json"
      }
      body = {
        mode: "block",
        configuration: {
          target: "ip",
          value: payload["ip"]
        },
        notes: "[ThisData] Rule created for Alert #{payload["alert"]["id"]} after user confirmed compromise"
      }.to_json

      HTTParty.post(url, body: body, headers: headers)
    end
  end

  # ...
end  

You'll probably want to move that response to a background job, add error handling and retries, etc. But you've done the hard work - great job!

3) Tell ThisData where to send webhooks

Now that your app is ready to automatically take action when notified of account compromises, add your webhook endpoint to ThisData's API Settings page. Remember to include a secret, so that you can verify the webhooks.

Screenshot of ThisData's API settings Section

4) Done!

Well done! Your app now uses ThisData to monitor and alert users to unusual account access, and when the user confirms their account has been compromised you automatically block further malicious activity.

Let us know about your experiences automating your security workflow in the comments below.

Related Resources:

YOU MAY ALSO BE INTERESTED IN

The future of authentication

Today I’m excited to announce a deal that we have been working on for the past few months and how that will impact the future of contextual ...

Introducing custom security rules

For the past few years we’ve been working hard to create a plug and play adaptive risk engine. We designed our core service using a mix of b ...