MongoDB SASL 鉴权方式 SCRAM-SHA-1步骤

转载于 MongoDB SCRAM-SHA-1 over SASL

文章目录

    • Overview
    • Step 1
    • Step 2
    • Step 3
    • Edits

I recently implemented SCRAM-SHA-1 over SASL for Fantom’s MongoDB driver so it could authenticate against MongoDB v3 databases.
Much to my surprise, for such a massive breaking change to MongoDB drivers, there’s next to nothing available that succinctly explains how it works. Not a sausage!
What I did find though, was a list of specifications and documentation that was as cryptic as the authentication mechanism itself!
By scrutinising every word in the documentation, and by reading the source code for a native Erlang driver, I was finally able to figure it out.
So now I present to you, what would have been extremely helpful to me, a fully worked authentication conversation with MongoDB for SCRAM-SHA-1 over SASL.

Overview

All the authentication documentation for MongoDB v3.x talks of SCRAM-SHA-1, or Salted Challenge Response Authentication Mechanism (SCRAM) with SHA-1. SCRAM defines how to encode an authentication message to send to the server. It uses the PBKDF2 algorithm from the Public-Key Cryptography Standards (PKCS).

SASL, or Simple Authentication and Security Layer, then defines a protocol of how to send / receive these authentication messages.

These SASL messages are then wrapped up in MongoDB BSON documents and sent as database commands in the usual MongoDB driver manner.

If you want the low down on all of the above, here are links to the relevant parts of the specifications:

  • MongoDB Driver Authentication with SCRAM-SHA-1
  • RFC5802 - Salted Challenge Response Authentication Mechanism (SCRAM) - Sec. 3 Algorithm Overview
  • RFC4422 - Simple Authentication and Security Layer (SASL) - Sec. 3 The Authentication Exchange
  • RFC2898 - Password-Based Cryptography Specification - Sec. 5.2 PBKDF2

Note that the MongoDB specification above was bloody hard to track down! It does have a sample client / server conversation, but it makes no attempt to explain how it calculated at the given values. For consistency, this example uses the same the parameters, but shows how they were calculated.

The example conversation assumes the following values:

  • Username: user
  • Password: pencil
  • Database: test

Note that all code listed here is written in Fantom.

Step 1

Send an authentication request to the server.

In the request we name the user we wish to authenticate as, and a nonce. The nonce is random sequence of characters and should be of cryptographic strength.

clientNonce    := ... crypto strength random characters ...
// --> fyko+d2lbbFgONRv9qkxdawLclientFirstMsg := "n=${userName},r=${clientNonce}"
// --> n=user,r=fyko+d2lbbFgONRv9qkxdawL

Send a command to the test database ( test.$cmd ) consisting of the following BSON document. Note that n, is constant and in some literature is referred to as GS2 Header.

{"saslStart"     : 1,"mechanism"     : "SCRAM-SHA-1","payload"       : binary("n,," + clientFirstMsg),"autoAuthorize" : 1
}

In all these messages, the payload field is a BSON binary object, but the content is always just ASCII characters.

The server should respond with the following BSON document:

{"conversationId" : 1,"payload"        : binary("r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE,s=rQ9ZY3MntBeuP3E1TDVC4w==,i=10000"),"done"           : false,"ok"             : 1
}

From which we can derive the following variables:

conversationId   := 1
serverFirstMsg   := "r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE,s=rQ9ZY3MntBeuP3E1TDVC4w==,i=10000"
serverNonce      := "fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE"
serverSalt       := "rQ9ZY3MntBeuP3E1TDVC4w=="
serverIterations := 10000

Step 2

Send an encoded message that proves we have the password. This is hard part.

First we create a string hash of the normalised password.

hashedPassword  := Buf().print("${userName}:mongo:${password}").toDigest("MD5").toHex
// --> "1c33006ec1ffd90f9cadcbcc0e118200"

Next we do the cryptographic stuff. Most languages have classes or libraries for doing this. Fantom uses Java’s javax.crypto.spec.PBEKeySpec.

dkLen           := 20   // the size of a SHA-1 hash
saltedPassword  := Buf.pbk("PBKDF2WithHmacSHA1", hashedPassword, Buf.fromBase64(serverSalt), serverIterations, dkLen)
// --> 6a bd 37 85 0d a6 e3 27 df 8d c5 af d4 30 79 10 52 f9 24 99

In the next string, clientFinalNoPf (client final no proof), note that biws is a constant and is the just the string “n,” Base64 encoded. In some literature this is referred to as the GS2 Header.

The string “Client Key” is also constant and is used as a default message to be hashed by the (salted) password.

