nodejs如何实现Digest摘要认证?

news2024/12/25 9:52:12

文章目录

      • 1.前言
      • 2. 原理
      • 3. 过程
      • 4. node实现摘要认证
      • 5. 前端如何Digest摘要登录认证(下面是海康的设备代码)

1.前言

根据项目需求,海康设备ISAPI协议需要摘要认证,那么什么是摘要认证?估计不少搞到几年的前端连摘要认证都不知道是什么?能解决什么问题?我们平时用的比较多的是Basic基础认证,Digest 摘要认证比 Basic 基础认证的安全级别更高,它可以通过传递用户名、密码等计算出来的摘要来解决明文方式在网络上发送密码的问题,通过服务产生随机数 nonce 的方式可以防止恶意用户捕获并重放认证的握手过程。

2. 原理

1.客户端发出一个没有认证证书的请求,下面示例为用户名密码校验的ISAPI协议命令(GET方法),每次下发新的命令都需要重新认证。

GET /ISAPI/Security/userCheck HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 10.18.37.12
Connection: Keep-Alive

2. 服务器产生一个随机数nonce,并且将该随机数放在WWW-Authenticate响应头,与服务器支持的认 证算法列表,认证的域realm一起发送给客户端,401 Unauthorized表示认证失败、未授权。返回的WWW-Authenticate表示设备支持的认证方式,stale表示nonce值是否过期,如果过期会生成新的随机数。

HTTP/1.1 401 Unauthorized
Date: Wed, 01 February 2023 19:16:52 GMT
Server: App-webs/
Content-Length: 178
Content-Type: text/html
Connection: keep-alive
Keep-Alive: timeout=10, max=99
WWW-Authenticate: Digest qop="auth", realm="IP Camera(12345)", 
nonce="4e5749344e7a4d794e544936596a4933596a51784e44553d", stale="FALSE"

看到上面出现了那么多之前没见过的参数,下面会做出详细解释:

  1. WWW-Authentication:用来定义使用何种方式(Basic、Digest、Bearer等)去进行认证以获取受保护的资源
  2. realm:表示Web服务器中受保护文档的安全域(比如公司财务信息域和公司员工信息域),用来指示需要哪个域的用户名和密码
  3. qop:保护质量,包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略,(可以为空,但是)不推荐为空值
  4. nonce:服务端向客户端发送质询时附带的一个随机数,这个数会经常发生变化。客户端计算密码摘要时将其附加上去,使得多次生成同一用户的密码摘要各不相同,用来防止重放攻击
  5. nc:nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量。例如,在响应的第一个请求中,客户端将发送“nc=00000001”。这个指示值的目的是让服务器保持这个计数器的一个副本,以便检测重复的请求
  6. cnonce:客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
  7. response:这是由用户代理软件计算出的一个字符串,以证明用户知道口令
  8. Authorization-Info:用于返回一些与授权会话相关的附加信息
  9. nextnonce:下一个服务端随机数,使客户端可以预先发送正确的摘要
  10. rspauth:响应摘要,用于客户端对服务端进行认证
  11. stale:当密码摘要使用的随机数过期时,服务器可以返回一个附带有新随机数的401响应,并指定stale=true,表示服务器在告知客户端用新的随机数来重试,而不再要求用户重新输入用户名和密码了

3. 客户端接收到401响应表示需要进行认证,选择一个算法(目前只支持MD5)生成一个消息摘要(message digest,该摘要包含用户名、密码、给定的nonce值、HTTP方法以及所请求的URL),将摘要放到Authorization的请求头中重新发送命令给服务器。如果客户端要对服务器也进行认证,可以同时发送客户端随机数cnonce,客户端是否需要认证,通过报文里面的qop值进行判断。

GET /ISAPI/Security/userCheck HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 10.18.37.12
Connection: Keep-Alive
Authorization: Digest username="admin",realm="IP 
Camera(12345)",nonce="595463314d5755354d7a4936596a49344f475a6a5a44453d",uri="/ISAPI/Security/userCheck",cnonc
e="011e08f6c9d5b3e13acfa810ede73ecc",nc=00000001,response="82091ef5aaf9b54118b4887f8720ae06",qop="auth"

