关于接口设计:如何设计一个安全的对外接口

29次阅读

共计 4237 个字符,预计需要花费 11 分钟才能阅读完成。

安全措施

安全措施大体来看次要在两个方面,一方面就是如何保证数据在传输过程中的安全性,另一个方面是数据曾经达到服务器端,服务器端如何辨认数据,如何不被攻打;上面具体看看都有哪些安全措施。

1、数据加密

数据在传输过程中是很容易被抓包的,如果间接传输比方通过 http 协定,那么用户传输的数据能够被任何人获取;所以必须对数据加密,常见的做法对关键字段加密比方用户明码间接通过 md5 加密;当初支流的做法是应用 https 协定,在 http 和 tcp 之间增加一层加密层(SSL 层),这一层负责数据的加密和解密;

2、数据加签

数据加签就是由发送者产生一段无奈伪造的一段数字串,来保证数据在传输过程中不被篡改;你可能会问数据如果曾经通过 https 加密了,还有必要进行加签吗?数据在传输过程中通过加密,实践上就算被抓包,也无奈对数据进行篡改;然而咱们要晓得加密的局部其实只是在外网,当初很多服务在内网中都须要通过很多服务跳转,所以这里的加签能够避免内网中数据被篡改;

3、工夫戳机制

数据是很容易被抓包的,然而通过如上的加密,加签解决,就算拿到数据也不能看到实在的数据;然而有不法者不关怀实在的数据,而是间接拿到抓取的数据包进行歹意申请;这时候能够应用工夫戳机制,在每次申请中退出以后的工夫,服务器端会拿到以后工夫和音讯中的工夫相减,看看是否在一个固定的工夫范畴内比方 5 分钟内;这样歹意申请的数据包是无奈更改外面工夫的,所以 5 分钟后就视为非法申请了;

4、AppId 机制

大部分网站根本都须要用户名和明码能力登录,并不是谁来能应用我的网站,这其实也是一种平安机制;对应的对外提供的接口其实也须要这么一种机制,并不是谁都能够调用,须要应用接口的用户须要在后盾开明 appid,提供给用户相干的密钥 secret;在调用的接口中须要通过 appid+secret 获取到 access_token,而后每个申请都须要附带 access_token,服务器端会进行相干的验证;

5、限流机制

原本就是实在的用户,并且开明了 appid,然而呈现频繁调用接口的状况;这种状况须要给相干 appid 限流解决,罕用的限流算法有令牌桶和漏桶算法;

6、黑名单机制

如果此 appid 进行过很多非法操作,或者说专门有一个中黑零碎,通过剖析之后间接将此 appid 列入黑名单,所有申请间接返回错误码;

7、数据合法性校验

这个能够说是每个零碎都会有的解决机制,只有在数据是非法的状况下才会进行数据处理;每个零碎都有本人的验证规定,当然也可能有一些常规性的规定,比方身份证长度和组成,电话号码长度和组成等等;

如何实现

以上大体介绍了一下罕用的一些接口安全措施,当然可能还有其余我不晓得的形式,心愿大家补充,上面看看以上这些办法措施,具体如何实现;

1、数据加密

当初支流的加密形式有对称加密和非对称加密;
对称加密:对称密钥在加密和解密的过程中应用的密钥是雷同的,常见的对称加密算法有 DES,AES;长处是计算速度快,毛病是在数据传送前,发送方和接管方必须约定好秘钥,而后使单方都能保留好秘钥,如果一方的秘钥被泄露,那么加密信息也就不平安了;
非对称加密:服务端会生成一对密钥,私钥寄存在服务器端,公钥能够公布给任何人应用;长处就是比起对称加密更加平安,然而加解密的速度比对称加密慢太多了;宽泛应用的是 RSA 算法;RSA 的非对称加密实现参考这里

两种形式各有优缺点,而 https 的实现形式正好是联合了两种加密形式,整合了单方的长处,在平安和性能方面都比拟好;

对称加密和非对称加密代码实现,jdk 提供了相干的工具类能够间接应用,此处不过多介绍;对于 https 如何配置应用相对来说简单一些,能够参这篇文章 HTTPS 剖析与实战

2、数据加签

