多因素身份认证之手机推送认证

多因素身份认证 (MFA) 是保护企业 IT 资源访问安全的一种关键工具,也是零信任安全模型的核心组成。特别在远程办公以及数据泄露事件层出不穷的背景下,越来越多企业都开始考虑实施多因素身份认证策略。

根据近期一项对 IT 专业人员的调查,52.6%的中小型企业已经要求为所有应用程序和系统登录添加多因素身份认证。其中手机推送认证形式(无密码认证)的二次认证是保证良好用户体验的绝佳选择,但与其他认证形式相比又如何呢?

本文将从企业实施的角度分析多因素身份认证的最佳实践,希望能找到同时满足决策者和终端用户的方案。

1. MFA 是什么?企业为什么要使用?

多因素身份认证也称多因子身份认证(Multi-Factor Authentication,MFA),是在传统登录认证时使用的用户名密码之外增加额外认证因素的做法。通常,这些因素可以分为以下三类:

  • “用户知道的”:包括用户名密码凭证,以及密保安全问题等
  • “用户拥有的”:与设备相关,例如手机上的令牌或硬件令牌
  • “用户天生的”:一般指生物识别技术,人脸、指纹、虹膜。

根据美国最大通信公司 Verizon 在2021年发布的《数据泄露调查报告》显示,61%的数据泄露都涉及密码凭证。由此可见,密码作为单一验证因素时并不充分,企业还是会因数据泄露造成损失。要解决这一问题,在登录时添加二次认证就能大大提升安全性,数据表明多因素身份认证能使账号被盗的可能性降低99.9%。那么用于二次认证的因素对于安全保护的效果是否有不同影响呢?对此,谷歌安全博客发现,不同形式的验证因素对于账号接管攻击(ATO)的防护效果确实存在差异:

上图详细说明了六种最常用多因素身份认证因素的防护效果对比,主要分为基于设备和基于知识两大类。可以看到基于设备的验证因素中,设备安全提示的防护效果达到了100%,而手机推送认证(无密码认证)就属于设备安全提示这一类别,下面将具体介绍手机推送认证形式,以及与其他形式的验证因素相比有何优势。

2. 什么是手机推送形式的多因素身份认证?

手机推送形式的多因素身份认证需要用户持有智能手机,并利用手机上的消息推送功能来验证用户身份,因此属于“基于设备”的多因素身份认证,因其无需输入动态密码,也属于无密码认证形式。在 PC 端登录应用或系统时,用户在输入用户名和密码后,只需解锁手机并在推送出来的新消息中点击确认按钮就能批准 PC 端的访问请求。

这一验证形式对于终端用户来说十分方便,对 IT 管理员来说也降低了部署成本,因而广受欢迎。在简单说明了手机推送验证的过程后,下面将介绍除了手机推送以外的其他验证因素,进一步比较各自的优缺点。

3. 推送认证以外的 MFA 因素
 

     1)短信验证

短信验证也是如今广泛采用的一种验证因素。用户提交密码凭证后,会收到短信或邮件形式的验证码,用户还需要输入正确的验证码以后才能完成登录。

从上一节的图表可以看出,短信验证能有效防止自动和批量的网络钓鱼攻击,但难以应对针对性的账号攻击,原因在于短信验证需要第三方网络在验证服务器和终端用户之间充当中间人,为网络攻击提供了更多可操作空间,增加了安全风险。

相比之下,手机推送验证是直接在用户的智能手机上使用验证器APP实现的,不需要用户输入验证码,不仅优化了用户体验,也节省了验证时间。与短信验证相比,使用手机推送验证的用户每年在登录上花费的时间要少13分钟。

    2)时间型动态口令(TOTP)

时间型动态口令(TOTP)是一种随机生成的身份验证令牌形式,通常是通过智能手机APP创建,因此和手机推送一样也属于“基于设备”的验证因素。

动态口令每隔一段时间不断刷新,用户必须在刷新前输入正确的验证码,否则就会失效。这相当于在系统和攻击者之间增加了大型缓冲区,攻击者需要同时获取用户凭证和持有手机上的动态口令才能破解账号,因此动态口令比短信验证更安全,也更容易管理。

然而,有些用户认为动态口令过于繁琐,体验不佳。但是多因素身份认证已经为用户添加了额外验证步骤,而动态口令与其他形式的验证因素相比已经是相对轻巧有效的方案了。

手机推送和动态口令在安全性上大致相同,在用户体验方面稍稍优于后者。一般而言,身份验证器APP都会同时支持这两种形式,允许用户按需选择,这在部署多因素身份认证中是很重要的考量。

    3)硬件令牌

硬件令牌就像数字版的密钥锁。用户持有的每个硬件令牌都内置独一无二的密钥,只要用户不丢失,身份验证就应该是高度安全的。谷歌的数据图也证明了硬件令牌的安全性,可以完全阻止几种主要的账号攻击。硬件令牌虽然安全性较高,但在部署上也有不少问题:成本高、维护难、易丢失。维护难是指实施和绑定硬件令牌工作量巨大;易丢失会导致终端用户账号被持续锁定,只有找回令牌才能解锁。

相比之下,手机推送的验证形式性价比更高,更适合大规模实施,安全性也几乎没有差别。

    4)生物识别

过去,生物识别似乎只存在于科幻小说,主要有视网膜扫描和语音或面部识别。如今,生物识别已经成为现实。很多人都习惯用指纹识别或面部识别解锁手机或笔记本电脑。 尽管在目前的企业环境中大规模管理生物识别数据仍然存在很多问题,但通过手机上的身份验证器APP,生物识别也自然而然加入了验证环节中。例如,用户要接收推送通知必须先解锁手机,而现在大多数人解锁手机都会用到生物识别,相当于设备自带的额外安全层。

