【问题标题】:Cant get digest auth to work with node.js无法获得摘要身份验证以与 node.js 一起使用
【发布时间】:2019-07-26 21:29:35
【问题描述】:

我正在尝试使用来自gathercontent.com 的 API 使用节点 js 进行简单的(!)摘要身份验证。

似乎一切正常,但我仍然收到如下所示的“凭据错误”响应:

{ success: false, error: 'Wrong Credentials!' }

代码如下所示:

var https = require('https'),
    qs = require('querystring');
apikey = "[my api key goes in here]",
    pwd = "[my password goes in here]",
    crypto = require('crypto');


module.exports.apiCall = function () {

    var options = {
        host:'abcdefg.gathercontent.com',
        port:443,
        path:'/api/0.1/get_pages_by_project/get_me',
        method:'POST',
        headers:{
            "Accept":"application/json",
            "Content-Type":"application/x-www-form-urlencoded"
        }
    };

    var req = https.request(options, function (res) {

        res.on('data', function (d) {
            var creds = JSON.parse(d);


            var parsedDigest = parseDigest(res.headers['www-authenticate']);
            console.log(parsedDigest);
            var authopts = {
                host:'furthercreative.gathercontent.com',
                port:443,
                path:'/api/0.1/get_pages_by_project/get_me',
                method:'POST',
                headers:{
                    "Accept":"application/json",
                    "Content-Type":"application/x-www-form-urlencoded",
                    "Authorization" : getAuthHeader(parsedDigest, apikey, parsedDigest['Digest realm'], pwd)
                }
            };

            console.log(authopts);
            console.log('\n\n\n');
            var req2 = https.request(authopts, function (res2) {
                console.log("statusCode: ", res2.statusCode);
                console.log("headers: ", res2.headers);


                res2.on('data', function (d2) {
                    var result = JSON.parse(d2);

                });
            });

            req2.end();



        });

    });


    req.write('id=1234');

    req.end();

    req.on('error', function (e) {
        console.error(e);
    });

};