clientFinalNoPf := "c=biws,r=${serverNonce}"
// --> "c=biws,r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE"authMessage     := "${clientFirstMsg},${serverFirstMsg},${clientFinalNoPf}"
// --> "n=user,r=fyko+d2lbbFgONRv9qkxdawL,r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE,s=rQ9ZY3MntBeuP3E1TDVC4w==,i=10000,c=biws,r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE"clientKey       := Buf().print("Client Key").hmac("SHA-1", saltedPassword)
// --> 6e ca 60 b8 b0 46 77 1f c7 17 40 92 de 6e 7e 83 78 59 b3 56storedKey       := clientKey.toDigest("SHA-1")
// --> a7 9c fa 9f b5 2d a9 ff a9 2c 19 1a 78 99 38 4f 77 81 38 e0clientSignature := Buf().print(authMessage).hmac("SHA-1", storedKey)
// --> 5e e7 f3 48 ab 9d ee 7b 9b 87 7c ae 7f 07 07 a2 20 78 73 70clientProof     := xor(clientKey, clientSignature)
// --> 30 2d 93 f0 1b db 99 64 5c 90 3c 3c a1 69 79 21 58 21 c0 26clientFinal     := "${clientFinalNoPf},p=${clientProof.toBase64}"
// --> "c=biws,r=fyko+d2lbbFgONRv9qkxdawLHo+Vgk7qvUOKUwuWLIWg4l/9SraGMHEE,p=MC2T8BvbmWRckDw8oWl5IVghwCY="

The xor() method is an annoying little thing where each byte of each input have to be xored together. It is not a native Fantom function, but instead a method you have to write yourself.

It’s the clientFinal string that we send as the payload in the next message to the server.

{"saslContinue"   : 1,"conversationId" : conversationId,"payload"        : binary(clientFinal)
}

The server should then respond with:

{"conversationId" : 1,"payload"        : binary("v=UMWeI25JD1yNYZRMpZ4VHvhZ9e0="),"done"           : false,"ok"             : 1
}

Now it is our chance to validate the server and check that it also knows the user’s password.

Note the string “Server Key” is constant and is used as a default message to be hashed by the (salted) password.

serverKey := Buf().print(“Server Key”).hmac(“SHA-1”, saltedPassword)
// --> 95 1a d5 1f 2a 8c 5f e3 8e a8 6b e9 72 fb fd 6a 79 40 f0 84

serverSignature := Buf().print(authMessage).hmac(“SHA-1”, serverKey).toBase64
// --> “UMWeI25JD1yNYZRMpZ4VHvhZ9e0=”
Note that serverSignature should equal the value sent by the server.

Step 3

Acknowledge and end the conversation.

Next we send a quick message to the server to finish up. Note that payload is an empty binary object.

{"saslContinue"   : 1,"conversationId" : conversationId,"payload"        : binary()
}

To which the server should respond with:

{"conversationId" : 1,"payload"        : binary(),"done"           : true,"ok"             : 1
}

Note that done is now true and that signals the end of the authentication conversation.

Edits

  • 1 November 2015 - Original article.
  • 26 May 2018 - Added notes on “Client Key” and “Server Key” being constant.

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

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

相关文章

C++函数模板案例

利用函数模板封装一个排序的函数&#xff0c;可以对不同数据类型数组进行排序排序规则从大到小&#xff0c;排序算法为选择排序分别利用char数组和int数组进行测试 #include<iostream> using namespace std;template<class T> void myswap(T& a, T& b) {T…

[Python从零到壹] 七十三.图像识别及经典案例篇之图像去雾ACE算法和暗通道先验去雾算法实现

十月太忙&#xff0c;还是写一篇吧&#xff01;祝大家1024节日快乐O(∩_∩)O 欢迎大家来到“Python从零到壹”&#xff0c;在这里我将分享约200篇Python系列文章&#xff0c;带大家一起去学习和玩耍&#xff0c;看看Python这个有趣的世界。所有文章都将结合案例、代码和作者的经…

java中什么是守护线程?

在 Java 中&#xff0c;线程分为两种类型&#xff1a;用户线程&#xff08;User Thread&#xff09;和守护线程&#xff08;Daemon Thread&#xff09;。 用户线程&#xff08;User Thread&#xff09;&#xff1a; 用户线程是应用程序中的主要线程&#xff0c;当所有的用户线程…

实例分割网络:Mask RCNN

文章目录 网络结构Mask 分支RoIAlignRoIPooling的精度问题RoIAlign方法Mask RepresentationMask R-CNNNetwork Architecture实现细节实验结果与其他的实例分割网络的对比对比实验不同backbone的对比实验不同的激活函数的对比实验RoiAli

更多内窥镜维修技能学习与交流可关注西安彩虹

内窥镜结构及光学成像原理 众多品牌的硬镜其内部结构基本相似&#xff08;如下图&#xff09;&#xff0c;最关键的在于不同用途的硬镜在其结构上发生变化&#xff0c;包括光学成像系统和机械结构。光学成像系统由物镜系统、转像系统、目镜系统三大系统组成。 工作原理 被观察…

1文件+2个命令,无需安装,单机离线运行70亿大模型

1文件2个命令&#xff0c;无需安装&#xff0c;单机离线运行70亿大模型 大家好&#xff0c;我是老章 最近苹果发布了自己的深度学习框架--MLX&#xff0c;专门为自家M系列芯片优化。看了展示视频&#xff0c;这个框架还能直接运行Llama 7B的大模型&#xff0c;在M2 Ultral上运…

计算三位数每位上数字的和

