1

我一直在尝试为我的 API 实现一个授权系统,该系统可以用于 Web 应用程序。我看过三足 OAuth 以及签名验证,但我对 OAuth 并不感兴趣。我正在测试的方法可以在这里找到。它使用 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. 有什么我看不到的刺眼的洞吗?可能是注意力不集中造成的。

谢谢。

编辑

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

这个答案似乎持有密码存储的答案,但不清楚如何使用 api 密钥/共享密钥。

编辑 2

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

4

1 回答 1

1

阅读这篇文章Javascript Cryptography Considered Harmful,我决定简单地使用 SSL。API 还将使用访问密钥和随机数。

于 2013-09-29T20:19:37.560 回答