将HMAC与Web App和API结合使用

时间:2013-08-28 11:41:56

标签: php api security oauth hmac

我一直在尝试为我的API实施一个授权系统,该系统可以用于Web应用程序。我看过三条腿的OAuth以及签名验证,但我对OAuth并不感兴趣。我正在测试的方法可以找到here。 它使用HMAC授权签署类似于Amazon Signature的请求。我刚刚在这里拼凑了一个工作示例。没什么好看的。只是一个概念证明。

客户端使用CryptoJs和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);
        }
    }); 

为服务器使用slim php框架和php 5.4。

         


    //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

显然,这有问题。 我能看到的是

  1. 如何存储用户数据服务器端。我无法存储明文密码,我无法存储密码哈希并存储用户的密钥是在惹麻烦。我似乎无法解决这个问题。

  2. 当应用程序在内存中运行时,是否可以存储密钥客户端,以便无法获取?显然,使用像firebug这样的工具或者带有chrome和firefox的webdev工具,你可以得到它。但是可以将它嵌入代码中,甚至可以在匿名函数中嵌套,这样你就无法轻易获得它。我不是很担心,因为它将是我自己的应用程序正在运行。

  3. 申请该请求的适当超时是什么?

  4. 我看不到任何明显的漏洞吗?也许是因为注意力不集中而失明。

  5. 感谢。

    修改

    正如我所说,这只是一个证据,我忘了添加请求方法/动词也将被添加到哈希。

    This answer似乎掌握了密码存储的答案,但目前尚不清楚如何使用api密钥/共享秘密。

    编辑2

    我在这里看到的另一个问题是允许用户在其他消费者应用程序上输入密码。一个好的解决方案是使用某种API密钥或共享密钥,但是对此有任何想法吗?

1 个答案:

答案 0 :(得分:1)

阅读本文Javascript Cryptography Considered Harmful,我决定只使用SSL。 API还将使用访问密钥和随机数。