前言:大家开始写Web后台技术时,很多人的第一个功能就是写的登录/注册功能模块,但一般都只简单的实现了功能逻辑,在安全方面并没有考虑太多。

转载了两位前辈的文章,然后结合自己的经验整理了一下,致敬:

Java技术栈:如何设计一个安全的登录接口?

捡田螺的小男孩:   软件开发中如何安全传输存储用户密码?

安全风险一:暴力破解登录

只要网站是暴露在公网的,那么很大概率上会被人盯上,尝试爆破这种网站简单且有效的方式:通过各种方式获得了网站的用户名之后,通过编写程序来遍历所有可能的密码,直至找到正确的密码为止。

伪代码如下:

# 密码字典
password_dict = []
# 登录接口
login_url = ''
def attack(username):  for password in password_dict:  data = {'username': username, 'password': password}  content = requests.post(login_url, data).content.decode('utf-8')  if 'login success' in content:  print('got it! password is : %s' % password)

那么这种情况,我们要怎么防范呢?

1.1、验证码

有聪明的同学就想到了,我可以在它密码错误达到一定次数时,增加验证码校验!比如我们设置,当用户密码错误达到3次之后,则需要用户输入图片验证码才可以继续登录操作:

伪代码如下:

fail_count = get_from_redis(fail_username)
if fail_count >= 3:  if captcha is None:  return error('需要验证码')  check_captcha(captcha)
success = do_login(username, password)
if not success:  set_redis(fail_username, fail_count + 1)

伪代码未考虑并发,实际开发可以考虑加锁。

这样确实可以过滤掉一些非法的攻击,但是以目前的OCR技术来说的话,普通的图片验证码真的很难做到有效的防止机器人。

当然,我们也可以花钱购买类似于三方公司提供的滑动验证等验证方案,但是也并不是100%的安全,一样可以被破解。

1.2、登录限制

那这时候又有同学说了,那我可以直接限制非正常用户的登录操作,当它密码错误达到一定次数时,直接拒绝用户的登录,隔一段时间再恢复。

比如我们设置某个账号在登录时错误次数达到10次时,则5分钟内拒绝该账号的所有登录操作。

伪代码如下:

fail_count = get_from_redis(fail_username)
locked = get_from_redis(lock_username)  if locked:  return error('拒绝登录')
if fail_count >= 3:  if captcha is None:  return error('需要验证码')  check_captcha(captcha)
success = do_login(username, password)
if not success:  set_redis(fail_username, fail_count + 1)  if fail_count + 1 >= 10:  # 失败超过10次,设置锁定标记  set_redis(lock_username, true, 300s)

umm,这样确实可以解决用户密码被爆破的问题。但是,这样会带来另一个风险:攻击者虽然不能获取到网站的用户信息,但是它可以让我们网站所有的用户都无法登录!

攻击者只需要无限循环遍历所有的用户名(即使没有,随机也行)进行登录,那么这些用户会永远处于锁定状态,导致正常的用户无法登录网站!

1.3、IP限制

那既然直接针对用户名不行的话,我们可以针对IP来处理,直接把攻击者的IP封了不就万事大吉了嘛。

我们可以设定某个IP下调用登录接口错误次数达到一定时,则禁止该IP进行登录操作。

伪代码如下:

ip = request['IP']
fail_count = get_from_redis(fail_ip)
if fail_count > 10:  return error('拒绝登录')
# 其它逻辑
# do something()
success = do_login(username, password)
if not success:  set_redis(fail_ip, true, 300s)

这样也可以一定程度上解决问题,事实上有很多的限流操作都是针对IP进行的,比如niginx的限流模块就可以限制一个IP在单位时间内的访问次数。

但是这里还是存在问题:

  • 比如现在很多学校、公司都是使用同一个出口IP,如果直接按IP限制,可能会误杀其它正常的用户

  • 攻击者完全可以在IP被封后切换IP来攻击

1.4、手机验证(推荐)

那难道就没有一个比较好的方式来防范吗?

当然有。

