Reputation: 2805
I have been trying to implement a system of authorisation for my API which would be able to work for a web app. I have looked at three legged OAuth as well as signature verification, but I am not really interested in OAuth. The method I am testing out can be found here. It uses HMAC to authorize sign the request similar to Amazon Signature. I have just cobbled together a working example here. Nothing really fancy. Just a proof of concept.
Client side uses CryptoJs and JQuery
//Dummy credentials
var username = 'username';
var password = 'password';
//first create the key and store it in memory for the duration of the app
var KEY = CryptoJS.SHA512(username + "." + password).toString();
var url = "http://127.0.0.1/whisppa/api";
//setup the parameters that will be sent to the server
var params =
{
"user" : username,
"requested_url" : CryptoJS.MD5(url).toString(),//hash the url to make it shorter
"signature_version" : "1.0",
"time" : 0
}
//get all parameters and sort them
var props = [];
for(var prop in params)
{
props.push(prop);
}
props.sort();
//concatenate the parameters as a parameter string and set the time value
var param_string = "";
for(var prop in props)
{
prop = props[prop];
if(prop == "time")
{
params.time = Math.round(new Date() / 1000);
}
param_string += prop + "=" + params[prop] + "&";
}
param_string = param_string.substring(0, param_string.length - 1);
//generate the hmac of the request to be set in the header
var hmac = CryptoJS.HmacSHA512(param_string, KEY).toString();
//make the request
$.ajax({
type:"GET",
beforeSend: function (request)
{
request.setRequestHeader("Auth-KEY", hmac);
},
url: url,
data: "params=" + escape(JSON.stringify(params)),
processData: false,
success: function(msg) {
$('body').html(msg);
}
});
Using slim php framework and php 5.4 for the server.
//define the time before the app starts
define('TIME', round(microtime(true)));
$headers = getallheaders();
//just make a few checks here to ensure necessary params are set
if(!isset($_GET['params']) || !isset($headers['Auth-KEY']))
{
$app->halt(400);
}
//get the parameters from the get string
$params = json_decode($_GET['params']);
//make some more checks for important parameters
if(!isset($params->time) || !isset($params->user))
{
$app->halt(400);
}
//get the parameters and sort them
$properties = get_object_vars($params);
ksort($properties);
//concatenate the parameters as a parameter string
$param_string = '';
foreach($properties as $prop => $value)
{
$param_string .= $prop . '=' . $value . '&';
}
$param_string = substr($param_string, 0, strlen($param_string) - 1);
//in reality, fetch the user from the database
$password = 'password';
//create the hash and then generate the HMAC
$KEY = hash('SHA512', $params->user . '.' . $password);
$Auth_KEY = hash_hmac('SHA512', $param_string, $KEY);
//verify the request
if($Auth_KEY == $headers['Auth-KEY'])
{
$app->halt(401);
}
//verify the time
if(TIME - $params->time > 60)
{
$app->halt(408);
}
//TODO: verify the requested url matches the current url
Obviously, there are problems with this. What I can see is
How to store the user data server side. I can't store the plaintext password, I can't store the password hashed and storing the user's key is asking for trouble. I can't seem to get around this issue.
Is it possible to store the key client side while the app is running in memory such that it can't be gotten at? Obviously, using a tool like firebug or the webdev tools that come with chrome and firefox, you can get at it. but is it possible to nest it in code so deeply or even in an anonymous function such that, you are unable to get at it easily. I am not very worried about this though since it will be my own app that is running.
What is an appropriate timeout to apply to the request?
Are there any glaring holes that I can't see? Maybe due to inattention blindness.
Thanks.
EDIT
As I said, this is just a proof, I forgot to add that the request method/verb will be added to the hash as well.
This answer seems to hold the answer to password storage, but it is not clear how to use api keys/share secret.
EDIT 2
Another issue I see here is allowing users to enter their passwords on other consumers applications. A good solution will be to use some sort of API keys or Shared secret, but then any ideas on this?
Upvotes: 1
Views: 2032
Reputation: 2805
Reading this article Javascript Cryptography Considered Harmful, Ive decided to simply use SSL. API will also use access keys and nonces.
Upvotes: 1