3. 如何选择手机推送的多因素身份认证方案?

IT 企业可以选择多种解决方案在用户群中强制执行手机推送的认证策略。而在考虑采用哪种解决方案时,需要重点关注以下三个问题:

  • 有哪些 IT 资源需要保护?
  • 所选方案如何与现有工具交互?
  • 方案成本是多少?

基于以上三点,身份目录即服务(DaaS)可提供更适合现代化 IT 架构的解决方案。基于 SaaS 模式,提供云 MFA 能力,具备目录服务、RADIUS 认证模块、统一策略管控中心,管理员可以轻松管理几乎所有 IT 资源的身份认证,包括云和本地资源。

其中,云 MFA 模块支持手机推送认证、手机APP 令牌等多种认证形式,兼容 iOS 和安卓系统,不仅适合云应用和本地应用的手机推送认证,也可用于VPN、云桌面等网络设备的身份验证,免去安装部署过程,按需订阅付费,更经济更高效。

(本文来源于宁盾,仅供学习和参考,未经授权禁止转载和复制。如欲了解多因素身份认证更多内容,可前往宁盾官网博客解锁更多干货)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现Spring Boot + Vue + WebSocket带Token身份认证推送消息,可以按照以下步骤进行: 1.在Spring Boot中配置WebSocket,包括WebSocket配置类、WebSocket处理器、WebSocket拦截器等。 2.在Vue中使用WebSocket连接到后端WebSocket服务,可以使用Vue-WebSocket插件来实现。 3.在后端WebSocket连接建立时,验证用户身份,可以使用JWT Token来进行身份认证。在用户登录成功后,生成JWT Token并返回给前端,前端在连接WebSocket时将JWT Token放入请求头中。 4.后端WebSocket服务在接收到WebSocket连接请求后,从请求头中获取JWT Token,并使用JWT Token验证用户身份。 5.验证通过后,将WebSocket连接信息保存到WebSocket连接池中,可以使用ConcurrentHashMap来保存WebSocket连接信息。 6.当需要向用户推送消息时,从WebSocket连接池中获取对应的WebSocket连接,并向连接中发送消息即可。 下面是一个简单的示例代码,仅供参考: 后端Spring Boot代码: WebSocket配置类: ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(webSocketHandler(), "/ws") .addInterceptors(new WebSocketInterceptor()) .setAllowedOrigins("*"); } @Bean public WebSocketHandler webSocketHandler() { return new MyWebSocketHandler(); } } ``` WebSocket处理器: ```java public class MyWebSocketHandler extends TextWebSocketHandler { private static final Map<Long, WebSocketSession> sessionPool = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { Long userId = getUserIdFromToken(session); if (userId == null) { session.close(); return; } sessionPool.put(userId, session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { Long userId = getUserIdFromToken(session); if (userId != null) { sessionPool.remove(userId); } } private Long getUserIdFromToken(WebSocketSession session) { HttpHeaders headers = session.getHandshakeHeaders(); String token = headers.getFirst("Authorization"); if (StringUtils.isEmpty(token)) { return null; } try { Claims claims = Jwts.parser().setSigningKey("secret").parseClaimsJws(token).getBody(); Long userId = Long.parseLong(claims.getSubject()); return userId; } catch (Exception e) { return null; } } public static void sendMessage(Long userId, String message) throws IOException { WebSocketSession session = sessionPool.get(userId); if (session != null) { session.sendMessage(new TextMessage(message)); } } } ``` WebSocket拦截器: ```java public class WebSocketInterceptor implements HandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { HttpHeaders headers = request.getHeaders(); String token = headers.getFirst("Authorization"); if (StringUtils.isEmpty(token)) { response.setStatusCode(HttpStatus.UNAUTHORIZED); return false; } try { Claims claims = Jwts.parser().setSigningKey("secret").parseClaimsJws(token).getBody(); Long userId = Long.parseLong(claims.getSubject()); attributes.put("userId", userId); return true; } catch (Exception e) { response.setStatusCode(HttpStatus.UNAUTHORIZED); return false; } } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { } } ``` 控制器: ```java @RestController public class WebSocketController { @GetMapping("/push") public void push(@RequestParam("userId") Long userId, @RequestParam("message") String message) throws IOException { MyWebSocketHandler.sendMessage(userId, message); } } ``` 前端Vue代码: ```javascript import Vue from 'vue' import VueWebSocket from 'vue-websocket' Vue.use(VueWebSocket, 'ws://localhost:8080/ws', { reconnection: true, reconnectionAttempts: 5, reconnectionDelay: 3000, format: 'json', passToStoreHandler: function (eventName, event) { if (!eventName.startsWith('SOCKET_')) { return } let method = 'commit' let target = eventName.toUpperCase() let msg = event Vue.store[method](target, msg) } }) // 登录成功后保存JWT Token到localStorage中 localStorage.setItem('jwtToken', 'xxxxx') // 连接WebSocket并带上JWT Token Vue.prototype.$socket = Vue.prototype.$connect({ format: 'json', passToStoreHandler: true, reconnection: true, reconnectionAttempts: 5, reconnectionDelay: 3000, queryParams: { Authorization: localStorage.getItem('jwtToken') } }) ``` 使用时,可以调用WebSocketController的push接口向指定用户推送消息,例如: ```javascript this.$http.get('/push', { params: { userId: 123, message: 'Hello, World!' } }) ``` 注意:本示例仅供参考,实际使用时需要根据实际需求进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值