4. 服务接收到摘要,选择算法以及掌握的数据,重新计算新的摘要跟客户端传输的摘要进行比较,验证是否匹配,若客户端反过来用客户端随机数对服务器进行质询,就会创建客户端摘要,服务可以预先将下一个随机数计算出来,提前传递给客户端,通过 Authentication-Info 发送下一个随机数。该步骤选择实现。

HTTP/1.1 200 OK
Date: Wed, 01 February 2023 19:16:52 GMT
Server: App-webs/
Content-Length: 132
Connection: keep-alive
Keep-Alive: timeout=10, max=98
Content-Type: text/xml
<?xml version="1.0" encoding="UTF-8"?>
<userCheck>
<statusValue>200</statusValue>
<statusString>OK</statusString>
</userCheck>

3. 过程

在说明如何计算摘要之前,先说明参加摘要计算的信息块。信息块主要有两种:

  1. 表示与安全相关的数据的A1,A1中的数据时密码和受保护信息的产物,它包括用户名、密码、保护域和随机数等内容,A1只涉及安全信息,与底层报文自身无关.
    若算法是:MD5 A1 = < user >:< realm >:< password>
    若算法是:MD5-sess
    则 A1=MD5( < user > :< realm >:< password >):< nonce >:< cnonce >

在这里插入图片描述

  1. 表示与报文相关的数据的A2,A2表示是与报文自身相关的信息,比如URL,请求反复和报文实体的主体部分,A2加入摘要计算主要目的是有助于防止反复,资源或者报文被篡改。
    若 qop 未定义或者 auth:
    A2=< request-method >:< uri-directive-value >
    若 qop 为 auth:-int
    A2=< request-method >:< uri-directive-value >:MD5(< request-entity-body >)
    注:< uri-directive-value >为完整的协议命令 URI,比如“/ISAPI/ContentMgmt/InputProxy/channels/status”。

    在这里插入图片描述
  2. 最重要的一步,定义摘要的计算规则
    若 qop 没有定义:
    摘要 response=MD5(MD5(A1):< nonce >:MD5(A2))
    若 qop 为 auth:
    摘要 response=MD5(MD5(A1):< nonce >:< nc >:< cnonce >:< qop >:MD5(A2))
    若 qop 为 auth-int:
    摘要 response= MD5(MD5(A1):< nonce >:< nc >:< cnonce >:< qop >:MD5(A2))

