【JavaWeb】浅谈接口安全设计指南(含源码)

一.前言

结合我之前写的【Java基础】加密与安全基础 可以看本文章时更加熟悉 “加密以及安全”的基础概念

相关术语

  • 加密:encryption
  • 解密:decryption
  • 秘钥:secret key (公钥:public key 私钥:private key)
  • 数字签名:digital signature
  • 数字证书:digital certificate
  • 摘要:digest

个人觉得安全措施大体来看主要在2个方面

  1. 是如何保证数据在传输过程中不被窃取
  2. 是数据已经到达服务器端,服务器端验证传过来的数据没有被篡改,如何不被攻击

二.安全措施

1.数据加密

我们知道数据在传输过程中是很容易被抓包的,如果直接传输比如通过http协议,那么用户传输的数据可以被任何人获取,所以必须对数据加密。常见的做法对关键字段加密,比如:用户密码直接通过md5加密。加密的作用是保证数据在传输过程中不被窃取。

  • 现在主流的做法是使用https协议在http和tcp之间添加一层加密层(SSL层),这一层负责数据的加密和解密;

2.数据加签

数据加签就是由发送者生成一段无法伪造的一段字符串,来保证数据在传输过程中没有被被抓包篡改。

你可能会问数据如果已经通过https加密了,还有必要进行加签吗?

  • 数据在传输过程中经过加密,理论上就算被抓包,也无法对数据进行篡改;但是我们要知道加密的部分其实只是在外网,现在很多服务在内网中都需要经过很多服务跳转,所以这里的加签可以防止内网中数据被篡改

3.时间戳机制

数据是很容易被抓包的,但是经过如上的加密加签处理,就算拿到数据也不能看到真实的数据。但是有不法者不关心真实的数据,而是直接拿到抓取的数据包进行恶意请求。这时候可以使用时间戳机制

  • 在每次请求中加入当前的时间,服务器端会拿到当前时间和请求中的时间相减,看看是否在一个固定的时间范围内,比如5分钟内。这样恶意请求的数据包是无法更改里面时间的,所以5分钟后就视为非法请求了。

4.AppId机制

大部分网站基本都需要用户名和密码才能登录,并不是谁来能使用我的网站,这其实也是一种安全机制。对应的对外提供的接口其实也需要这么一种机制,并不是谁都可以调用,需要使用接口的用户需要在后台开通appid,提供给用户相关的密钥(appSecret)。在调用的接口中需要提供appid以及密钥(主要用于与生成签名sign),服务器端会进行相关的验证;

5.限流机制

本来就是真实的用户,并且开通了appid,但是出现频繁调用接口的情况;这种情况需要给相关appid限流处理

  • 常用的限流算法有令牌桶漏桶算法

6.黑名单机制

如果此appid进行过很多非法操作,或者说专门有一个中黑系统,经过分析之后直接将此appid列入黑名单所有请求直接返回错误码.

7.数据合法性校验

这个可以说是每个系统都会有的处理机制,只有在数据是合法的情况下才会进行数据处理。每个系统都有自己的验证规则,当然也可能有一些常规性的规则,比如身份证长度和组成,电话号码长度和组成等等;

三.如何实现

以上大体介绍了一下常用的一些接口安全措施,当然可能还有其他我不知道的方式,希望大家补充,下面看看以上这些方法措施,具体如何实现;

1.数据加密

现在主流的加密方式有对称加密(单钥加密)非对称加密(双钥加密)

  • 对称加密:对称密钥的加密解密中使用的密钥是相同的

    • 常见的对称加密算法有:DESAES
    • DES:比较老的算法,一共有三个参数入口(原文,密钥,加密模式)。而3DES只是DES的一种模式,是以DES为基础更安全的变形,对数据进行了三次加密,也是被指定为AES的过渡算法
      -AES:高级加密标准,新一代标准,加密速度更快,安全性更高(优先选择)
    • 优点:计算速度快适合对大数据进行加解密
    • 缺点:是在数据传送前,发送方和接收方必须商定好密钥,然后使双方都能保存好秘钥,如果一方的秘钥被泄露,那么加密信息也就不安全了

    密钥只有一把,所以密钥的保存变得很重要。一旦密钥泄漏,密码也就被破解。

在这里插入图片描述

由于对称加密加解密速度快,因此可以和非对称加密混合使用,可以使用非对称加密方式加密对称加密的密钥,来保护密钥的安全。


  • 非对称加密加密和解密使用不同的密钥,由服务端会生成一对密钥私钥存放在服务端公钥可以发布给任何人使用。 用公钥加密的数据,只能用和它对应的私钥解密,用私钥加密也只能同与之对应的公钥解密。
    • 常见的非对称加密是RSA 加密算法
    • 优点: 比起对称加密更加安全
    • 缺点: 加解密的速度比对称加密太多了(如:密钥对的生成,根据公钥反推私钥) 不适合对大数据进行加解密
    • 场景: 最常用的使用场景就是数字签名密码传输,用作 数字签名时使用私钥加密,公钥解密;用作加密解密时,使用公钥加密,私钥解密

    同时生成公钥和私钥应该相对比较容易,但是从公钥推算出私钥,应该是很困难或者是不可能的
    在这里插入图片描述

  • 两种方式各有优缺点,而 https 的实现方式正好是结合了两种加密方式整合了双方的优点,在安全和性能方面都比较好

