Securing an API
February 10, 2011 2 Comments
- Write an Extension – allow BuildMaster to do new things by developing your own actions, providers, notifiers, etc.
- Use the BuildMaster API – create scripts or applications that pull data from, or tell BuildMaster to do certain things
While the extensions have been available since Day One (it’s actually how we implemented the included providers and actions), the BuildMaster API is relatively new. As you might imagine, there are a lot of considerations that go into developing a useful, long-term API, and one that I found particularly interesting was security.
Why Security is Important
We didn’t really have to worry about security with extensions: you either have permission to upload an extension into BuildMaster, or you don’t. There’s really no sense in getting more granular than that.
But the API is different. As a web application, BuildMaster is accessible by anyone on the intranet, and it’s secured by traditional, browser-based methods: integrated authentication, single-sign on, or a log-in form. None of these work very well with an API.
API Security Options
Obviously, leaving the API unsecured is not an option: a malicious developer could simply read the documentation, figure out what the API URL is (it’s /api/), and do whatever he wants. Security is a must, and there are a few ways to go about it.
- Protocol-based Security – use something like WS-Security to handle things “behind the scenes”. This would allow consumers of the API to write very straightforward code:
apiClient.Username = "userid";
applicationData = apiClient.Applications_GetApplication(appId);
Of course, this would mean that they’d either have to use some built-in SOAP client, or simply write one that added the following to SOAP header:
- Log-in/Token Security – add “meta” methods (login, logout) and secure all other methods unless those are called first. This would look something like this.
loginToken = apiClient.login("userid","secret");
applicationData = apiClient.Applications_GetApplication(loginToken, appId);
- Key-based Security – secure all methods by requiring that a key (known only to the server) is passed in first.
applicationData = apiClient.Applications_GetApplication("348dcvn2-sxn21l35n!24", appId);
Why We Chose Key-based Security
Requiring that a username and password are used to access the API presents a few interesting challenges, the largest being identifying who the actual user is.
Let’s say you build a desktop gadget that looks like a big red button and lets the user kick off a build. You’d either need to have the user enter his name/password, or hard-code a username/password (like “big-red-button-user”) to perform the action (and completely ignore any logging/auditing). Grabbing his username from the operating system environment doesn’t quite work, since you can’t get the password programmatically.
It’s equally challenging when you want to build a web-application. If you don’t prompt for a username and password, you’re stuck using a different user (and losing all of the auditing).
Add to that the additional complexity – WS-Security is no picnic (especially for consumers), and creating a token-based authentication system is yet-another subsystem to support – and options 1 and 2 become much less appealing.
That leaves us Option 3, Key-based Security. It’s incredibly simple to implement (if the key entered matches the server key, then you’re golden), and there’s only one vector of attack (the key) instead of every users’ name/password.
While the key is sacred, there is a very simple work-around if you don’t want users (on a desktop application) having it: create your own name/password web-service that wraps the BuildMaster API service. It may sound a bit convoluted, but since the method calls are available through WSDL, it’s more an exercise in code generation than tedium.
The method we chose to implement wasn’t any more “correct” than the other methods. But, for the users who consume our API, the simplicity of an API Key vastly outweighed the benefits of protocol- or login-based security.