June 30, 2016

Automated OneLogin Security Response with ThisData

In this tutorial we're going to learn how to use ThisData and OneLogin together, to better protect your users. You'll respond to account compromises by automatically logging out an attacker and locking the user account to prevent further access. This guide is targeted at IT administrators using OneLogin, but is equally applicable to app developers who support SAML Single Sign On (and Single Log Out).

Caveat: OneLogin are only able to notify you (or the ThisData API) of login events via email, according to their support team. If you're using your own system behind OneLogin that gets used during the authentication process, it will be simple to send that metadata to our API. If you're completely hosted on OneLogin, let us know how you'd approach the problem of tracking those events! We'll keep looking for solutions too.

Why? What is automated security?

Traditional response to compromised accounts is to wait for your employee or user to contact 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 you (and optionally your end user) in near-real-time. When your system receives the notification that something is amiss, an easy way to prevent further unauthorized activity is to reduce high-risk permissions, and/or terminate all current sessions and lock the account down.

With ThisData and OneLogin you can achieve an 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 OneLogin account. You'll need to be telling ThisData when users log in via OneLogin, using our events API. See the above caveat.

We'll learn about OneLogin's administrative API endpoints, then add some code to your app which listens and responds to ThisData webhooks by blocking malicious activity.

What's in OneLogin's API?

OneLogin is a cloud-based identity and access management platform - it lets you consolidate all your users and permissions in to one place, and provide a single source of authentication to the third-party service providers your employees use.

OneLogin's API allows you to create users, add and remove roles, terminate sessions, and lock user accounts down. The great thing here is that since all your SAML third-party services are talking to OneLogin, a single API call to log a user out will propagate across all those websites. If a security incident occurs you can quickly and easily ensure that they become inaccessible to an attacker.

Since all the third-party services are talking to OneLogin, a single API call to log a user out will propagate across all websites

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

When we detect an anomaly, or a threat is confirmed, we'll send you a webhook. So first you'll need to write some code to which ThisData can send its webhooks. For example, code which sits behind a URL like https://yourcompany.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. By default it's your API key.

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

  # The ThisData API key is also used to allow request verification
  # IMPORTANT Don't actually use hardcoded secrets or credentials.
  THISDATA_API_KEY = "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, THISDATA_API_KEY, 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 know our app is 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, using OneLogin's API. The was_user attribute null, because the user hasn't responded yet.

An appropriate reaction could be to temporarily revoke the user's administrative roles or editing permissions, so that they can't do any damage until their identity is confirmed. This is an example of continuous authentication, where the risk of the user influences their ability to perform actions, and this changes continuously and automatically.

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 for that user until the issue is resolved. Log the user out, and lock the account so the attacker can't log in again.

If was_user is true, then all is well. The activity has a good chance of being legitimate. If you've removed any user roles or permissions, you'll want to re-instate them.

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 temporarily remove roles which could do damage.
  OneLogin.findUser(112233).remove_roles("admin", "sales")
else if webhook.was_user == false  
  // It was NOT the user. Account compromised. Immediately block access
  OneLogin.findUser(112233).lockAccount
  OneLogin.findUser(112233).logout
else if webhook.was_user == true  
  // It *was* the user. Remove any previous rules
  OneLogin.findUser(112233).add_roles("admin", "sales")

3) Use OneLogin's API to Log Out and Lock Out a User

OneLogin provides API access for logging out users, and for locking user accounts. Here's an example where we respond to confirmed compromises by doing exactly that. 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 this example is 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.
  THISDATA_API_KEY = "564bfa2656d8"
  ONELOGIN_ACCESS_TOKEN = "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
      headers = {
        "Authorization: bearer #{ONELOGIN_ACCESS_TOKEN}",
        "Content-Type: application/json"
      }
      # The base URL for these user requests
      user_url = "https://api.us.onelogin.com/api/1/users/#{payload["user"]["id"]}"

      HTTParty.put("#{user_url}/lock_user", body: '', headers: headers)
      HTTParty.put("#{user_url}/logout",    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 of unusual account access, and when the user confirms their account has been compromised, you automatically block further malicious activity using OneLogin's API.

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

Related Resources:

YOU MAY ALSO BE INTERESTED IN

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 ...

Cloudbleed - ThisData's Response

Late last week Cloudflare announced that a pretty serious bug had been found in the way they handled their traffic. The bug allowed private ...