Friday, October 1, 2010

Cloud Security Architecture Matters

Not all cloud services are created equal. Clouds are architected by flesh and blood men and women, and because the are no cloud standards yet, the architectural choices are invariably different.
A couple of months ago I was looking for differences between various cloud vendors' API implementations, to show at a local OWASP meeting.

And differences I found. The most important difference is in the way users present their credentials to the service, and how the service ensures that it receives a valid request.

To make things comparable, let's look at the way 3 leading cloud vendors (Amazon Web Services, GoGrid and RackSpace) authenticate and authorize usage of their cloud resources.

We care about the way the service ensures that it receives a valid request, because we want to minimize the risk of account hijacking, and to minimize the risk of action replay.

A hijacked account means that a third party does unintended actions using resources that are tagged as belonging to the account owner. The third party can use the account for unwanted activity, such as distribution of illegal materials or promotion of spam, and the bill is handed out to the account owner.

Action replay means that someone may repeatedly send identical commands to the cloud infrastructure on behalf of the account owner. At best it is just a nuisance that may cost a little.
At worst, it may enable a third party to replace valid content with invalid, or even malicious content.

I am going to compare the basic action of listing the contents of the cloud objects container.
To make long things short, here is a brief description of the results.
  • All APIs make use of the HTTP protocol, with some custom extended headers.
  • All APIs require a public access identifier and a secret key shared between the account owner and the vendor.
  • AWS does not need the secret key in plaintext. It requires a unique signature for each request, making it extremely difficult to do any harm to the account, even if the request is somehow intercepted on the way.
  • GoGrid does not need the secret key in plaintext. It requires a signature as part of an authentication request, and returns a security token that is valid for a period of 10 minutes, and for all actions within 10 minutes. 
  • RackSpace requires the secret key in plaintext as part of an authentication request, and returns a security token that is valid for a period of 24 hours, and for all actions within 24 hours.  

Amazon S3 API security

AWS S3 requires a unique signature for each request, making it extremely difficult to do any harm to the account, even if the request is somehow intercepted on the way.
Here is how the HTTP request to list S3 buckets looks like:

GET / HTTP/1.1
Host: s3.amazonaws.com
Date: Fri, 05 Feb 2010 12:00:00 GMT
Authorization: AWS 15B4D3461F177624206A:xQE0diMbLRepdf3YB+FIEXAMPLE=

The authorization header is the key to the request. It is comprised of a constant "AWS", a public access key that is managed on the account's Amazon control panel, and a base64 encoded hash that is unique to this specific request.
In our case, the public access key is 15B4D3461F177624206A and the hash is xQE0diMbLRepdf3YB+FIEXAMPLE=

To better understand the hash, here is a pseudocode representation of the way it is built.
The SecretAccessKeyID is a 320 bit secret string that is shared between the account owner and AWS.
The end result is a hash that contains the MD5 encoding of the request, and all the request headers including a timestamp.
The timestamp must be within 15 minutes of the AWS internal time.


Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;

Signature = Base64( HMAC-SHA1( UTF-8-Encoding-Of( SecretAccessKeyID, StringToSign ) ) );

StringToSign = HTTP-Verb + "\n" +
    Content-MD5 + "\n" +
    Content-Type + "\n" +
    Date + "\n" +
    CanonicalizedAmzHeaders +
    CanonicalizedResource;

CanonicalizedResource = [ "/" + Bucket ] +
    <HTTP-Request-URI, from the protocol name up to the query string> +
    [ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"];

The end result is that each and every request has a unique signature, and there is no separate authentication and authorization step like with one of the following vendors.
 

GoGrid API security

GoGrid requires a signature as part of an authentication request, and the timestamp that must be encoded inside the signature makes the request valid for a period of 10 minutes. The same signature can be reused making it also valid for all actions within 10 minutes.  This makes the account vulnerable for 10 minutes at a time, but the requirement to use https mitigates the risk.
GoGrid uses a REST interface for its API.
Here is how the HTTP request chain to list GoGrid objects looks like:

https://api.gogrid.com/api/common/lookup/list
    ?lookup=lookups
    &sort=name
    &asc=true
    &api_key=SK1mndkKSjsALKSExdsj30
    &sig=Lsmdfj3kw99sdklnasdkjhds
    &v=1.1

The api_key is the public identifier, and the signature is a MD5 token created by a method like the php snippet listed below.

<?php
    $apikey = 'SK1mndkKSjsALKSExdsj30';
    $secret = ‘SharedSecretBetweenMeAndGoGrid';
    $timestamp = time();
    $sig = md5($apikey . $secret . $timestamp);
?>

RackSpace API security

RackSpace requires the secret key in plaintext as part of an authentication request, and returns a security token that is valid for a period of 24 hours, and for all actions within 24 hours. This method degrades the secret key to being just a simple password, and creates a double risk:
  1. The 24 hour validity of a security token is a very long period, during which the holders of the token can do whatever action they want to do with the account. 
  2. During login, the secret key is passed in plaintext in the request. Although the request must be over SSL, this still makes the RackSpace API the least secure one among its competitors.
Here is how the login session looks like:

GET /v1.0 HTTP/1.1
Host: auth.api.rackspacecloud.com
X-Auth-User: jdoe
X-Auth-Key: a86850deb2742ec3cb41518e26aa2d89


The response includes a number of tokens that must be used in subsequent requests. I color coded the relevant ones so it is easier to follow.


HTTP/1.1 204 No Content
Date: Fri, 05 Feb 2010 15:32:21 GMT
Server: Apache
X-Server-Management-Url: https://servers.api.rackspacecloud.com/v1.0/35428
X-Storage-Url: https://storage.clouddrive.com/v1/CloudFS_9c83b-5ed4
X-CDN-Management-Url: https://cdn.clouddrive.com/v1/CloudFS_9c83b-5ed4
X-Auth-Token: eaaafd18-0fed-4b3a-81b4-663c99ec1cbb
Content-Length: 0
Content-Type: text/plain; charset=UTF-8

After we successfully authenticated, we must use the security token from the X-Auth-Token header, and parts of the URL from the X-Storage-Url header.

The request to list cloud files looks now like this

GET /v1/CloudFS_9c83b-5ed4?format=json HTTP/1.1
Host: storage.clouddrive.com
X-Auth-Token: eaaafd18-0fed-4b3a-81b4-663c99ec1cbb


Summary

I think that security matters, therefore Amazon comes up on top in this comparison. This gives me yet another reason to use AWS for me and my clients.

No comments:

Post a Comment