两步认证技术

什么是两步认证

在介绍两步认证之前,首先来看下目前主流的几种认证方式。

[
](https://dn-coding-net-production-pp.codehub.cn/ebc07cdb-948e-434f-ae4f-
d14a81c7893d.png)

上图中的认证方式大体上可以分为三大类

1.You know : 比如密码,这种只有我们知道的

2.You are : 比如指纹,这种只有我们拥有的

3.You have : 比如信用卡,这种只属于你的

了解上面所说的三大类之后,我们就很容易的理解下面几点。

什么是两步认证?

两步验证,对应的英文是 Two-factor Authentication(2FA),从名字可以看出,「两步」是 2FA 的重点,而两步就是我们上面所提到的
You know + You Have ,也就是 密码 + 一次性密码(One Time Password,OTP)

为什么我们需要两步验证?

传统的密码认证方式,如果在用户名密码在其他网站上泄露,任何用户都可以使用你的账号密码进行登陆做任何操作。但有了两步认证就能在一定程度上有效的避免这种情况的发生。因为在每次登录时,不仅需要输入您的帐户密码,还需输入移动设备为您生成的一次性动态验证码

OTP

前面说过,2FA 中使用的是一次性密码(One Time Password,OTP),也被称作动态密码。一般 OTP 有两种策略:HOTP ( HMAC-
based One Time Password) 和TOTP ( Time-based One-time Password)
。目前被广泛使用的正是后者这种基于时间的动态密码生成策略。

算法大体是这样:

  1. 客户端和服务器事先协商好一个SECRET,用于一次性密码的生成过程,此密钥不被任何第三方所知道。此外,客户端和服务器都采用时间做计数器。

  2. 客户端对密钥和计数器的组合(SECRET,time/30)使用HMAC(Hash-based Message Authentication Code)算法计算一次性密码,公式如下:HMAC-SHA-1(SECRET, time/30)

  3. 各种算法加特效后成6位数字

在这里就只简要介绍该算法,如想深入了解

==> https://tools.ietf.org/html/rfc6238

基于TOTP的密码有如下特点

  1. 无需记忆,不会产生password这样的泄漏问题

  2. 动态生成,每30s生成一个,安全性大大提高

  3. 对网络无要求,离线下仍可正常使用

  4. 成本低,无需购买硬件和软件

