导语:之前做过一个小项目,其中用到了F2A(双因素认证)进行登录以及其他的用户身份认证,现在就这个功能的原理以及实现做一个总结。
# 目录
- F2A原理
- 实现方法
- 实战演练
# F2A原理
F2A就是双因素认证的缩写,一种采用时间同步技术的系统,采用了基于时间、事件和密钥三变量而产生的一次性密码来代替传统的静态密码。
大家都知道一般网站验证用户身份都是采用单因素认证,比如:用户名+密码,还有的是手机号+短信验证码。
但是用户+密码的容易被黑客爆破,脱库,撞库进行获取;而短信验证码也容易被伪基站,钓鱼网站,WIFI等手段获取到,从而泄漏用户隐私信息。
今天介绍的这个F2A就是在使用了上述提到的方法外,再另外加一层防护网,对用户登录进行二次验证,验证码是基于用户密钥独立生成的,随机生存的6位数。
而且是在独立的APP专门设备上面存放,只有你自己可以查看,而且这个验证码是60秒切换一次,每次都不一样,加大了黑客破解的时间难度。
# 实现方法
继续打开上次的demo
文件夹,然后创建一个f2a
文件夹,写入一个文件index.js
。
这次要实现的功能包括以下几点:
- 生成二维码功能;
- 生成F2A密钥
- 验证F2A密码
# 生成二维码功能
这个功能比较简单,安装一个qrcode
就可以了。
$ npm install qrcode --save
const express = require('express');
const app = express();
const qrcode = require('qrcode');
app.get('/f2a/qrcode', (req, res) => {
let url = req.query.url;
if (!url) {
return res.json({
code: 101,
msg: 'get_fail',
data: {
info: "url不能为空!"
}
});
}
qrcode.toDataURL(url, (err, data) => {
return res.send('<img src="' + data + '">');
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
打开游览器,随便输入一个网址参数,来测试一下。
这里使用百度一下:http://localhost:3000/f2a/qrcode?url=https://www.baidu.com/
。
这里是预览截图:
# 生成F2A密钥
接下来就是今天的主角登场,F2A双因素。
先来下载一个依赖包,安装一下引入文件。
$ npm install speakeasy --save
获取F2A
这里主要使用generateSecret
来创建F2A密钥信息;
const express = require('express');
const app = express();
const speakeasy = require('speakeasy');
app.get('/f2a', (req, res) => {
let secret = speakeasy.generateSecret({length: 20});
return res.json({
code: 200,
msg: 'get_succ',
data: {
info: '获取成功',
secret: secret.base32,
url: secret.otpauth_url,
}
});
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
其中secret
就是你的个人密钥,url
就是你的F2A设备要扫描的地址。
接下来在游览器看一下效果。
# 验证F2A密码
这里主要使用totp.verify
方法来验证六位数字是否为正确。
app.post('/f2a', (req, res) => {
let token = req.body.code;
let secret = req.body.secret;
if (!token) {
return res.json({
code: 101,
msg: 'get_fail',
data: {
info: "验证码不能为空!"
}
});
}
if (!(/^\d{6}$/.test(token))) {
return res.json({
code: 101,
msg: 'get_fail',
data: {
info: "验证码格式不正确!"
}
});
}
if (!secret) {
return res.json({
code: 101,
msg: 'get_fail',
data: {
info: "密钥不能为空!"
}
});
}
let verifyInfo = {
secret,
encoding: 'base32',
token,
}
let verify = speakeasy.totp.verify(verifyInfo);
if (verify) {
return res.json({
code: 200,
msg: 'get_succ',
data: {
info: "验证成功!"
}
});
} else {
return res.json({
code: 101,
msg: 'get_fail',
data: {
info: "验证失败!"
}
});
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
其实这个依赖包还有个生成随机六位数的方法。
app.get('/code', (req, res) => {
let secret = req.query.secret;
if (!secret) {
return res.json({
code: 101,
msg: 'get_fail',
data: {
info: "密钥不能为空!"
}
});
}
var token = speakeasy.totp({
secret,
encoding: 'base32'
});
return res.json({
code: 200,
msg: 'get_succ',
data: {
info: "获取成功!",
code: token,
}
});
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
打开http://localhost:3000/f2a/code?secret=F44T62DGOJJT66TLH5WGWVBQJ47XUZJO
,会出现一个六位数,这个就是生成的,大约有60秒有限期。
这个code里面的六位数其实和F2A应用的六位数是等效的,一样的,你也可以使用这个方法做一个类似的小程序,扫码绑定,显示六位数,这也是可以的。
# 实战演练
现在F2A的功能基本上是实现了,但是想要体验一下,你必须准备好F2A应用才可以。
这里推荐三个比较常用F2A应用:
- 微信小程序:二次验证码
有了应用后,
# 绑定F2A
先打开那个网址,由于是get请求,直接在游览器打开,获取到密钥和地址。
现在已经出来了,
- 密钥是secret的值:
F44T62DGOJJT66TLH5WGWVBQJ47XUZJO
, - 地址是url的值:
otpauth://totp/SecretKey?secret=F44T62DGOJJT66TLH5WGWVBQJ47XUZJO
接下来有两种方法绑定:
- 直接在F2A应用输入secret里面;
- 扫描二维码更方便(省去输入环节);
接下来采用第二种方法。
在游览器打开http://localhost:3000/f2a/qrcode?url=otpauth://totp/SecretKey?secret=F44T62DGOJJT66TLH5WGWVBQJ47XUZJO
,看到一个二维码,使用F2A应用扫描后,成功绑定。
# F2A验证
这次使用postman进行验证,打开postman软件,输入url
以及参数。
点击发送按钮,可以如下图,验证成功。
以上就是使用node实现F2A功能的一个基本的使用方法总结。