0

Explain It To Me Like I'm Five, lol. As a web developer I only worked with the traditional websetup: I got a Java application, I deploy it to a server somewhere, and connect to/test it with a web browser. If sensitive information needs to be sent back and forth, like passwords for example, an SSH server is used.

Well now I have written a RESTful App in Java Play! that I want to deploy onto an Amazon EC2 instance. I have not been able to wrap my head around the concept of PCI security and how I can apply it to my setup. In this case I want the rest app to allow for administrative functions so adding login credentials to the requests submitted to the various REST services is needed.

What is the best way to do this on the amazon EC2 side and the client side (a Jersey-based Client).

IcedDante
  • 6,145
  • 12
  • 57
  • 100

2 Answers2

1

Generally speaking its a bad idea to just require a username password in the URL. Even if you're using SSL the URL and all its query parameters are not secure. If you want your feeds to be secure you could sign your requests with a private key using a hashing method such as HMAC-SHA1 or HMAC-SHA256. It works something like this.

The client needs two keys a public key and private key

PublicKey: ABCDJJJZ7BW9P0IH2NI3
PrivateKey: uFehWXvcM7mf==c8ZhOe3Fz+6d+zyQ2ja4A3De1N

Build a string from the verb, url, headers and body. It's recommended that you use SSL or include an Expire parameter to the url to prevent any hackers from effectively performing a replay attack. Make sure the date and host are included in the headers, the date is very important to make sure that the signature is always changing.

verb + \n
url + \n
header1: value + \n
header2: value + \n
body

Then the client signs the request using the hashing method described above. The pseudo code would look something like this.

base64encode(hmac-sha1($private_key, $string_to_sign))

The a header is appended to the request that contains the signature and public key

Authorization: $public_key:$signature

When the server received the message they retrieve the private key for the user using the public key received in the Authorization header. Then the server performs the same algorithm and if the two signatures match the server knows that the request is authenticated because only a client with the correct public and private key can generate the same signature.

Here's a snipit of PHP code using this algorithm to sign a request.

public function sign() {
    $time = time();
    $date = gmdate(self::DATE_FORMAT_RFC2616, $time);
    $verb = $this->payload['verb'];
    $headers = $this->payload['headers'];
    $query = $this->payload['query'];
    $body = $this->payload['body'];

    // Build the content string
    $endpoint = $this->endpoint;        

    // Set the expiration time of the request
    if($this->expires != 0 && !isset($query['Expires'])) {
        $query['Expires'] = $time + ($this->expires * 60);
    }

    // Add the date to the request headers
    $headers['date'] = $date;

    // Add the host to the request headers 
    $headers['host'] = $this->host;

    // Sort the query array and convert to querystring
    uksort($query, 'strnatcasecmp');
    $querystring = SUUtilities::to_query_string($query); 

    // Sort the headers to be sure they're in the right order
    $modified_headers = array();
    uksort($headers, 'strnatcasecmp');

    // Concatinate the headers so the key and value are a single string
    $include = array('date', 'host');
    foreach($headers as $key => $value) {
        if(in_array($key, $include) || substr($key, 0, 5) == "x-sbr") {
            $modified_headers[] = preg_replace('/\s/', '', strtolower($key)).": ".$value;
        }
    }

    // Build the string to sign
    $string_to_sign = $verb.PHP_EOL
        .$endpoint."?".$querystring.PHP_EOL
        .implode(PHP_EOL, $modified_headers).PHP_EOL
        .$body;

    // Create the signature for the request
    $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->credentials['private_key'], true));

    // Add the authorization header to the list of headers.
    array_push($modified_headers, 'authorization: SBR '.$this->credentials['key'].':'.$signature);

    $request = array();
    $request['headers'] = $modified_headers;
    $request['host'] = $this->host;
    $request['endpoint'] = $endpoint;
    $request['query'] = $query;     

    return $request;
}

Additional resources:

bwight
  • 3,300
  • 17
  • 21
  • 2
    "Even if you're using SSL the URL and all its query parameters are not encrypted" do you have any reference to back that up? Because I'm fairly certain [the opposite is true](http://stackoverflow.com/questions/2629222/are-querystring-parameters-secure-in-https-http-ssl) – Razor Feb 05 '13 at 15:17
  • You're right, I should have said it's not secure not that its not encrypted it is indeed encrypted. I've edited my answer to read correctly. – bwight Feb 05 '13 at 15:23
  • Thanks for this reply: I'm going to investigate this some more, check out the links you have provided and give you a much deserved check – IcedDante Feb 07 '13 at 22:04
0

If your client drops a cookie you could have your REST service check for the cookie and return Forbidden if the cookie is not there. Another approach would be to require a parameter that takes a dynamic piece of the URL (something that changes with most requests) and SHA encode it with a private key known to your REST service.

Todd Flanders
  • 383
  • 2
  • 12