两步认证流程

  1. 服务器随机生成一个类似于『DPI45HKISEXU6HG7』的密钥,并且把这个密钥保存在数据库中。

  2. 在页面上显示一个二维码,内容是一个URI地址(otpauth://totp/账号?secret=密钥?issuer=厂商)

  3. 客户端扫描二维码,把密钥『DPI45HKISEXU6HG7』保存在客户端。

  4. 客户端每30秒使用密钥『DPI45HKISEXU6HG7』和时间戳通过TOTP『算法』生成一个6位数字的一次性密码

两步认证的其他情况

然后仅仅只是做好上面这种正常的验证流程是不够的,Coding 的两步认证在一些异常情况下做了很多处理。

  1. 生成的 OPT 应该是不能复用的,也就是用户在登陆或者危险操作时输入完一次 OPT 后,手机端 OPT 仍然未刷新时,在进行登陆或者危险操作时输入刚才的OPT是无效的,必须等待手机上OPT的刷新。

  2. 既然可以离线使用,那么怎么保证时间的差异性,我们服务端会兼容服务器时间的前后30s。从而有效的避免细微时间上差异而导致的验证失败,同时也避免了用户刚输入完 OPT 后还未做提交操作时 OPT 刷新而引起验证失败。

  3. 在遇到使用遍历所有6位数数字进行暴力破解 OPT 时,我们会对错误次数进行限制,90s 限制其错误次数能有效避免暴力破解的出现。

  4. 在开启两部认证后,其他所有登陆的客户端都会因为开启两部认证而过期,必须重新登陆。

两部认证的实现

目前 Coding 采用的 https://github.com/wstrange/GoogleAuth 实现的TOTP 算法,在提供 TOTP
算法之外,该库提供了可以存储用户 secret 的接口,采用的 ServiceLoader 方式,ServiceLoader
方式可以通过链接了解更多==>http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html


在对外网开放的后台管理系统中,使用静态口令进行身份验证可能会存在如下问题:

    (1) 为了便于记忆,用户多选择有特征作为密码,所有静态口令相比动态口令而言,容易被猜测和破解;

    (2) 黑客可以从网上或电话线上截获静态密码,如果是非加密方式传输,用户认证信息可被轻易获取;

    (3) 内部工作人员可通过合法授权取得用户密码而非法使用;

静态口令根本上不能确定用户的身份,其结果是,个人可以轻松地伪造一个假身份或者盗用一个已有使用者的身份,给企业造成巨大的经济和声誉损失。本文主要介绍并实现了一种动态口令(OTP)的实现方式。

  动态口令(OTP,One-Time
Password)又称一次性密码,是使用密码技术实现的在客户端和服务器之间通过共享秘密的一种认证技术,是一种强认证技术,是增强目前静态口令认证的一种非常方便技术手段,是一种重要的双因素认证技术,动态口令认证技术包括客户端用于生成口令产生器的,动态令牌,是一个硬件设备,和用于管理令牌及口令认证的后台动态口令认证系统组成。

otp从技术来分有三种形式, 时间同步、事件同步、挑战/应答。

(1) 时间同步

原理是基于 动态令牌动态口令验证服务器的时间比对,基于
时间同步令牌,一般每60秒产生一个新口令,要求服务器能够十分精确的保持正确的时钟,同时对其令牌的晶振频率有严格的要求,这种技术对应的终端是硬件令牌。

(2)事件同步

基于事件同步的令牌,其原理是通过某一特定的事件次序及相同的种子值作为输入,通过HASH算法中运算出一致的密码。

(3)挑战/应答

常用于的网上业务,在网站/应答上输入 服务端下发的
挑战码动态令牌输入该挑战码,通过内置的算法上生成一个6/8位的随机数字,口令一次有效,这种技术目前应用最为普遍,包括刮刮卡、短信密码、动态令牌也有挑战/应答形式。

使用阿里云身份宝(或者Google Authenticator)时间同步实现OTP动态口令

如上图,是一种基于时间同步的OTP计算方式,是通过客户端和服务器持有相同的密钥并基于时间基数,服务端和客户端采用相同的Hash算法,计算出长度为六位的校验码。当客户端和服务端计算出的校验码相同是,那么验证通过。

  由于客户端需要存储密钥和计算校验码的载体,阿里云的身份宝(或者Google
的Authenticator)提供了手机端的APP进行密钥存储和校验码计算。下面我们以这两款客户端为例,实现在应用采用OTP进行权限验证,主要流程如下图:

流程关键代码如下,(更详细代码,请Git下载:https://github.com/suyin58/otp-demo

1 用户注册:

1.1 生成OTP密钥:

1
2
3
String secretBase32 = TotpUtil.getRandomSecretBase32(64);

oper.setOtpSk(secretBase32);

1.2 生成OTP扫描用字符串:

约定字符串格式如下:

1
2
String totpProtocalString = TotpUtil.generateTotpString(operCode, host,
secretBase32);

1.3 将1.2中生成的字符串生成二维码,通过邮件发送给用户

1
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
String host = "otptest@wjs.com"; // 自定义

            String totpProtocalString = TotpUtil.generateTotpString(operCode,
host, secretBase32);

            String filePath = f_temp;

            String fileName = Long.toString(System.currentTimeMillis()) +
".png";



            try{

                QRUtil.generateMatrixPic(totpProtocalString, 150, 150,
filePath, fileName);

            }catch (Exception e){

                throw new RuntimeException("生成二维码图片失败:" + e.getMessage());

            }


            String content = "用户名:"+operCode+"</br>"


                    +"系统使用密码 +
动态口令双因素认证的方式登录。</br>请按以下方式激活手机动态口令:</br>安卓用户请点击<a
href='http://otp.aliyun.com/updates/shenfenbao.apk'>下载</a>,"

+"</br>苹果手机在AppStore中搜索【身份宝】(Alibaba)。下载安装后,通过扫描以下二维码激活动态口令。</br>"

                    +"<img src=\"cid:image\">";

            EmailBaseLogic emailBaseLogic = new EmailBaseLogic();

//            String to, String title, String content, String imagePath

            emailBaseLogic.sendWithPic(email,"账户开立通知", content, filePath + "/"
+ fileName);

1.4 将用户注册信息与1.1的OTP密钥存储到数据库中

数据存储代码(略)

2 客户端工具使用

2.1 下载APP

安卓用户下载地址:http://otp.aliyun.com/updates/shenfenbao.apk

苹果手机在AppStore中搜索【身份宝】(Alibaba),或者Google Authenticator

2.2 扫描二维码

使用下载的APP,扫描1.3邮件中的二维码,客户端获取密钥。APP使用密钥基于时间算出6位校验码(每分钟变化)。

1 用户登录

客户端输入登录用户名、用户密码,以及2.2客户端工具中的6位校验码。

1.1 服务端根据用户名和用户密码获取用户信息和密钥

代码参考略

1.2 服务端使用密钥基于时间算出6位校验码

1
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
String secretHex = "";

        try {

            secretHex = HexEncoding.encode(Base32String.decode(secretBase32));

        } catch (Base32String.DecodingException e) {

            LOGGER.error("解码" + secretBase32 + "出错,", e);

            throw new RuntimeException("解码Base32出错");

        }

        long X = 30;

        String steps = "0";

        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        df.setTimeZone(TimeZone.getTimeZone("UTC"));

        long currentTime = System.currentTimeMillis() / 1000L;

        try {

            long t = currentTime / X;

            steps = Long.toHexString(t).toUpperCase();

            while (steps.length() < 16) steps = "0" + steps;

            return generateTOTP(secretHex, steps, "6",

                    "HmacSHA1");

        } catch (final Exception e) {

            LOGGER.error("生成动态口令出错:" + secretBase32, e);

            throw new RuntimeException("生成动态口令出错");

        }

1.3 比较客户端和客户端校验码是否一致

代码参考略

其他,Demo中的例子可以使用身份 + 密码,先进行密码验证,在通过动态口令进行二次验证,使系统登录更加安全可靠。


Google
Authenticator是谷歌推出的一款动态口令工具,旨在解决大家Google账户遭到恶意攻击的问题,在手机端生成动态口令后,在Google相关的服务登陆中除了用正常用户名和密码外,需要输入一次动态口令才能验证成功,此举是为了保护用户的信息安全。那么,Authenticator采用了哪些算法?又是如何实现的?且看本文技术解读。

很多手机用户会使用 [Google Authenticator](https://code.google.com/p/google-
authenticator/)(谷歌身份认证)来生成认证令牌,与传统单因子密码不同,其采用的是更安全的双因子(2FA two-factor
authentication
)认证。FA是指结合密码以及实物(信用卡、SMS手机、令牌或指纹等生物标志)两种条件对用户进行认证的方法。只需要在手机上安装如此高大上的密码生成应用程序,就可以生成一个随着时间变化的一次性密码,用于帐户验证,而且这个应用程序不需要连接网络即可工作。

实际上Google Authenticator采用的算法是TOTP(Time-Based One-Time
Password基于时间的一次性密码),其核心内容包括以下三点:

一个共享密钥(一个比特序列);

当前时间输入;

一个签署函数。

共享密钥

共享密码用于在手机端上建立账户。密码内容可以是通过手机拍照二维码或者手工输入,并会被进行base32加密。

手工密码的输入格式如下:

xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx

包含该令牌的二维码的内容是一个URL:

otpauth://totp/Google%3Ayourname@gmail.com?secret=xxxx&issuer=Google

时间输入(当前时间)

输入的时间值来自于手机本身,一旦我们获得密钥后,就无需与服务器再进行通信了。但是最重要一点是务必确保手机上的时间是正确的,因为往后的步骤服务器会多次重复使用之前得到的时间值,服务器只会认准这个值。进一步说,服务器会比对所有提交的令牌以确认哪一个是你输入并提交的。

签署

签署所使用的方法是HMAC-SHA1。HMAC的全称是Hash-based message authentication
code(哈希运算消息认证码),以一个密钥和一个消息为输入,生成一个消息摘要作为输出,这里以SHA1作为消息输入。使用HMAC的原因是:只有用户本身知道正确的输入密钥,因此会得到唯一的输出。其算法可以简单表示为:

hmac = SHA1(secret + SHA1(secret + input))

事实上,TOTP是HMAC-OTP(基于HMAC的一次密码生成)的超集,区别是TOTP以当前时间作为输入,而HMAC-
OTP以自增计算器作为输入,该计数器使用时需要进行同步。

算法

首先,要进行密钥的base32加密。虽然谷歌上的密钥格式是带空格的,不过base32拒绝空格输入,并只允许大写。所以要作如下处理:

1
2
3
original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx

secret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))