function parseDigest(s){

    var parts = s.split(',');
    var obj = {};
    var nvp = '';

    for(var i = 0; i < parts.length; i++){

        nvp = parts[i].split('=');
        obj[nvp[0]] = nvp[1].replace(/"/gi, '');
    }



    return obj;
}

function getAuthHeader(digest, apikey, realm, pwd){
    var md5 = crypto.createHash('md5');
    var s = '';

    var nc = '00000001';
    var cn = '0a4f113b';

    var HA1in = apikey+':'+realm+':'+pwd;
    md5 = crypto.createHash('md5');
    md5.update(HA1in);
    var HA1out = md5.digest('hex');

    var HA2in = 'POST:/api/0.1/get_pages_by_project/get_me';
    md5 = crypto.createHash('md5');
    md5.update(HA2in);
    var HA2out = md5.digest('hex');

    md5 = crypto.createHash('md5');
    var respIn = HA1out + ':' + digest.nonce + ':'+nc+':'+cn+':'+digest.qop+':'+ HA2out;
    md5.update(respIn);
    var resp = md5.digest('hex');


    s = [   'Digest username="',apikey,'", ',
        'realm="',digest['Digest realm'],'", ',
        'nonce="',digest.nonce,'", ',
        'uri="/api/0.1/get_pages_by_project/get_me", ',
        'cnonce="',cn,'", ',
        'nc="',nc,'", ',
        'qop="',digest.qop,'", ',
        'response="',resp,'", ',
        'opaque="',digest.opaque,'"'].join('')

    return s;
}

我会尝试卷曲,但我不确定如何!

任何帮助表示赞赏!

【问题讨论】:

  • 您应该将cnonce 设为非常量随机值,以防止预计算字典攻击和其他攻击,请参阅section 4.9ff in the spec

标签: http node.js authorization


【解决方案1】:

我发现有几个问题可能与您的问题有关。很难说哪些是真正的罪魁祸首,因为对gathercontent 的实现一无所知。如果您粘贴了他们的“WWW-Authenticate”标头示例,则提供具体帮助会容易得多。

所以我在推测实际原因是什么,但这里有一些实际问题你应该解决,以符合规范(即保护它在未来不被破坏,因为服务器开始做的事情略有不同):

  • 在您正在创建的 Authorization 标头中,删除 nc 周围的双引号,也可能还有 qop
  • 我不知道qop 值gathercontent 正在使用什么。如果是 auth-int,那么您还必须将散列的 HTTP 正文附加到 HA2,请参阅 #3.2.2.3 of the spec - 此外,他们可能指定 逗号分隔的列表qop 值供您选择 - 或者服务器可能根本不会发送 qop 的值,即它们使用最基本的 HTTP 摘要身份验证,您的实现在其中将违反spec,因为那样你就不允许,例如发送cnoncenc等。
  • 您尝试通过parsedDigest['Digest realm'] 获取领域,即您假设realm 是初始Digest 关键字之后的第一个属性。情况可能是这样,也可能不是,但你不应该依赖它(修改你的 parseDigest 函数以在拆分其余部分之前删除字符串 "Digest "
  • 按照您使用parsedDigest 的方式,您假设Digest 始终以这种方式大写,而realmnonce 是总是小写。根据规范,这些都是不区分大小写的

几个不相关的问题:

  • 服务器真的强制你使用Digest authentication吗?这是 HTTPS,所以您不妨使用Basic authentication,它更容易,而且使用 HTTPS,同样安全。 (在这里回答我自己,检查了gathercontent:Basic auth 显然是not possible
  • 正如我在对您的问题的评论中提到的,cnonce 对于每个请求都应该是随机的,尤其是,您不应该从Wikipedia 复制和粘贴它,这会使您更容易受到攻击(但这里不是问题,因为在您的情况下,所有数据都通过 SSL)

关于如何卷曲 - 试试这个:

curl --data 'id=1234' --digest --user "apikey:pwd" https://abcdefg.gathercontent.com:443/api/0.1/get_pages_by_project/get_me

【讨论】:

  • 感谢尤金,详细的回答,我会处理你的建议。 GatherContent 中的 'qop' = 'auth'。我试图遵循 rfc2617 标准。当我试图在重构之前获得身份验证时,我的代码有点被破解(因此是“摘要领域”)......可能不得不重新考虑这一点! :)
  • 必须赏金给尤金,因为他回答的范围很广,并帮助澄清我有数字工作,因为这是我问题的意图。解决方案是更改请求中的密码字符:(
【解决方案2】:

是来自 GatherContent 的彼得。

第一个非常明显的事情是只使用get_me 而不是get_pages_by_project/get_me。你在后者中混合了两种不同的东西。 get_me 不需要通过 POST 发送任何参数,因此您可以删除它们。

另外,请确保您的密码始终为小写x

有什么改变吗?

编辑: 对于任何感兴趣的人,这里是我们的 API 文档: http://gathercontent.helpjuice.com/questions/26611-How-do-I-use-the-API

【讨论】:

  • 令人困惑的是密码必须是小写的单个 x 字符,而不是我现有的小写密码,我认为这很奇怪。谢谢彼得。
【解决方案3】:

express-auth 模块支持多种身份验证方案,包括 HTTP Digest。见:https://github.com/ciaranj/express-auth

另一个很好的选择是passporthttps://github.com/jaredhanson/passport

两个模块中的 http-digest 示例往往侧重于为您的 node.js 应用程序建立身份验证,而不是将身份验证请求转发给第三方。但是,您应该能够通过一点点面条使其工作。

如果按下,我会使用护照。提供的示例更加清晰和有文档记录。

希望对您有所帮助...

【讨论】:

  • express-authpassport 是服务器端的库,但问题是如何执行客户端请求。
【解决方案4】:

我会建议您使用 mikeal 的 request 模块,它会变得更容易和更清洁。

Request 尚不支持 HTTP Auth,遗憾的是,您只需要设置 Authorization 标头即可。

【讨论】:

    【解决方案5】:

    试试urllib,它将适用于简单和消化的身份验证。

    参见示例: https://stackoverflow.com/a/57221051/8490598

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-30
      • 2015-10-10
      • 2014-03-29
      • 1970-01-01
      • 1970-01-01
      • 2016-09-04
      • 2023-03-06
      相关资源
      最近更新 更多