Saturday, March 29, 2014

Leveraging Dwolla OAuth for User Authentication

Dwolla Forms, a Dwolla Labs project, uses Dwolla’s OAuth API to authenticate users rather than the standard email / password login scheme most web applications use today. Your app can too!

By relying on a third party like Dwolla to authenticate your users, you outsource the responsibility of username and password storage, retrieval, and verification. This lets you leverage all the hard work we’ve put into building a secure login system, for free! Of course, relying on a third party requires trust and faith in the accuracy and security of this third party authority. Rest assured, we’ve got our data locked down. (read more about security at Dwolla)

By lowering the amount of sensitive information you hold, you become a less tasty target for attackers. As another consequence, you’ll save your users from having to remember yet another username and password combination.

Overview

We can implement authentication via Dwolla OAuth in three easy steps:

  1. Obtain authorization to access a Dwolla user’s account information via OAuth
  2. Retrieve the user’s unique Dwolla ID using the Dwolla REST API’s Account Information endpoint
  3. Authenticate the user based on their Dwolla ID.

When a user signs up via OAuth for the first time, we retrieve and store their Dwolla ID with their user data. In the future, when that user attempts to sign in again, we point them to OAuth with Dwolla, and then we ask, in effect, “Hey Dwolla, what’s the Dwolla ID of the user I just redirected to you?” From there, we look up the corresponding user by their Dwolla ID and generate a login session for them.

Sample code!

Let’s look at how that works with some sample Node.js code. We’re using the express.js web application framework and nanek's node-dwolla package.

First, we’ll start off by creating a route which initiating OAuth:
    app.get('/auth/dwolla', function(req, res) {
      var authUrl = Dwolla.authUrl(redirectUri, "AccountInfoFull");
      return res.redirect(authUrl);
    })
After the user logs in and authorizes our Application, they’ll be returned to /auth/return, which is handled here:
    app.get('/auth/return', function(req, res) {
    var code = req.query['code'];
    $.waterfall(
      [
        // Exchange code for token
        function(callback) {
          Dwolla.requestToken(code, redirectUri, callback);
        }
        // Get user info
        , function(token, callback) {
          Dwolla.setToken(token);
          Dwolla.fullAccountInfo(function(err, user) {
            if(err) { return callback(err); }
            return callback(null, user);
          });
        }
        // If user is new, create a new user with Name and Dwolla ID returned by fullAccountInfo
        // otherwise, if they already exist, return the existing user object.
        , function(user, callback) {
          db.User
            .findOrCreate(
              {
                dwolla_id: user['Id']
              }
              , {
                name: user['Name']
                , dwolla_id: user['Id']
              }
            )
            .complete(function(err, user) {
              if(err) { return callback(err); }

              // log user in:
              req.session._user = user;
              return callback(null, user);
              }
            })
        }
      ]
      , function(err, results) {
        if(err) {
          return res.send('oh no!');
        }
        return res.render('nextpage');
      }
    )
  })
In this route, we first extract the verification code we get upon user redirect and exchange it for an OAuth token, thus completing the OAuth process. Then, we retrieve the user’s Dwolla account information with Dwolla.fullAccountInfo().

From the user object we get back, we’ll find an existing user account based on the user’s Dwolla ID, or create a new one, with the given user’s name and Dwolla ID. Finally, we’ll log the user in by attaching their user object to the current session and send them off to the next page.

Tuesday, March 25, 2014

Do the weaknesses of SHA-1 weaken the security of Dwolla’s API?

I'd like to share our answer to an interesting question regarding SHA-1 and the authentication scheme implemented by Dwolla's Off-Site Gateway Submit Directly flow:
"I'm looking at the developer stuff on checkout workflow, and see that the "signature" being transmitted between Dwolla and a business website is specified to use the SHA1 hash system. It is my understanding that that method is becoming vulnerable to an attack that has adequate computing power behind it. So, what other hash methods are allowable, for a business interacting with Dwolla? Thanks in advance!"
Though there are some weaknesses with SHA-1, they relate only to hash collisions. This means the weaknesses aren't helpful to attackers who are trying to determine the underlying input(s) of the hash. The only way to obtain the input(s) is by brute force.
In our case, the input of the signature hash is a concatenation of the Application Key, timestamp, and OrderID. These are all provided in the checkout form, so the attacker knows what the input is. What the attacker doesn't have is the Application Secret, which is a 50 character string used as the key in this key-based hash.
In order to forge a signature, the attacker would need to obtain the App Secret by way of brute force. If we assume the search space per character is all alphanumeric characters and 3 symbols ("+", "/", and "="), that leaves us with 65 possible characters. Since the secret is 50 characters long, we can say that an attacker would need to make
or
attempts to exhaust all possibilities.

Let's say an attacker can hash 10 million candidates per second on a single CPU. To exhaust all possibilities, it would take roughly:

Even if he has a large farm of machines running, say 1000 machines, with a collective power of 10 billion hashes/sec, it'd still take roughly:

The sheer size of the Secret string renders brute force an infeasible way to obtain it.  I would recommend reading Jeff Atwood's write up about hashes.

Sunday, March 16, 2014

Spotflux doesn't play nice with Tunnelblick

I recently gave Spotflux, a free VPN tunneling service, a try on my Mac.  I loved the experience -- extremely high speed and unlimited bandwidth, but I noticed that I could no longer use my beloved Tunnelblick VPN client.  When attempting to connect to a network with Tunnelblick, it errored out:

  openvpnstart returned with status #226

and left this in the log:

*Tunnelblick: openvpnstart log:
 Loading tun-signed.kext
 stderr from kextload: /Applications/Tunnelblick.app/Contents/Resources/tun-signed.kext failed to load - (libkern/kext) kext (kmod) start/stop routine failed; check the system/kernel logs for errors or try kextutil(8).
 stderr from kextload: /Applications/Tunnelblick.app/Contents/Resources/tun-signed.kext failed to load - (libkern/kext) kext (kmod) start/stop routine failed; check the system/kernel logs for errors or try kextutil(8).
 stderr from kextload: /Applications/Tunnelblick.app/Contents/Resources/tun-signed.kext failed to load - (libkern/kext) kext (kmod) start/stop routine failed; check the system/kernel logs for errors or try kextutil(8).
 stderr from kextload: /Applications/Tunnelblick.app/Contents/Resources/tun-signed.kext failed to load - (libkern/kext) kext (kmod) start/stop routine failed; check the system/kernel logs for errors or try kextutil(8).
 stderr from kextload: /Applications/Tunnelblick.app/Contents/Resources/tun-signed.kext failed to load - (libkern/kext) kext (kmod) start/stop routine failed; check the system/kernel logs for errors or try kextutil(8).
 Error: Unable to load net.tunnelblick.tun and/or net.tunnelblick.tap kexts in 5 tries. Status = 71

Apparently, the kext (driver) that Spotflux loads is incompatible with Tunnelblick and prevents Tunnelblick from loading its own kext. The solution is to unload Spotflux's kext and try connecting via Tunnelblick again.

Let's first run kextstat in Terminal to ensure we've got the offending kext loaded:
 
kextstat | grep spotflux
  121    0 0xffffff7f82231000 0x6000     0x6000     com.spotflux.Spotflux.tun

Then, let's unload it via kextunload.  This requires sudo.

sudo kextunload -b com.spotflux.Spotflux.tun

Once unloaded, Tunnelblick will be able to load its kext and connect as usual!  This will need to be done every time Spotflux is launched and you wish to use Tunnelblick afterwards, unfortunately.