第二步要获取当前时间值,这里使用的是UNIX
time
函数,或者可以用纪元秒。

1
input = CURRENT_UNIX_TIME()

在Google Authenticator中,input值拥有一个有效期。因为如果直接根据时间进行计算,结果将时刻发生改变,那么将很难进行复用。Google
Authenticator默认使用30秒作为有效期(时间片),最后input的取值为从Unix epoch(1970年1月1日
00:00:00)来经历的30秒的个数。

1
input = CURRENT_UNIX_TIME() / 30

最后一步是进行HMAC-SHA1运算

1
2
3
4
5
6
7
original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx

secret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))

input = CURRENT_UNIX_TIME() / 30

hmac = SHA1(secret + SHA1(secret + input))

至此,2FA所需的两个因子都已准备就绪了。但是HMAC运算后的结果会是20字节即40位16进制数,应该没有人会愿意每次都输入这么长的密码。我们需要的是常规6位数字密码!

要实现这个愿望,首先要对20字节的SHA1进行瘦身。我们把SHA1的最后4个比特数(每个数的取值是0~15)用来做索引号,然后用另外的4个字节进行索引。因此,索引号的操作范围是15+4=19,加上是以零开始,所以能完整表示20字节的信息。4字节的获取方法是:

1
four_bytes = hmac[LAST_BYTE(hmac):LAST_BYTE(hmac) + 4]