分数 10 作者 python课程组 单位 福州大学至诚学院 补充程序实现计算&#xff1a; 输入一个三位的整数&#xff08;不接受实数&#xff09;&#xff0c;求这个三位数每一位上数字的和是多少&#xff1f;例如&#xff1a;输入&#xff1a;382&#xff0c;输出&#xff1a;和为…

用gdal正射校正遥感影像

目录 代码示例有相应的RPC文件用gdal命令行校正 使用 gdal.Warp函数可以非常方便对遥感影像进行正射校正&#xff0c;这个过程需要我们确定目标影像的几何信息&#xff0c;包括坐标系、分辨率以及需要配准到的区域或基准影像 代码示例 以下是一个使用gdal.Warp配准影像的基本…

MySQL中是如何insert数据的

正常insert数据&#xff0c;MySQL并不会显式加锁&#xff0c;而是通过聚簇索引的trx_id索引作为隐式锁来保护记录的。比如两个事务对一个非唯一的索引情况添加&#xff0c;会造成幻读 但在某些特殊情况下&#xff0c;隐式锁会转变为显式锁&#xff1a; 记录之间有间隙锁inser…

Channel Attention前言——一二阶统计量

统计量 简述 ​ 一阶统计量和二阶统计量是统计学中常用的两类统计量。一阶统计量是指只考虑随机变量本身的统计量&#xff0c;而二阶统计量则是指考虑随机变量之间关系的统计量。 一阶统计量 一阶统计量是指只考虑随机变量本身的统计量&#xff0c;通常包括以下几种&#x…

二叉树的非递归遍历(详解)

二叉树非递归遍历原理 使用先序遍历的方式完成该二叉树的非递归遍历 通过添加现有项目的方式将原来编写好的栈文件导入项目中 目前项目存在三个文件一个头文件&#xff0c;两个cpp文件&#xff1a; 项目头文件的代码截图&#xff1a;QueueStorage.h 项目头文件的代码&#xff…

达梦(主备)搭建

一、服务器配置 1.扩展基础盘 磁盘分区 /sbin/fdisk /dev/vda<<EOF &> /dev/null p n 4p w EOF 硬盘刷新 partx -s /dev/vda echo "Disk Partition /dev/vda4 Create OK!" pvcreate /dev/vda4 rootlvnamedf -h|grep "\-root"|awk {prin…

全电动注塑机市场分析:全球市场规模将达到223.23亿美元

注射成型机(简称注射机或注塑机)是将热塑性塑料或热固性料利用塑料成型模具制成各种形状的塑料制品的主要成型设备。 注射成型是通过注塑机和模具来实现的。 注塑机通常由注射系统、合模系统、液压传达动系统、电气控制系统、润滑系统、加热及冷却系统、安全监测系统等组成。 注…

如何运用gpt改写出高质量的文章 (1)

大家好&#xff0c;今天来聊聊如何运用gpt改写出高质量的文章 (1)&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 如何运用GPT改写出高质量的文章 一、引言 随着人工智能技术的飞速发展&#xff0c;自然…

大一C语言作业 12.8

1.C 对一维数组初始化时&#xff0c;如果全部元素都赋了初值&#xff0c;可以省略数组长度。 这里没有指定数组长度&#xff0c;编译器会根据初始化列表的元素个数来确定数组长度。 2.C 在C语言中&#xff0c;字符数组是不能用赋值运算符直接赋值的。 3.C 在二维数组a中&#x…

《C++新经典设计模式》之第20章 访问者模式

《C新经典设计模式》之第20章 访问者模式 访问者模式.cpp 访问者模式.cpp #include <iostream> #include <list> #include <memory> using namespace std;// 提供一个作用于某对象结构中的各元素的操作表示&#xff0c;便可以在不改变各元素类的前提下定义&…

springboot(ssm寝室小卖部系统 宿舍小商店网站Java(codeLW)

springboot(ssm寝室小卖部系统 宿舍小商店网站Java(code&LW) 开发语言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&#xff08;或8.0&#x…

【力扣100】7.无重复字符的最长子串

添加链接描述 class Solution:def lengthOfLongestSubstring(self, s: str) -> int:# 思路是使用队列&#xff0c;加入一次取一个最大值&#xff0c;然后如果重复&#xff0c;则队列出到没有重复值位置# 但是这个队列其实使用数组实现的strlist[]temp0result0if len(s)0:re…

字符串指令集

字符串指令的格式 例子1就成功发送了指令 例子2就是发送的字符串有误 查询当前位置就会在附加信息中返回当前座位的坐标 第一个指令的参数就是闪灯的两个参数 如第一个示例就是10ms On Time 第二个就是Off Time 使用标准库来接收字符串命令 字符串指令的接收 因为一个指令就是…

科技改变旅游,道观漫游可视化:智能化管理助力道观游览

道观漫游可视化是一种通过技术手段实现道观游览的可视化展示方式&#xff0c;让游客能够更加直观地了解道观的历史、文化和建筑特色。 随着旅游业的不断发展&#xff0c;道观漫游可视化已经成为了旅游行业中的一个重要方向&#xff0c;吸引了越来越多的游客前来体验。 道观漫游…