数据签名应用比拟多的是 md5 算法,将须要提交的数据通过某种形式组合(如:参数名 ASCII 码从小到大排序)为一个字符串,而后通过 md5 生成一段加密字符串,这段加密字符串就是数据包的签名,服务端接管到数据后,以同样的形式进行操作,而后比拟签名,就能够辨认数据是否被篡改过,数据签名一个简略的例子:

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

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 即可,密钥 Secret 应用字母、数字等特殊字符随机生成即可;生成惟一 AppId 依据理论状况看是否须要全局惟一;然而不论是否全局惟一最好让生成的 Id 有如下属性:
趋势递增:这样在保留数据库的时候,应用索引性能更好;
信息安全:尽量不要间断的,容易发现法则;
对于全局惟一 Id 生成的形式常见的有类 snowflake 形式等;
对于 access_token 的生成能够应用 JWT 生成,存入 redis 中并设置过期工夫。

String access_token = JWT.create().withAudience(employee.getId().toString(), employee.getName()).sign(Algorithm.HMAC256(secret));

5、限流机制

罕用的限流算法包含:令牌桶限流,漏桶限流,计数器限流;
1. 令牌桶限流
令牌桶算法的原理是零碎以肯定速率向桶中放入令牌,填满了就抛弃令牌;申请来时会先从桶中取出令牌,如果能取到令牌,则能够持续实现申请,否则期待或者拒绝服务;令牌桶容许肯定水平突发流量,只有有令牌就能够解决,反对一次拿多个令牌;
2. 漏桶限流
漏桶算法的原理是依照固定常量速率流出申请,流入申请速率任意,当申请数超过桶的容量时,新的申请期待或者拒绝服务;能够看出漏桶算法能够强制限度数据的传输速度;
3. 计数器限流
计数器是一种比较简单粗犷的算法,次要用来限度总并发数,比方数据库连接池、线程池、秒杀的并发数;计数器限流只有肯定工夫内的总申请数超过设定的阀值则进行限流;

具体基于以上算法如何实现,Guava 提供了 RateLimiter 工具类基于基于令牌桶算法:

// 示意 1 秒钟容许解决的申请数量
RateLimiter limiter = RateLimiter.create(200);
if (!limiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) {result.put("code", "10003");
    result.put("msg", "接口的访问量超过限度!");
    return result;
}

以上形式只能用在单利用的申请限流,单利用的申请限流还能够通过 AtomicInteger,Semaphore 来实现,然而上述计划都不反对集群限流,不能进行全局限流;这个时候就须要分布式限流,能够基于 redis+lua 来实现;能够参考这篇文章

6、黑名单机制

咱们能够给每个用户设置一个状态比方包含:初始化状态,失常状态,中黑状态,敞开状态等等;或者咱们间接通过分布式配置核心,间接保留黑名单 IP 列表,每次查看是否在列表中即可;理论我的项目中往往因为网关或者反向代理服务器的存在,服务端通过 request.getRemoteAddr()不能间接拿到客户端的真是 ip,这里须要依据理论状况依照具体的参数进行获取;

public static String getRemoteAddr(HttpServletRequest req) {if (req == null) throw new NullPointerException("HttpServletRequest is null");

    // X-Forwarded-For:Squid 服务代理
    String ip = req.getHeader("X-Forwarded-For");

    // Proxy-Client-IP:apache 服务代理
    if ((ip == null) || (ip.length() == 0) || "unknown".equalsIgnoreCase(ip)) ip = req.getHeader("Proxy-Client-IP");

    // WL-Proxy-Client-IP:weblogic 服务代理
    if ((ip == null) || (ip.length() == 0) || "unknown".equalsIgnoreCase(ip)) ip = req.getHeader("WL-Proxy-Client-IP");

    // X-Real-IP:nginx 服务代理
    if ((ip == null) || (ip.length() == 0) || "unknown".equalsIgnoreCase(ip)) ip = req.getHeader("X-Real-IP");

    // HTTP_CLIENT_IP:有些代理服务器
    if ((ip == null) || (ip.length() == 0) || "unknown".equalsIgnoreCase(ip)) ip = req.getHeader("HTTP_CLIENT_IP");

    // 还是不能获取到,最初再通过 request.getRemoteAddr(); 获取
    if ((ip == null) || (ip.length() == 0) || "unknown".equalsIgnoreCase(ip)) ip = req.getRemoteAddr();

    // 有些网络通过多层代理,那么获取到的 ip 就会有多个,个别都是通过逗号(,)宰割开来,并且第一个 ip 为客户端的实在 IP
    return ((ip == null) || (ip.trim().length() == 0)) ? null : ip.split(",")[0].trim();}

7、数据合法性校验

合法性校验包含:常规性校验以及业务校验;
常规性校验:包含签名校验,必填校验,长度校验,类型校验,格局校验等;
业务校验:依据理论业务而定,比方订单金额不能小于 0 等;

以上参考自这篇原文

正文完
 0