Node.js - 摘要式身份验证 - 密码错误

问题描述 投票:0回答:1

我正在尝试访问本地 NVR 摄像头系统,该系统对其 API 使用摘要身份验证方法,问题是我收到“密码错误”错误。我对所有内容进行了两次和三次检查,一切似乎都是正确的,所以我不明白为什么我会收到错误消息。我可以用一双新眼睛了。显然我已经检查了几十次密码是否正确,所以我怀疑我的

getAuth
逻辑在某个地方不正确。

'use strict';
const express = require('express');
const app = express();
const crypto = require('crypto')

const md5_hash = function(value) {
  return crypto.createHash('md5').update(value).digest('hex');
};

app.use(express.json());
app.use(express.text());

app.get('/', async function(request, response) {
  var reply = {};
  var api = {
    'user': request.query.user || '',
    'pass': request.query.pass || '', // decoded
    'nonce': request.query.nonce || '',
    'uri': request.query.uri || '/LAPI/V1.0/System/Security/Login',
    'ip': '192.168.3.254'
  };
  var url = 'http://' + api.ip + api.uri;
  // request to get nonce value from server
  var preauth_reply = await req({'url':url, 'options':{'headers':{'method':'PUT'}}});
  // gets the nonce from the preauth response headers
  api.nonce = preauth_reply.httpHeaders['WWW-Authenticate'].split('=')[4].replace(/[^0-9]+/g, '');
  api.auth_string = 'Digest ' + getAuth(api);
  var request = {
    'url': 'http://' + api.ip + api.uri,
    'options': {
      'headers': {
        'method': 'PUT',
        'Content-Type': 'application/json',
        'Authorization': api.auth_string
      }
    }
  };
  var reply = await req(request);
  response.status(200).json(reply);
});

function getAuth(api) {
  var auth = {
    'username': api.user,
    'realm': 'NVRDVR',
    'qop': 'auth',
    // aleady updated with pre-auth header nonce number
    'nonce': api.nonce,
    // 'algorithm': 'MD5',
    'cnonce': (Math.random() * ((new Date).getTime() / 1e3)).toFixed(0),
    'nc': '00000001',
    'uri': api.uri,
    'response': '' // built later
  };
  auth.response = (function() {
    var c_string = api.user + ':' + auth.realm + ':' + api.pass;
    let c_hash = md5_hash(c_string);
    var u_string = 'PUT:' + api.uri; 
    let u_hash = md5_hash(u_string);
    var l_string = c_hash + ':' + auth.nonce + ':' + auth.nc + ':' + auth.cnonce + ':' + auth.qop + ':' + u_hash;
    return md5_hash(l_string);
  })();
  // ex output: username="root",realm="NVRDVR",qop=auth,nonce="123456789",cnonce="987654321",nc=00000001,uri="/LAPI/V1.0/System/Security/Login",response="817a24036881587163fb49e04d0d944d"
  return Object.keys(auth).map(function(k) {
    let is_not_quoted = ['qop', 'algorithm', 'nc'].indexOf(k) > -1;
    let value = is_not_quoted ? auth[k] : '"' + auth[k] + '"';
    return k + '=' + value;
  }).join(',');
}