然后将它转化为标准的32bit无符号整数(4 bytes = 32 bit):

1
large_integer = INT(four_bytes)

最后再进行7位数(1百万)取整,就可得到6位数字了:

1
2
3
large_integer = INT(four_bytes)

small_integer = large_integer % 1,000,000

这也是我们最后要的目标结果,整个过程总结如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx

secret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))

input = CURRENT_UNIX_TIME() / 30

hmac = SHA1(secret + SHA1(secret + input))

four_bytes = hmac[LAST_BYTE(hmac):LAST_BYTE(hmac) + 4]

large_integer = INT(four_bytes)

small_integer = large_integer % 1,000,000

我在这里准备了一个完整可执行的GO语言程序,感兴趣的朋友请点击点击 [这里](https://github.com/robbiev/two-factor-
auth)进行查看。

英文出自: [Garbagecollected](http://garbagecollected.org/2014/09/14/how-google-
authenticator-works/)


当你使用网银时,网站要求提供六位数动态口令。那么网站是如何验证这六位数是不是正确的呢?验证原理是什么?

令牌实际相当于一个密码本,输进去AAA得到BBB,BBB是正确答案,验证通过。实际用的时候还会令牌会有一个时间有效性的问题,在不同的时间里输入AAA得到的答案是不同的,服务器端认为的正确答案是在随时间变动的,前一分钟有可能是DDD,后一分钟可能是MMM,一般在一个有效时间段(一般为一分钟)才会得到的答案BBB,

每个令牌都有不同ID,帐号先与令牌ID绑定,令牌会根据自身的特定ID与当前时间来计算出6位的随机码。服务器端程序因为
有了令牌ID,所以也可以根据这个令牌的特征和当前时间来生成同样的随机码,然后你提交令牌生成的验证码,服务器会验证与它自己生成的是否一致,一致就通过,不一致就提示错误……

动态口令牌使用唯一的128位种子将其初始化;其内部芯片每分钟都会使用一种算法,组合该种子与当前时间,生成一个随机的数字。而在认证服务器则采取和这个动态密码器同一种算法产生该随机数字,保证动态密码器和我行网银服务器的单一认证,就像每个客户都有了世界上独一无二的身份认证,保证客户使用网银的安全性。

服务器端和每个对应的“动态口令牌”都使用同样的一套算法,可以自定义计算数组的时间间隔。每批次“动态口令牌”都拥有唯一的序列号,然后服务器端和“动态口令牌”执行相同的计算程序,在设定好的相同的更新时间计算出新的数组.