我们可以看到近些年来,几乎所有的应用都会让用户绑定手机,一个是国家的实名制政策要求,第二个是手机基本上和身份证一样,基本上可以代表一个人的身份标识了。所以很多安全操作都是基于手机验证来进行的,登录也可以。

  1. 当用户输入密码次数大于3次时,要求用户输入验证码(最好使用滑动验证

  2. 当用户输入密码次数大于10次时,弹出手机验证,需要用户使用手机验证码和密码双重认证进行登录

伪代码如下:

fail_count = get_from_redis(fail_username)  if fail_count > 3:  if captcha is None:  return error('需要验证码')  check_captcha(captcha)   if fail_count > 10:  # 大于10次,使用验证码和密码登录  if dynamic_code is None:  return error('请输入手机验证码')  if not validate_dynamic_code(username, dynamic_code):  delete_dynamic_code(username)  return error('手机验证码错误')  success = do_login(username, password, dynamic_code)  if not success:  set_redis(fail_username, fail_count + 1)

我们结合了上面说的几种方式的同时,加上了手机验证码的验证模式,基本上可以阻止相当多的一部分恶意攻击者。但是没有系统是绝对安全的,我们只能够尽可能的增加攻击者的攻击成本。

大家可以根据自己网站的实际情况来选择合适的策略。


安全风险二:中间人攻击获取密码

中间人攻击(man-in-the-middle attack, abbreviated to MITM):简单一点来说就是,A和B在通讯过程中,攻击者通过嗅探、拦截等方式获取或修改A和B的通讯内容。

举个栗子:小白小黄发快递,途中要经过快递点A,小黑就躲在快递点A,或者干脆自己开一个快递点B来冒充快递点A。然后偷偷的拆了小白小黄的快递,看看里面有啥东西。甚至可以把小白的快递给留下来,自己再打包一个一毛一样的箱子发给小黄

那在登录过程中,如果攻击者在嗅探到了从客户端发往服务端的登录请求,就可以很轻易的获取到用户的用户名和密码。那么这种情况,我们要怎么防范呢?

那么这种情况,我们要怎么防范呢?

2.1、更换HTTPS协议

防范中间人攻击最简单也是最有效的一个操作,更换HTTPS,把网站中所有的HTTP请求修改为强制使用HTTPS。

https原理是什么呢?为什么它能解决http的三大风险呢?

https = http + SSL/TLS, SSL/TLS 是传输层加密协议,它提供内容加密、身份认证、数据完整性校验,以解决数据传输的安全性问题。

为什么HTTPS可以防范中间人攻击?

HTTPS实际上就是在HTTP和TCP协议中间加入了SSL/TLS协议,用于保障数据的安全传输。相比于HTTP,HTTPS主要有以下几个特点:

  • 内容加密

  • 数据完整性

  • 身份验证

为了加深https原理的理解,我们一起复习一下「一次完整https的请求流程」吧~

HTTPS为了兼顾安全与效率,同时使用了对称加密和非对称加密。数据是被对称加密传输的,对称加密过程需要客户端的一个密钥,为了确保能把该密钥安全传输到服务器端,采用非对称加密对该密钥进行加密传输,总的来说,对数据进行对称加密,对称加密所要使用的密钥通过非对称加密传输。

  1. 客户端发起https请求

  2. 服务器必须要有一套数字证书,可以自己制作,也可以向权威机构申请。这套证书其实就是一对公私钥。

  3. 服务器将自己的数字证书(含有公钥、证书的颁发机构等)发送给客户端。

  4. 客户端收到服务器端的数字证书之后,会对其进行验证,主要验证公钥是否有效,比如颁发机构,过期时间等等。如果不通过,则弹出警告框。如果证书没问题,则生成一个密钥(对称加密算法的密钥,其实是一个随机值),并且用证书的公钥对这个随机值加密。

  5. 客户端会发起https中的第二个请求,将加密之后的客户端密钥(随机值)发送给服务器。

  6. 服务器接收到客户端发来的密钥之后,会用自己的私钥对其进行非对称解密,解密之后得到客户端密钥,然后用客户端密钥对返回数据进行对称加密,这样数据就变成了密文。

  7. 服务器将加密后的密文返回给客户端。

  8. 客户端收到服务器发返回的密文,用自己的密钥(客户端密钥)对其进行对称解密,得到服务器返回的数据。

2.2、加密传输

在HTTPS之外,我们还可以手动对敏感数据进行加密传输:

  • 用户名可以在客户端使用非对称加密,在服务端解密

  • 密码可以在客户端进行MD5之后传输,防止暴露密码明文


安全风险三:如何安全传输存储用户密码

我们开发网站或者APP的时候,首先要解决的问题,就是「如何安全传输和存储用户的密码」。一些大公司的用户数据库泄露事件也时有发生,带来非常大的负面影响。因此,如何安全传输存储用户密码,是每位程序员必备的基础。

3.1. 如何安全地传输用户的密码

要拒绝用户密码在网络上裸奔,我们很容易就想到「使用https协议」。

3.2. 如何安全地存储你的密码?

假设密码已经安全到达服务端啦,那么,如何存储用户的密码呢?一定不能明文存储密码到数据库哦,如果一旦被黑客拖库(),整个数据库被下载走,那黑客可以登录任意一个账户做出危险的操作,甚至无法弥补的事故,所以要正确的加密密码,保护用户账户的安全。可以用「哈希摘要算法加密密码」,再保存到数据库。

哈希摘要算法:只能从明文生成一个对应的哈希值,一般不能反过来根据哈希值得到对应的明文。

  • MD5摘要算法保护你的密码  (容易被破解)
  • MD5+盐摘要算法保护用户的密码 (推荐)
  • 提升密码存储安全的利器登场,Bcrypt

参考链接(推荐看,文章短且精炼):

正确的加密存储密码防止被拖库(脱裤)保护用户登录安全

Web安全:明文密码漏洞

3.3、总结

  • 因此,一般使用https 协议 + 非对称加密算法(如RSA)来传输用户密码,为了更加安全,可以在前端构造一下随机因子哦。

  • 使用BCrypt + 盐存储用户密码。

  • 在感知到暴力破解危害的时候,「开启短信验证、图形验证码、账号暂时锁定」等防御机制来抵御暴力破解。


安全风险四:其它

除了上面我们聊的这些以外,其实还有很多其它的工作可以考虑,比如:

  • 操作日志,用户的每次登录和敏感操作都需要记录日志(包括IP、设备等)

  • 异常操作或登录提醒,有了上面的操作日志,那我们就可以基于日志做风险提醒,比如用户在进行非常登录地登录、修改密码、登录异常时,可以短信提醒用户

  • 拒绝弱密码 注册或修改密码时,不允许用户设置弱密码

  • 防止用户名被遍历 有些网站在注册时,在输入完用户名之后,会提示用户名是否存在。这样会存在网站的所有用户名被泄露的风险(遍历该接口即可),需要在交互或逻辑上做限制

  • 防止XSS跨站脚本攻击   替换http请求参数的特殊字符,如< 变成 &lt ; >变成 &gt ;<script>变成 & ltscript &gt;这样就会直接显示,而不会作为脚本执行。(前端要做替换,后端也要做替换,双重保证)

  • ...

后记

现在国家不断的出台各种法律,对用户的数据越来越看重。作为开发者,我们需要在保护用户数据和用户隐私方面做更多的工作。

Java设计安全的登录接口相关推荐

  1. 开源IM项目-InChat登录接口设计与实现(基于Netty)

    只给你最值得的信息 小弟正在做的一个开源IM项目,目标是实现一个轻量级.高效率的支持聊天与物联网的通讯框架.昨天刚刚出的设计稿并再今天做了实现. 项目是基于Netty的二次开发,关于Netty我这里就 ...

  2. 如何设计登录接口,十分钟内连续登录5次失败,需要等待30分钟才能登录

    正常业务里的实现不能这样搞,合适的方法是走缓存,比如使用redis,我当时就只有原生Java API能用,请大家把这个当成算法题来看待 常言道:字数越短问题越大.   今天阿里的面试官小哥哥让我实现一 ...

  3. java仿qq登录 界面设计,Java Swing仿QQ登录界面效果

    本文实例为大家分享了Java Swing仿QQ登录界面展示的具体代码,供大家参考,具体内容如下 闲来无事将早些时候已实现的QQ登录界面再实现了一遍,纯手工打造(意思是没有用NetBeans.MyEcl ...

  4. Java模式设计卖电脑实验报告,面向对象(Java)实验0继承、接口和多态

    <面向对象(Java)实验0继承.接口和多态>由会员分享,可在线阅读,更多相关<面向对象(Java)实验0继承.接口和多态(11页珍藏版)>请在金锄头文库上搜索. 1.电子信息 ...

  5. java设计一个形状shape_编程定义一个图形接口 Shape, 内含2个抽象方法 get... JAVA编程题:编一个程序包含一个接口 shape(该接......

    导航:网站首页 > 编程定义一个图形接口 Shape, 内含2个抽象方法 get... JAVA编程题:编一个程序包含一个接口 shape(该接... 编程定义一个图形接口 Shape, 内含2 ...

  6. java金蝶星空云金蝶Java 对接 金蝶云星空 接口 对接 金蝶API 对接 金蝶 接口 解决 会话失效 问题 会话已失效,请重新登录

    java金蝶星空云金蝶Java 对接 金蝶云星空 接口 对接 金蝶API 对接 金蝶 接口 解决 会话失效 问题 会话已失效,请重新登录 1.准备工作 1.1 接口调用账户 1.2 下载 金蝶星空云 ...

  7. Java信息管理系统界面设计(包括登录界面及界面切换)

    Java学生成绩管理系统界面设计(包括登录界面及界面切换),内含学生成绩管理系统各用户应有功能模块设计. 登录界面 package Panel; import java.awt.*; import j ...

  8. Java后端如何保证用户注册登录接口的安全性之用户注册篇

    现在的面试难免显得有些教科书式,面试官往往会问应试者一些跟自己现有项目压根用不上的新技术,以及一些所谓的基础知识,然后招到的往往又都是理论知识很丰富,前言技术又很熟悉,但是处理起实际业务时就只能呵呵的 ...

  9. java微信小程序接口openid过期_Java微信小程序登录接口获取openid

    根据官方文档,wx.login()的回调函数中,需要我们传递生成的用户登录凭证到code2accessToken的接口中 小程序登录方法 code2accessToken的方法中要求传入如下参数 co ...

最新文章

  1. Thrift的接口定义语言IDL
  2. 解决docker pull镜像速度慢的问题
  3. python基础一入门必备知识-Python快速入门指南基础知识详细说明
  4. python 二进制文件_使用Python进行二进制文件读写的简单方法(推荐)
  5. Direct2D (13) : 画刷之 ID2D1BitmapBrush
  6. 收藏 | Pytorch-lightning的使用
  7. 【Linux】Linux 标准目录结构
  8. Java刷新Jpanel_java – 刷新JPanel
  9. MVP小白入门,只需5步
  10. sdi 采集卡---环视频拼接直播方案
  11. 转 Androidpn里的Xmpp的理解(消息推送)
  12. 计算机添加pdf打印机驱动,win10系统添加pdf打印机的解决方案
  13. 图解yarn的作业提交流程
  14. 怎样组织一次攻防演练比赛- 前期准备阶段
  15. 怎样快速将方形图片剪裁成椭圆形?分享大家一个小妙招
  16. 户外设备选择远距离蓝牙需要了解的知识-----工程师必看
  17. intel 服务器芯片组 c6,华擎推出C621A WS工作站主板 支持志强W-3300处理器
  18. 获取手机联系人,并通过拼音字母快速查询
  19. Shell 脚本的详细解读 (一)
  20. 使用反编译工具反编译Dll,编译成C#

热门文章

  1. 响应式导航(从水平到垂直)的分析与实现
  2. Jumping Frog
  3. opendns_如何使用OpenDNS或Google DNS设置Verizon FIOS路由器
  4. 学黑客要学什么编程语言
  5. 【UE4 第一人称射击游戏】13-瞄准开火
  6. 影视剪辑,超实用的视频剪辑素材网站
  7. 如何在 Windows 10 的同时安装 Ubuntu 20.04实现双系统
  8. 安检设备是什么,有什么作用
  9. cocos3 图片按照椭圆运动
  10. 【JavaSE】抽象类和接口