async function req(request) {
  var reply = {};
  var response = await fetch(request.url, request.options).catch(function(err) {
    // err = [ 'stack', 'message', 'cause' ]
    // err.cause = [ 'stack', 'message', 'name', 'code', 'data' ]
    if (err.cause.code === 'HPE_INVALID_HEADER_TOKEN') {
      // returns header/body payload, so I can parse it later myself
      return err.cause.data;
    }
  });
  var body = null;
  try {
    body = await response.clone().json();
  }
  catch (e) {
    try {
      body = await response.clone().text();
    }
    // resolves "HPE_INVALID_HEADER_TOKEN" error by parsing header/body myself
    catch (innerE) {
      let res_parts = response.split('\n');
      var first_curly_bracket_index = res_parts.findIndex(function(item) {return !!item.match('{');});
      // gets everything BEFORE the first open curly bracket, where the JSON starts
      let headers = Object.assign({}, ...res_parts.slice(1, first_curly_bracket_index - 1).flatMap(function(item) {
        let split_parts = item.split(/:(.*)/s);
        return {[split_parts[0]]: split_parts[1]?.trim()}; 
      }));
      reply.httpCode = 200;
      reply.httpHeaders = headers;
      // gets everything AT the first open curly bracket, where the JSON starts
      body = JSON.parse(res_parts.slice(first_curly_bracket_index).join('\n'));
    }
  }
  if (body) {
    reply.success = true;
    reply.httpHeaders = response.header || reply.httpHeaders;
    reply.httpCode = response.status || reply.httpCode;
    reply.data = body || null;
    return reply;
  }
  reply.error = true;
  return reply;
}

app.listen(8888);

作为一种调试方法,我将所有变量放入我的

reply
对象中(在此代码的未删节版本中),以确保没有任何未定义的内容,这是输出。
data
httpHeaders
是来自服务器的响应。
"StatusCode": 50801
翻译为
The password is incorrect.

{
    "httpCode": 200,
    "httpHeaders": {
        "Content-Type": "text/html;CHARset=utf-8",
        "Connection": "close",
        "X-Frame-Options": "SAMEORIGIN",
        "Content-Security-Policy": "img-src 'self' data:; default-src 'self' 'unsafe-inline' 'unsafe-eval';",
        "X-Content-Type-Options": "nosniff",
        "X-XSS-Protection": "1; mode=block"
    },
    "success": true,
    "data": {
        "Response": {
            "ResponseURL": "/LAPI/V1.0/System/Security/Login",
            "ResponseCode": 1,
            "ResponseString": "Common Error",
            "StatusCode": 50801,
            "Data": {
                "RemainLockTimes": 2,
                "RemainUnlockTime": 5
            }
        }
    },
    "api": {
        "user": "api",
        "pass": "{REMOVED}",
        "nonce": "1406364820",
        "uri": "/LAPI/V1.0/System/Security/Login",
        "ip": "192.168.3.254",
        "c_string": "api:NVRDVR:{REMOVED}",
        "u_string": "PUT:/LAPI/V1.0/System/Security/Login",
        "l_string": "30779ba755a47f5eb9cf90ce2e6d7c2f:1406364820:00000004:200397104:auth:a9bc5304a295f82075fc55d8bfa70457",
        "auth": {
            "username": "api",
            "realm": "NVRDVR",
            "qop": "auth",
            "nonce": "1406364820",
            "cnonce": "200397104",
            "nc": "00000004",
            "uri": "/LAPI/V1.0/System/Security/Login",
            "response": "817a24036881587163fb49e04d0d944d"
        },
        "auth_string": "Digest username=\"api\",realm=\"NVRDVR\",qop=auth,nonce=\"1406364820\",cnonce=\"200397104\",nc=00000004,uri=\"/LAPI/V1.0/System/Security/Login\",response=\"817a24036881587163fb49e04d0d944d\""
    }
}

并且,如果有帮助的话,这些是来自预身份验证请求的标头,我从中获取随机数:

Status: 200
WWW-Authenticate: Digest qop="auth",algorithm="MD5",realm="NVRDVR",nonce="1412251636",stale="FALSE"
Content-Type: text/html;CHARset=utf-8
Connection: close
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: img-src 'self' data:; default-src 'self' 'unsafe-inline' 'unsafe-eval';
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block

有人看出有什么问题吗?

node.js express digest-authentication
1个回答
0
投票

并非所有角色都能被“看到”。以十六进制查看。例如,即使是“纯”文本/ASCII 也有 nul 字符。

© www.soinside.com 2019 - 2024. All rights reserved.