其实上网输入密码服务器验证时跟这个动态口令牌没有有直接物理联系,唯一有联系就是二者根据唯一的序列号,利用公式,你算你的,我算我的。但同一时间算出的数字都是一样的,要不就会验证不能通过,这个6位阿拉伯数字的计算过程中时间是一个很重要的参数,使用时间参数时2者的时间必须要保持一致,要不就会时间不同步导致动态口令牌失效!

对于失去时间同步的令牌,目前可以通过增大偏移量的技术(前后10分钟)来进行远程同步,确保其能够继续使用,降低对应用的影响,但对于超出默认(共20分钟)的时间同步令牌,将无法继续使用或进行远程同步,必须返厂或送回服务器端另行处理。

原理

动态密码的密码其实不是随机的,而是由规律的。当下动态密码分为两类,时间性和事件性。何为时间性动态密码?该类令牌产出动态密码是以时间为参数的,而事件性一般以使用次数为参数的。我们以时间性动态为主要说明对象。整个验证的过程如下:

1.动态密码令牌产生动态密码 以时间和种子为参数,进行迭代,得出动态密码,这里的时间一般是秒数。每个时间性动态密码令牌中会内置一个时钟芯片。

2.服务器校验动态密码。 服务器读取系统时间加上种子,以相同的迭代方法得出动态密码,然后双方进行比对。

讲到这边,可能有所怀疑难道令牌的时间和服务器的时间一定会一致吗?我的答案肯定是不一致的。那怎么能检验的过去呢?原来很简单,服务器校验是是在一个时间区间里校验的,比如现在是12:00,服务器会生成11:55-12:05中所有的动态密码,然后和令牌产生的动态密码比对,这样不就解决了时间不一致的问题了。另外服务器会把令牌和服务器相差的时间记录下来,下次检验的会先把这个偏移值记录下来,以减少动态密码迭代次数,这样就完成了另外一个比较重要的功能,偏移值自动调整。

动态口令,又叫动态令牌、动态密码。它的主要原理是:
用户登录前,依据用户私人身份信息,并引入随机数产生随机变化的口令,使每次登录过程中传送的口令信息都不同,以提高登录过程中用户身份认证的安全性。

  银行通常提供给用户两种动态口令:
一种是固定数量的动态口令,最常见的就是刮刮卡。用户每次根据银行提示,刮开卡上相应区域的涂层,即可获得一个口令。刮刮卡成本低廉,使用方法简单,因此很多银行采用这种方法,如工商银行;
另一种是硬件形式的动态口令,即电子令牌,它采用专用硬件,每次可以用自带的密码生成芯片得到一个当前可用的一次性动态密码,交通银行等就采用这种方式。一般来讲,每个客户端的电子令牌都有一个唯一的密钥,该密钥同时存放在服务器端,每次认证时令牌与服务器分别根据同样的密钥,同样的随机数和同样的算法计算出认证时的动态口令,从而确保口令的一致性和认证的成功。因每次认证时,随机数的参数不同,所以每次产生的动态口令也不同。每次计算时参数的随机性保证了每次口令不可预测,以保证系统安全。

OTP与常用认证技术比较

目前在信息系统中使用的增强型认证技术包括:

1 USBKey: 申请PKI证书。

2 动态口令卡:打印好的密码刮刮卡。

3 动态短信:使用电信通道下发口令。

4 IC卡/SIM卡:内置与用户身份相关的信息。

5 生物特征:采用独一无二的生物特征来验证身份,如指纹。

6 动态令牌:动态口令生成器和认证系统。



动态口令认证技术不足

动态口令认证技术没有第3方权威机构认证,如果业务应用系统安全策略不完善的情况下,可能会受到中间人攻击。如某某银行使用时间型动态令牌受到网络钓鱼攻击。建议在应用中完善安全使用策略,划清口令使用权限,加强交易系统流程控制,用以提高系统安全性。

otpauth://totp/oa?secret=63985989418859891633&period=60&digits=8

secret:密钥,也就是上面生成的seed

period:每60秒生成一次

digits:生成的随机码长度


Golang的实现

https://www.jianshu.com/p/e4b574be0ba6

Java服务端的实现:

https://github.com/wstrange/GoogleAuth

https://github.com/suyin58/otp-demo

Python的实现:

https://github.com/sahands/python-totp

Flutter、Dart的实现:

https://github.com/vubon/dart-totp

算法原理:

https://blog.csdn.net/qq_29951983/article/details/80814272

Measure

Measure

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×