天天看点

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

文章目录

    • 一.前言
    • 二.安全措施
      • 1.数据加密
      • 2.数据加签
      • 3.时间戳机制
      • 4.AppId机制
      • 5.限流机制
      • 6.黑名单机制
      • 7.数据合法性校验
    • 三.如何实现
      • 1.数据加密
      • 2.数据加签
      • 3.时间戳机制
      • 4.AppId机制
      • 5.限流机制
      • 6.黑名单机制
      • 7.数据合法性校验
    • 四.源码

一.前言

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

个人觉得安全措施大体来看主要在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.数据加密

现在主流的加密方式有

对称加密(单钥加密)

非对称加密(双钥加密)

  • 对称加密:对称密钥的

    加密

    解密

    中使用的

    密钥是相同的

    • 常见的对称加密算法有:

      DES

      AES

    • DES:

      比较老的算法

      ,一共有三个参数入口(原文,密钥,加密模式)。而3DES只是DES的一种模式,是以DES为基础更安全的变形,对数据进行了三次加密,也是被指定为

      AES的过渡算法

      -

      AES:高级加密标准,新一代标准,加密速度更快,安全性更高(优先选择)

    • 优点:

      计算速度快

      ,适合对大数据进行加解密。
    • 缺点:是在数据传送前,

      发送方和接收方必须商定好密钥

      ,然后使双方都能保存好秘钥,

      如果一方的秘钥被泄露,那么加密信息也就不安全了

    密钥只有一把,所以密钥的保存变得很重要。一旦密钥泄漏,密码也就被破解。
【JavaWeb】浅谈接口安全设计指南(含源码)
由于对称加密加解密速度快,因此可以和非对称加密混合使用,可以使用非对称加密方式加密对称加密的密钥,来保护密钥的安全。
  • 非对称加密:

    加密和解密使用不同的密钥

    ,由

    服务端

    会生成

    一对密钥

    私钥

    存放在

    服务端

    公钥

    可以发布给

    任何人

    使用。

    用公钥加密的数据,只能用和它对应的私钥解密,用私钥加密也只能同与之对应的公钥解密。

    • 常见的非对称加密是

      RSA 加密

      算法
    • 优点: 比起对称加密更加安全
    • 缺点: 加解密的速度比对称加密

      太多了(如:密钥对的生成,根据公钥反推私钥) 不适合对大数据进行加解密
    • 场景: 最常用的使用场景就是

      数字签名

      密码传输

      ,用作

      数字签名

      时使用私钥加密,公钥解密;用作

      加密解密

      时,使用公钥加密,私钥解密。
    同时生成公钥和私钥应该相对比较容易,但是从公钥推算出私钥,应该是很困难或者是不可能的
    【JavaWeb】浅谈接口安全设计指南(含源码)
  • 两种方式各有优缺点,而

    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,

    相同得验证通过

    ,如果不相等说明数据不可信。

能够保障

不可篡性

不可抵赖性

,但是

不能保障数据的私密性

(明文传输)

【JavaWeb】浅谈接口安全设计指南(含源码)
  • 即使他人截获并篡改了

    「明文数据」

    ,由于

    「私钥」

    是保密的,篡改者也无法生成正确的

    「签名」

    。所以能保证

    数据的完整

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的私钥

,这个签名它无法冒充,会被服务端识别出来。

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

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 个请求。
      【JavaWeb】浅谈接口安全设计指南(含源码)
  • 滑动窗口计数器算法

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

    • 它把时间以一定比例分片,比如一分钟分为 6 个区间,每个区间为 10s。每过一定区间的时间,就抛弃最前面的一个区间,如下图所示。如果当前窗口的请求数量超过了限制数量的话,就拒绝后续请求。
【JavaWeb】浅谈接口安全设计指南(含源码)

很显然:当滑动窗口的格子划分的越多,滑动窗口的滚动就越平滑,限流的统计就会越精确。

【JavaWeb】浅谈接口安全设计指南(含源码)
  • 漏桶限流

    漏桶算法的原理是按照固定常量速率流出请求,流入请求速率任意,当请求数超过桶的容量时,新的请求等待或者拒绝服务;可以看出漏桶算法可以强制限制数据的传输速度;

    • 个人理解: 我们可以把发请求的动作比作成注水到桶中,我们处理请求的过程可以比喻为漏桶漏水。我们往桶中以任意速率流入水,以一定速率流出水。当水超过桶流量则丢弃,因为桶容量是不变的,保证了整体的速率。如果想要实现这个算法的话也很简单,准备一个队列用来保存请求,然后我们定期从队列中拿请求来执行就好了。
【JavaWeb】浅谈接口安全设计指南(含源码)
  • 令牌桶限流

    令牌桶算法的原理是系统以一定速率向桶中放入令牌,填满了就丢弃令牌;请求来时会先从桶中取出令牌,如果能取到令牌,则可以继续完成请求,否则等待或者拒绝服务;令牌桶允许一定程度突发流量,只要有令牌就可以处理,支持一次拿多个令牌;

    • 个人理解: 令牌桶算法也比较简单。和漏桶算法算法一样,我们的主角还是桶(这限流算法和桶过不去啊)。不过现在桶里装的是令牌了,请求在被处理之前需要拿到一个令牌,请求处理完毕之后将这个令牌丢弃(删除)。我们根据限流大小,按照一定的速率往桶里添加令牌。
      【JavaWeb】浅谈接口安全设计指南(含源码)
      具体基于以上算法如何实现,

      Guava

      提供了

      RateLimiter工具类

      基于基于

      令牌桶算法

    • 以上代码表示一秒钟只允许处理五个并发请求,以上方式只能用在

      单应用

      的请求限流,不能进行

      全局限流

      。这个时候就需要

      分布式限流

      ,可以基于

      redis+lua

      来实现;

6.黑名单机制

如何为什么中黑我们这边不讨论,我们可以

给每个用户设置一个状态

比如包括:

初始化状态,正常状态,中黑状态,关闭状态

等等,或者我们直接通过

分布式配置中心

直接保存黑名单列表,每次检查是否在列表中即可

7.数据合法性校验

合法性校验包括:

常规性校验

以及

业务校验

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

四.源码

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

继续阅读