Building a Secure API in a Cloud Environment
Introduction
In the last couple of years there has been a boom in cloud computing, but mainly just the term is new, as we've been using cloud services for years without even realizing it. Almost every cloud, whereas it's Saas, PaaS or IaaS, implements some kind of API (Application Programming Interface), which can be used by developers to interact with the service programmatically. APIs can be used for a number of things, like pulling data out of the database, sending data to be stored in the database, pushing jobs to a queue, etc.
OpenStack is a cloud computing system that uses a dashboard to manage every component of the system that further uses APIs to do its thing. Let's take a look at the picture below taken from [1] that presents the basic overview of the components used in OpenStack, which are connected via the APIs.
Cloud APIs are basically programing interfaces embedded into the cloud system, which a programmer uses to instruct the cloud into performing some action. When a cloud service provides an API, it means we have all the advantages of an API available, so we can automate many tasks we would normally have to do by hand: imagine that we have to apply some action to all our virtual machines running in the cloud – if there are just a few virtual machines, it wouldn't be a problem to just click the button, which performs the action we would like to execute. But what if there are thousands of virtual machines, or if there isn't a single button to perform the needed action, but we have to press multiple buttons and switch between panes to do it? In such situations, it would be very painful to do this by hand, so API comes to the rescue, enabling us to automate most of the stuff.
Methodologies
Before going any further with this article, we need to explain that when planning an API, we have to think about the overall picture. Whenever we send a message over a communication channel, we're using some kind of protocol or a set of rules that must be known both to the sender as well as the recipient to be able to process the message correctly. Whenever we're concerned about securing the API, we need to send and receive messages over a secure network connection: we might use a secure HTTPS connection rather than an insecure HTTP one; and it's not limited only to HTTP and HTTPS, but we might be sending data over other protocols as well: POP(S), IMAP(S), SMTP(S), LDAP(S), XMPP(S), etc. Remember to use the secure version of the protocol (with the letter 'S' in its name) when we want to encrypt the data we're sending out on the Internet: only the sender and recipient should be able to read the message in cleartext.
The other thing is data representation or format: it doesn't really matter how the message is structured or formatted as long as both endpoints understand it. Usually, the APIs are exchanging messages in XML or JSON format, or possibly both are supported, which gives users more flexibility when interacting with the application. Whichever format we're using to exchange data between the two endpoints, we must properly secure them against possible attack vectors. When dealing with an API where the communication is done over HTTP(S), we need to check and test the application for known web application injection flaws, like CSRT, XSS, SQL injection, schema validation, etc.
Most types of APIs are the following:
- REST (Representational State Transfer): REST is an architecture, not a protocol, built upon HTTP protocol, where URI requests are used to reference resources by using GET, POST, PUT and DELETE HTTP methods. An important concept to grasp is the following: requests need to be authorized based on resource content and not URLs, because when we change the application, the URLs will change as well, and all of a sudden our security policy is not valid anymore: URLs play a minor part in the application, and we shouldn't build security on them. Every security feature we would like to have in REST has to be manually developed, since none of it is built-in. Usually REST API is simpler to learn, which is why many developers are using it to build their own API to extend their application.
- SOAP (Simple Object Access Protocol): is a protocol defining the exchange of structured data over the network [4]. Usually it uses HTTP(S) for message transmission and XML for data representation or message format. If HTTP(S) is used, it uses GET for reading and POST for writing data. There's also a WS-Security, which is an extension to SOAP and provides guidelines for solving the following security problems [5]: identify and authentication of a client, ensure integrity of the message, and prevent eavesdropping of messages in transit.
- XML-RPC: uses XML based messaging format and HTTP communication protocol.
-
JSON-RPC: uses JSON based messaging format and HTTP communication protocol.
Identification Techniques
When developing a brand new application programming interface, we need to address the following issues:
- Identity: when starting to interact with a server that doesn't initially know who we are, we must prove our identity; we need to provide the answer to the question, "Who are you?" Usually, we must provide a user ID or a public key, which uniquely identifies us; note that the provided information is public, so anybody can identify as us, but he won't be able to prove it.
- Authentication: here we have to prove that we are who we say we are by answering the question, "Can you prove you are who you say you are?" The server will send us a challenge, to which we must respond with something that's known only to us, let it be a password, a private key, a token, etc.
-
Authorization: when we've proven our identify, we can request access from the application we're interacting with. At that time, the application needs to check whether we're allowed to access the requested resource.
Below we can see a number of techniques an application can use to identify and authenticate users.
- Username/Password: we're sending username and password to the endpoint application to identify and authenticate ourselves. An example is basic authentication, where we connect to the server, which responds with a WWW-Authenticate HTTP header, which contains a scheme the server wants us to authenticate against. Whenever servers sends us a WWW-Authenticate HTTP header, they want us to authenticate, and we have to respond with the Authorization HTTP header with the username/password key-pair being base64 encoded. Since the base64 encoded string can be easily decoded to get its plain text, if basic authentication is used, the communication must be encrypted.
- Sessions: at first we send a username and password to the application to get back a cookie, which is sent in all subsequent requests to establish an authentication session.
- Certificates: an application might use public/private PKI infrastructure to authenticate users, which requires a valid server and client certificates that are signed by a valid certificate authority, which can prove the validity of the certificate.
- OAuth: has gained a lot of popularity over the years and is mainly used when an application uses another application on behalf of the user. If we're writing an application that implements the share button to Facebook or Twitter, we're most probably using OAuth authentication, which grants the application access to the Facebook/Twitter data without revealing the password of the application itself. Therefore, the application doesn't need to provide username/passwod authentication, but OAuth only.
- Custom Authentication Scheme: we can use custom authentication scheme to identify, authenticate and authorize users using whatever we prefer and customize it all the way down to the core: we should only do so if we really know what we're doing.
- API Key: when API keys are in used, the identity and authenticity can be proven directly in the first request sent to the server without prior authentication with a username/password or a private key – this can have a minor advantage of saving some bandwidth, but isn't really crucial, since Internet connections are really fast nowadays. An API key is a simply a very long, unique token that's known only to the application running on the server-side as well as the client sending an API request. The items below present why API keys are superior to some other authentication schemes we outlined previously.
- Entropy: when auto-generating the API key, which is a long ID, the entropy is significantly larger when compared to username/password, which usually has between 5-15 characters, while the API key has a much larger entropy space because of larger string as well as characters being randomly selected. Thus, the API key has a significant increase in the randomness, which makes it harder to bruteforce and therefore more secure.
- Password Change: when we change the password through the web interface, because we've forgotten it or we simply want to be compliant with some security policy and change it every so often, the clients that use the API need to be reconfigured to support the password change. If API key is used, the password can be changed regardless of the API key, which remains the same.
- Speed: usernames and passwords are usually stored in a form of MD5/SHA1 hashes in the backend database. When the user authenticates with them to the application, a hash is calculated and compared to the value stored in the database. If the hashes match, access is granted, otherwise it's not. MD5/SHA1 hashes have known collision attacks, which lowers their security, but there are other methods to attack a hash: rainbow tables, powerful specialized hardware components, advanced software algorithms, cloud cracking farms, etc. Such attacks are not really relevant when API keys are involved, because API keys are just strings that are compared upon each request. Since API keys are normally quite long, they can't be bruteforced in real-time, which eliminates the attack vector considerably. Also consequentially just a string, which is known by the server application as well as the client, can be bruteforced easily. Note that usernames and passwords can be just as difficult to crack if proper slower algorithms like Blowfish, Bcrypt or SHA512 are used. Why slower algorithms, shouldn't we strive towards faster algorithms and web applications? In this case no; when using a slower algorithm to compute a hash of some kind, it means that when an attacker is bruteforcing the hash, it will also slow his attack considerably, which is a good thing to prevent hackers from cracking our hashes in realtime.
- Exposure: when we generate an API key, we immediately download it from the web page and save it into a file on our filesystem, but it isn't so with passwords, which are not handled with so little fuss.
-
Traceability: with API keys, we always know which request was issued by which client, because of the used unique API. This isn't so with usernames and passwords, which are often shared among many users and sent in emails in cleartext – because of that they can also be stolen by the attackers easily.
After the development of the API, we have to properly test it. Since API is most often built on HTTP(S) there are also standard web application threats lurking in the background. Each of the chosen techniques outlined in this article also has its share of vulnerabilities we must be careful enough to not overlook. OWASP association provides many documents that outline the vulnerabilities in chosen architecture like [6], but there are so many possible vulnerabilities existing in an API that it's better if a professional penetration tester or security consultant checks the application/code for vulnerabilities. We should also do a penetration test on a regular basis to minimize the number of bugs still residing in our code. In the end, it all depends on how the code is written, but we have a standard principle of never trusting the users data that still applies. To build a safer API, we might even apply SDLC (Secure Development Life Cycle) into the development process.
Conclusion
When choosing a cloud service provider, we'll most likely be using its API sooner or later to automate actions. When choosing the CSP, one of the things to check for is the proper documentation and security of its API. If the CSP has good documentation of its API, that's the first good sign that something is right, but otherwise it might be the opposite. In addition to that, CSPs need to allow each customer to hire a penetration tester to test its API, or they have to hire a security consultant or penetration tester themselves. When developing our own API, we must take same security precautions and make sure the API is thoroughly tested for security bugs.
References:
[1] OpenStack: The Open Source Cloud Operating System,
https://www.openstack.org/software/.
[2] Do you need API keys? API Identity vs. Authorization,
https://blog.apigee.com/detail/do_you_need_api_keys_api_identity_vs._authorization/.
[3] It's Me, and Here's My Proof: Why Identity and Authentication Must Remain Distinct,
http://technet.microsoft.com/en-us/library/cc512578.aspx.
[4] SOAP,
https://en.wikipedia.org/wiki/SOAP.
[5] Securing Your API, Jason Austin,
http://www.slideshare.net/jfaustin/securing-your-api.
[6] REST Security Cheat Sheet,