4. node实现摘要认证

 const http = require('http');
    const crypto = require('crypto');

    const realm = '1e7d1893b17de03ccc122a4f';
    const users = {
        'admin': 'Coolyuan5g',
        'admin1': 'kg5g',
    };

    const authenticate = (res) => {
        res.setHeader('WWW-Authenticate',
            `Digest realm="${realm}", nonce="${Date.now()}", algorithm=MD5, qop="auth"`);
        res.statusCode = 401;
        res.end('Unauthorized');
    };

    const validate = (auth, password) => {
        const hash = crypto.createHash('md5');
        hash.update(`${auth.username}:${realm}:${password}`);
        return hash.digest('hex') === auth.response;
    };

    const parseAuth = (auth) => {
        const [, parameters] = auth.split(' ');
        const params = {};
        parameters.split(', ').forEach((param) => {
            const [key, value] = param.split('=');
            params[key] = value.replace(/\"/g, '');
        });
        return params;
    };

    const server = http.createServer((req, res) => {
        if (!req.headers.authorization) {
            return authenticate(res);
        }

        const auth = parseAuth(req.headers.authorization);
        if (!auth || !auth.username || !auth.realm || !auth.nonce || !auth.uri || !auth.response || auth
            .realm !== realm) {
            return authenticate(res);
        }

        if (!users[auth.username] || !validate(auth, users[auth.username])) {
            return authenticate(res);
        }

        res.end('Authorized');
    });

5. 前端如何Digest摘要登录认证(下面是海康的设备代码)

const axios = require('axios'),
    md5 = require('md5-node');


function generateCnonce() {
    let cnonce = '';
    const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    for (let i = 0; i < 32; i++) {
        cnonce += possible.charAt(Math.floor(Math.random() * possible.length));
    }
    return cnonce;
}


//获取超脑设备所有的设备通道信息
router.get('/getChannels', function (req, res, next) {
    const params = req.query;
    if (params.ip && params.userName && params.password) {
        axios.get(
            `http://${params.ip}/ISAPI/ContentMgmt/InputProxy/channels/status`
        ).then((response) => {
            console.log(response)
        }).catch((error) => {
            //第一次请求401,获取服务请求头返回的数据

            // WWW - Authentication: 用来定义使用何种方式( Basic、 Digest、 Bearer等) 去进行认证以获取受保护的资源
            // realm: 表示Web服务器中受保护文档的安全域( 比如公司财务信息域和公司员工信息域), 用来指示需要哪个域的用户名和密码
            // qop: 保护质量, 包含auth( 默认的) 和auth - int( 增加了报文完整性检测) 两种策略,( 可以为空, 但是) 不推荐为空值
            // nonce: 服务端向客户端发送质询时附带的一个随机数, 这个数会经常发生变化。 客户端计算密码摘要时将其附加上去, 使得多次生成同一用户的密码摘要各不相同, 用来防止重放攻击
            // nc: nonce计数器, 是一个16进制的数值, 表示同一nonce下客户端发送出请求的数量。 例如, 在响应的第一个请求中, 客户端将发送“ nc = 00000001”。 这个指示值的目的是让服务器保持这个计数器的一个副本, 以便检测重复的请求
            // cnonce: 客户端随机数, 这是一个不透明的字符串值, 由客户端提供, 并且客户端和服务器都会使用, 以避免用明文文本。 这使得双方都可以查验对方的身份, 并对消息的完整性提供一些保护
            // response: 这是由用户代理软件计算出的一个字符串, 以证明用户知道口令
            // Authorization - Info: 用于返回一些与授权会话相关的附加信息
            // nextnonce: 下一个服务端随机数, 使客户端可以预先发送正确的摘要
            // rspauth: 响应摘要, 用于客户端对服务端进行认证
            // stale: 当密码摘要使用的随机数过期时, 服务器可以返回一个附带有新随机数的401响应, 并指定stale = true, 表示服务器在告知客户端用新的随机数来重试, 而不再要求用户重新输入用户名和密码了
            // Digest realm="1e7d1893b17de03ccc122a4f", domain="/", qop="auth", nonce="d4cdb906ef173773:1e7d1893b17de03ccc122a4f:1863093aa1e:e", opaque="799d5", algorithm="MD5", stale="FALSE"
            if (error.response.status == 401) {
                let info = error.response.headers["www-authenticate"],
                    cnonce = generateCnonce(),
                    nonce = info.match(/\snonce="([^"]+)/)[1],
                    opaque = info.match(/\sopaque="([^"]+)/)[1],
                    qop = info.match(/\sqop="([^"]+)/)[1],
                    realm = info.match(/\srealm="([^"]+)/)[1],
                    uri = `/ISAPI/ContentMgmt/InputProxy/channels/status`,
                    a1Md5 = md5(`admin:${realm}:Coolyuan`),
                    a2Md5 = md5(`GET:${uri}`),
                    //这里的response计算是关键!!!通过md5计算拼接好的字符串
                    response = md5(`${a1Md5 }:${nonce}:${nc}:${cnonce}:auth:${a2Md5 }`),
                    authorization= `Digest username="admin", realm="${realm}", nonce="${nonce}", uri=${uri}, algorithm=MD5, response="${response}", opaque="${opaque}", qop=${qop}, nc=00000001, cnonce="${cnonce}"`


                //实现第二次请求
                axios.get(
                    `http://${params.ip}/ISAPI/ContentMgmt/InputProxy/channels/status`, {
                        headers: {
                            'Authorization': authorization
                        }
                    }
                ).then((data) => {
                    res.status(200).send({
                        "code": 200,
                        "message": "成功",
                        "result": "success",
                        "content": data.data
                    });
                }).catch((error) => {
                    res.status(500).send({
                        "code": 500,
                        "message": "失败",
                        "result": "fail",
                        "content": null
                    });
                })
            }
        })
    } else {
        res.status(401).send({
            "message": "失败",
            "result": "fail",
            "content": null
        });
    }
});

module.exports = router;