对称加密和非对称加密代码实现,Java提供了相关的工具类可以直接使用,此处不过多介绍,关于https如何配置使用相对来说复杂一些 HTTPS分析与实战

2.数据加签

3种数据签名安全策略:消息摘要, 数字签名 , 数字签名+加密[证书]

安全策略描述安全级别
消息摘要(Digest),也称消息Hash将数据和Key(自定义密钥)组合后进行hash安全级别低,密钥安全性非常低。在密钥安全情况下能基本保障数据的不可篡改性。
数字签名(Signature)使用证书和非对称签名算法对数据进行签名安全级别中,能够保障数据的不可篡改性和不可抵赖性,但是不能保障数据的私密性
签名-加密[证书]使用证书和非对称算法对数据签名,使用一次一密的密钥和对称算法对数据进行加密安全级别高,能够保障数据的不可篡改性和不可抵赖性,而且能保障数据的私密性。
  • 机密性(Confidentiality): 未经许可不许看
  • 完整性(Integrity) : 不许篡改
  • 可用性(Availability) : 防止不可用
  • 不可抵赖性(Non-Repudiation): 用户不能否认其行为

1.消息摘要(Digest)

  • 消息摘要使用比较多的摘要算法(也称Hash算法)是有MD5、SHA-1、SHA-256,将需要提交的数据通过某种方式组合成一个字符串,然后通过hash算法生成一段加密字符串,这段字符串就是数据包的签名signature。,比如:

    这个固定长度的 Hash 值,就是这份数据的摘要,也称为指纹。

str = 参数1={参数1}&参数2={参数2}&……&参数n={参数n}&signature={用户密钥};
MD5.encrypt(str);

注意:最后的用户密钥signature客户端和服务端都有一份,这样会更加安全;

  • 消息摘要原理:Hash算法不可逆,并且计算结果具有唯一性,在用户密钥的隐私得到保证的情况下,可以保证完整性
  • 消息摘要缺陷:用户密钥的是明文传输的,隐私性很难保证。

2.数字签名(Signature)

如果用「公钥」对数据加密,用「私钥」去解密,这是「加密」; 反之用「私钥」对数据加密,用「公钥」去解密,这是「签名」!!!

简单地看,似乎没有区别,只是换了个名字。但实际上,两者的用途完全不一样。 因为所有人都持有公钥,所以「签名」并不能保证数据的安全性,因为所有人都可以用公钥去解密。 但「签名」却能用于保证数据的准确性和不可否认性。因为公钥和私钥是一一对应的,所以 当一个公钥能解密某个密文时,说明这个密文一定来自于私钥持有者。

高效的数字签名方案:摘要算法(Hash算法)非对称加密结合使用。

  • 如何签名:客户端先用Hash算法计算明文数据Hash值,再对这个Hash值用 “私钥“ 加密。这样就能较快速地得到了原始信息的签名,将明文数据以及密文同时传给服务端
  • 如何验证:服务端先用相同的Hash算法计算客户端传递明文数据的Hash值,再用“公钥“ 对 客户端传递的签名进行解密,得到收到的Hash值,最后对比这两个Hash值判断是否相等。如果不相等说明数据不可信。

如果明文数据特别庞大,直接使用非对称加密生成签名会导致加解密的效率特别底下(慢慢),这也是为什么上面会先将明文数据hash后在通过私钥加密

具体操作

  1. 客户端明文数据做一个md5/SHA计算,对计算后的值通过 "私钥" 加密得到密文(签名)客户端明文数据密文发送给服务端
  2. 服务端密文 通过“公钥解密” 得到 值A,同时 服务端明文做一个md5/SHA计算得到值B
  3. 服务端比较客户端与服务Hash的明文值A与值B相同得验证通过,如果不相等说明数据不可信。

能够保障不可篡性不可抵赖性,但是不能保障数据的私密性(明文传输)
在这里插入图片描述

  • 即使他人截获并篡改了「明文数据」,由于「私钥」是保密的,篡改者也无法生成正确的「签名」。所以能保证数据的完整

3.签名+加密[证书]过程

  1. 客户端生成一个随机字符串,作为password,然后把这个password通过B公钥加密生成密文C,把A明文通过password加密生成密文B
  2. 同时把A明文MD5/SHA计算后的值通过A私钥加密得到签名D, 把密文B密文C签名D发给服务端服务端通过私钥解密文C得到password,然后通过password解密文B就可以得到A明文,同时签名可以用来验证发送者是不是A,以及A发送的数据有没有被第三方修改过

假设存在一个恶意的一方X,冒充了A,发送了密文B(password生成)密文C,服务端收到数据后,仍然可以正常解密得到明文,但是却无法证明这个明文数据是A发送的还是恶意用户B发送的签名D的含义就是A自己签名,服务端可以验证。X由于没有A的私钥,这个签名它无法冒充,会被服务端识别出来。
在这里插入图片描述

