
背景
美国五星上将麦克·阿瑟
曾经说过,伯恩云盘不能没有2FA,就像西方不能没有耶路撒冷。
在数字化的世界中,数据泄露和网络攻击已经成为了常态,因此我们需要采取主动措施
来保护我们的数据和隐私,而不是被动预防
。
所以,为伯恩云盘启用双因素身份验证(2FA 两步验证)刻不容缓,就像为马伯恩的小站启用HTTPS协议一样。
关键词
- 伯恩云盘 最好用的私人云盘,没有之一。
- Microsoft Authenticator 可不得白嫖一次巨硬
- 树莓派
还有2个项目没迁移完,不打算迁移了 - Debian Server 物美价廉的云服务器
- 马伯恩的小站
伯恩云盘的2FA
Why 2FA?
随着网络攻击的兴起,传统密码不再像以前那样安全。
数据的泄露也越来越常见,大量的用户名和密码被泄露,这得益于许多人在不同的服务上使用相同的账号和密码,当然我之前也是。
虽然我可以通过KeePass的password generator来生成复杂到无法破解的密码,但是KeePass的证书文件是存储在伯恩云盘上的,如果伯恩云盘寄
了,那我所有的账号密码可不都得危
。
于是,2FA应运而生。
2FA,全称Two Factor Authentication
,中文名叫双重因素认证
。
字面意来理解,双重因素认证,即认证需要用到两种因素。比如,你在银行 ATM 取钱的时候既要你的银行卡,也需要只有你才知道的六位数密码,这便是双重因素认证。
我们常见的手机验证码它也是属于2FA的一种方式,当我们在进行敏感操作时(修改密码),首先需要知道我们的用户名,然后需要下发一条手机验证码到我们的手机上,目的是为了验证我们对手机的所有权,这也是属于双重因素认证的一种方式。
中国大陆由于实名制的关系,几乎所有网站注册都需要使用手机号,而国内用户也没有使用手机令牌的习惯,因此,国内最常见的 2FA 就是手机短信验证码。
但国外的服务就不一样了,比如谷歌或者微软的服务,我们可以选择使用Authenticator手机令牌,它使用到的技术叫做 TOTP(Time-based One-time Password,中文名:基于时间的一次性口令),简单来说就是,当我们将账户和验证软件绑定之后,在一定时间内,验证软件会生成一串数字(一般是六位数),只要将这串数字输入到目标网站,即可完成认证。
如何在伯恩云盘(nextcloud)中启用2FA?
点击个人头像,找到设置——应用:
找到 Two-Factor TOTP Provider,并选择启用:
在nextcloud端启用TOTP
找到TOTP的应用提供者,例如谷歌或者微软,进行扫码登记:(以Microsoft authenticator为例)
验证账户:
大功告成,你以为这种就完了?
是的,用户的操作部分已经完了,优秀的产品总是可以通过简单便捷的操作完成复杂的功能。
TOTP的原理
为什么authenticator这个应用生产的一次性代码能和我们的服务器上的相关联呢?
刚好我有一个测试网站:2FA % TOTP测试
我将二维码的信息公开到红框内,可以看到这个二维码其实是用来做auth验证的,包含了secret和issuer的信息。
otpauth://totp/Passcode?secret=6shyg3uens2sh5slhey3dmh47skvgq5y&issuer=xiaomaTest
启用2FA的过程
当我们想要启用2FA的时候,首先我们需要扫描服务器提供的二维码,也就是上图的二维码,服务器会给我们颁发一个secret,通过和当前时间的计算(HMAC-SHA1算法),得出6位验证数字:
核心代码如下:
function updateCode() {
const T = Math.floor(Date.now() / 1000 / 30)
// 用户密钥
const K = arrayToBinary(Array.from(new Uint8Array(base32Decode('6shyg3uens2sh5slhey3dmh47skvgq5y'.toUpperCase(), 'RFC4648'))))
/**
* 数字转 Int64 字节流
* @param {number} num
* @returns
*/
function intToBytes(num) {
const bytes = [];
for (let i = 7; i >= 0; i--) {
bytes[i] = num & 255;
num = num >> 8;
}
return bytes;
}
/**
* @param {string} binaryStr
*/
function binaryToArray(binaryStr) {
return binaryStr.split('').map(char => char.charCodeAt())
}
function arrayToBinary(array) {
return array.map(char => String.fromCodePoint(char)).join('')
}
function arrayToHex(array) {
return array.map(char => char.toString(16)).join('')
}
const T1 = arrayToBinary(intToBytes(T))
// 计算 HMAC-SHA1,密钥为 K,消息为 T
const HS = binaryToArray(CryptoJS.HmacSHA1(CryptoJS.enc.Latin1.parse(T1), CryptoJS.enc.Latin1.parse(K)).toString(CryptoJS.enc.Latin1))
// 取出最后个字节的第 4 位
const offset = HS[19] & 0xf
// 将从 offset 开始的四个字节按大端组装为整数
let bytes = (HS[offset] & 0x7f /** 这里是为了忽略符号位 */) << 24
| HS[offset + 1] << 16
| HS[offset + 2] << 8
| HS[offset + 3]
// 整数转字符串,然后取出后六位
let code = bytes.toString().slice(-6);
// 不足 6 位数则补 0
for (let i = 0; i > 6 - code.length; i++) {
code = '0' + code;
}
code.split('').forEach((c, i) => {
$("#code-box").children()[i].innerHTML = c
})
}
我们手机验证器的secret和服务器的secret是一致的,只要保证它们的时间一致,就可以生成有时效性且相同的6位验证码。
这样即使我们的密码泄漏了,也不用担心数据被泄露,因为二次验证可以有效的保护我们的数据。
结束语
有的没的。