以上部分是代码的核心部分,仅供参考!谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/337264.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日一个解决问题:事务无法回滚是什么原因?

今天在码代码时发现事务不回滚了&#xff0c;学过MySQL 事务小伙伴们都懂&#xff0c;通过 begin 开启事务&#xff0c;通过 commit 提交事务或者通过 rollback 回滚事务。 正常来说&#xff0c;当我们开启一个事务之后&#xff0c;需要 commit 或者 rollback 来结束一个事务的…

下面这段Python代码执行后的输出结果是?

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注回复“书籍”即可获赠Python从入门到进阶共10本电子书今日鸡汤几行归塞尽&#xff0c;念尔独何之。大家好&#xff0c;我是皮皮。一、前言前几天在Python青铜交流群【桐霄L】问了一个Python基础的问题&#xff0c;这里拿出…

TortoiseGit 使用教程

一、下载工具 这里给大家准备了所有安装包自取 链接&#xff1a;https://pan.baidu.com/s/1xrxxgyNXNQEGD_RjwKnPMg 提取码&#xff1a;qwer 也可在官网自行下载最新版 1.下载git&#xff0c;直接去官网下载Git - Downloads&#xff0c;根据自己的系统合理下载&#xff0c…

openGauss客户端安装

目录1. 准备两台Linux系统2. 安装openGauss客户端3. 设置客户端主机环境变量4. 修改服务端配置文件5. 测试客户端远程连接客户端环境&#xff1a;openEuler release 22.03 (LTS-SP1) 服务端环境: openEuler release 20.03 (LTS-SP3) openEuler系统官网下载 1. 准备两台Linux系…

尚医通(十)数据字典加Redis缓存 | MongoDB

目录一、Redis介绍二、数据字典模块添加Redis缓存1、service_cmn模块&#xff0c;添加redis依赖2、service_cmn模块&#xff0c;添加Redis配置类3、在service_cmn模块&#xff0c;配置文件添加redis配置4、通过注解添加redis缓存5、查询数据字典列表添加Redis缓存6、bug&#x…

[oeasy]python0080_设置RGB颜色_24bit_24位真彩色_颜色设置