3.时间戳机制

解密后的数据,经过签名认证后,我们拿到数据包中的客户端时间戳字段,然后用服务器当前时间去减客户端时间,看本次请求是否超时

伪代码如下:

long interval=5*60*1000//超时时间
long clientTime=request.getparameter("clientTime");
long serverTime=System.currentTimeMillis();
if(serverTime-clientTime>interval){
    return new Response("超过处理时长")
}

4.AppId机制

生成一个唯一的appId以及对应的appSecret(密钥)。密钥使用字母、数字等特殊字符随机生成即可;

  • 生成唯一appId根据实际情况看是否需要全局唯一,但是不管是否全局唯一最好让生成的Id有如下属性:
    • 趋势递增:这样在保存数据库的时候,使用索引性能更好。
    • 信息安全:尽量不要连续的,容易发现规律。
    • 关于全局唯一Id生成的方式常见的有类Snowflake (Snowflake 俗称雪花算法,用于生成分布式自增 ID )方式等;

5.限流机制

常用的限流算法包括:固定窗口计数器算法滑动窗口计数器算法漏桶限流令牌桶限流

计数器限流:”计数器是一种比较简单粗暴的算法,主要用来限制总并发数,比如数据库连接池、线程池、秒杀的并发数;计数器限流只要一定时间内的总请求数超过设定的阀值则进行限流;

  • 固定窗口计数器算法
    规定我们单位时间处理的请求数量。比如我们规定我们的一个接口一分钟只能访问 10 次的话。使用固定窗口计数器算法的话可以这样实现:

    • 给定一个变量 counter 来记录处理的请求数量,当 1 分钟之内处理一个请求之后 counter+1,1 分钟之内的如果 counter=100 的话,后续的请求就会被全部拒绝。等到 1 分钟结束后,将 counter 回归成 0,重新开始计数(ps:只要过了一个周期就讲 counter 回归成 0)。

    • 这种限流算法无法保证限流速率,因而无法保证突然激增的流量。比如我们限制一个接口一分钟只能访问 10 次的话,前半分钟一个请求没有接收,后半分钟接收了 10 个请求。
      在这里插入图片描述

  • 滑动窗口计数器算法
    算的上是固定窗口计数器算法的升级版。滑动窗口计数器算法相比于固定窗口计数器算法的优化在于:

    • 它把时间以一定比例分片,比如一分钟分为 6 个区间,每个区间为 10s。每过一定区间的时间,就抛弃最前面的一个区间,如下图所示。如果当前窗口的请求数量超过了限制数量的话,就拒绝后续请求。

在这里插入图片描述
很显然:当滑动窗口的格子划分的越多,滑动窗口的滚动就越平滑,限流的统计就会越精确。
在这里插入图片描述

  • 漏桶限流
    漏桶算法的原理是按照固定常量速率流出请求,流入请求速率任意,当请求数超过桶的容量时,新的请求等待或者拒绝服务;可以看出漏桶算法可以强制限制数据的传输速度;
    • 个人理解: 我们可以把发请求的动作比作成注水到桶中,我们处理请求的过程可以比喻为漏桶漏水。我们往桶中以任意速率流入水,以一定速率流出水。当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率。如果想要实现这个算法的话也很简单,准备一个队列用来保存请求,然后我们定期从队列中拿请求来执行就好了。

在这里插入图片描述

  • 令牌桶限流
    令牌桶算法的原理是系统以一定速率向桶中放入令牌,填满了就丢弃令牌;请求来时会先从桶中取出令牌,如果能取到令牌,则可以继续完成请求,否则等待或者拒绝服务;令牌桶允许一定程度突发流量,只要有令牌就可以处理,支持一次拿多个令牌;
    • 个人理解: 令牌桶算法也比较简单。和漏桶算法算法一样,我们的主角还是桶(这限流算法和桶过不去啊)。不过现在桶里装的是令牌了,请求在被处理之前需要拿到一个令牌,请求处理完毕之后将这个令牌丢弃(删除)。我们根据限流大小,按照一定的速率往桶里添加令牌。
      在这里插入图片描述
      具体基于以上算法如何实现,Guava提供了RateLimiter工具类基于基于令牌桶算法
    RateLimiter rateLimiter = RateLimiter.create(5);
    
    • 以上代码表示一秒钟只允许处理五个并发请求,以上方式只能用在单应用的请求限流,不能进行全局限流。这个时候就需要分布式限流,可以基于redis+lua来实现;

6.黑名单机制

如何为什么中黑我们这边不讨论,我们可以给每个用户设置一个状态比如包括:初始化状态,正常状态,中黑状态,关闭状态等等,或者我们直接通过分布式配置中心直接保存黑名单列表,每次检查是否在列表中即可

7.数据合法性校验

合法性校验包括:常规性校验以及业务校验

  • 常规性校验:包括签名校验,必填校验,长度校验,类型校验,格式校验等。
  • 业务校验:根据实际业务而定,比如订单金额不能小于0等。

四.源码

优雅的实现第三方开放api接口签名(有状态/无状态)

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墩墩分墩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值