RGB颜色 回忆上次内容 上次 首先了解了 索引颜色 \33[38;5;XXXm 设置 前景为索引色\33[48;5;XXXm 设置 背景为索引色 RGB每种颜色 可选0-5总共 6 级 想用 精确RGB值 真实地 大红色画个 大红桃心 ♥️ 有可能吗&#xff1f;&#xff1f;&#x1f914; rgb 模式 关于 RGB 模式…

07- 梯度下降优化(Lasso/Ridge/ElasticNet) (机器学习)

归一化: 减少数据不同数量级对预测的影响, 主要是将数据不同属性的数据都降到一个数量级。 最大值最小值归一化:优点是可以把所有数值归一到 0~1 之间&#xff0c;缺点受离群值影响较大。0-均值标准化: 经过处理的数据符合标准正态分布&#xff0c;即均值为0&#xff0c;标准差…

Android 逆向工具大整理,碉堡了

文章目录jadx打开 gui 界面把安装包打开双击变量名和方法名可以高亮所有出现的地方**强大的搜索功能****搜索资源****查看 APK 签名****查看 APK dex 数&#xff0c;方法数****查看资源&#xff0c;配置清单****展开包名**查找方式引用反混淆导出 Gradle 工程导出反编译资源cla…

腾讯云安全组配置参考版

官方文档参考: 云服务器 安全组应用案例-操作指南-文档中心-腾讯云 新建安全组时&#xff0c;您可以选择腾讯云为您提供的两种安全组模板&#xff1a; 放通全部端口模板&#xff1a;将会放通所有出入站流量。放通常用端口模板&#xff1a;将会放通 TCP 22端口&#xff08;Lin…

JDBC(老版)

文章目录JDBC概述数据持久化Java中的数据存储技术JDBC介绍JDBC体系结构JDBC程序编写步骤获取数据库连接要素一&#xff1a;Driver接口实现类Driver接口介绍加载与注册JDBC驱动要素二&#xff1a;URL要素三&#xff1a;用户名和密码数据库连接方式举例使用PreparedStatement实现…

元学习方法解决CDFSL以及两篇SOTA论文讲解

来源&#xff1a;投稿 作者&#xff1a;橡皮 编辑&#xff1a;学姐 带你学习跨域小样本系列1-简介篇 跨域小样本系列2-常用数据集与任务设定详解 跨域小样本系列3&#xff1a;元学习方法解决CDFSL以及两篇SOTA论文讲解&#xff08;本篇&#xff09; 跨域小样本系列4&#xf…

Lesson 6.5 机器学习调参基础理论与网格搜索

文章目录一、机器学习调参理论基础1. 机器学习调参目标及基本方法2. 基于网格搜索的超参数的调整方法2.1 参数空间2.2 交叉验证与评估指标二、基于 Scikit-Learn 的网格搜索调参1. sklearn 中网格搜索的基本说明2. sklearn 中 GridSearchCV 的参数解释3. sklearn 中 GridSearch…

漏洞扫描器之AWVS

数据来源 01 漏洞扫描器及AWVS介绍 》漏洞扫描 》常见漏洞扫描工具 网络上公布的付费的或者免费的漏洞扫描工具、脚本多种多样。 √ 针对某类漏洞的&#xff1a;sql注入(sqlmap)、weblogic&#xff08;weblogicscan&#xff09; √ 针对某类CMS的: wordpress( wpscan)、 …

数据分析到底该怎么学呢?讲真,真不难!

这几年&#xff0c;“数据分析”是很火啊&#xff0c;在这个数据驱动一切的时代&#xff0c;数据挖掘和数据分析就是这个时代的“淘金”&#xff0c;懂数据分析、拥有数据思维&#xff0c;往往成了大厂面试的加分项。 比如通过数据分析&#xff0c;我们可以更好地了解用户画像…

CSS 重新认识 !important 肯定有你不知道的

重新认识 !important 影响级联规则 与 animation 和 transition 的关系级联层cascade layer内联样式!important 与权重 !important 与简写属性!important 与自定义变量!important 最佳实践 在开始之前, 先来规范一下文中的用于, 首先看 W3C 中关于 CSS 的一些术语定义吧. 下图…

微信小程序如何获取用户信息

自我介绍我是IT果果日记&#xff0c;微信公众号请搜索 IT果果日记一个普通的技术宅&#xff0c;定期分享技术文章&#xff0c;欢迎点赞、关注和转发&#xff0c;请多关照。微信小程序用户基本信息有哪些&#xff1f;除了基本信息&#xff0c;微信还会提供openId和unionId&#…

微服务项目简介

项目简介 项目模式 电商模式&#xff1a;市面上有5种常见的电商模式&#xff0c;B2B、B2C、 C2B、 C2C、O2O; 1、B2B模式 B2B (Business to Business)&#xff0c;是指 商家与商家建立的商业关系。如:阿里巴巴 2、B2C 模式 B2C (Business to Consumer), 就是我们经常看到的供…

6个月软件测试培训出来后的感悟 —— 写给正在迷茫是否要转行或去学软件测试的学弟们

本人刚从某培训机构学习结束&#xff0c;现在已经上班一个月了。这篇文章我不会说太多的知识点&#xff0c;或噱人去培训机构学习的话语&#xff0c;仅作为一个普通打工者的身份&#xff0c;来写给那些对于软件测试未来发展、薪资待遇等不清楚的正在为家庭&#xff0c;解决信用…

2023年中国数字化活动行业专题报告

易观&#xff1a;2023年2月&#xff0c;易观发布《2023年中国数字化活动行业专题报告》。报告主要分析了中国数字化活动市场发展背景与现状&#xff0c;数字化活动厂商的主要商业模式及其运作模式&#xff0c;典型案例&#xff0c;未来发展趋势洞察等。同时&#xff0c;易观分析…

网上流量卡可靠吗,网上的这些大流量卡你知道是怎么来的吗?

网上怎么这么多五花八门的流量卡&#xff0c;这些大流量卡是怎么来的你都知道吗&#xff1f;所谓的大流量卡&#xff0c;是因为每个省份为了拉新用户所自行包装的产品&#xff0c;一般是在在基础套餐上增加了一些流量包和充值送话费活动&#xff0c;然后得出来一个产品套餐&…