安全|API接口安全性设计(防篡改和重复调用)

admin 2021年11月18日17:15:58安全|API接口安全性设计(防篡改和重复调用)已关闭评论141 views字数 2379阅读7分55秒阅读模式

API接口的安全性主要是为了保证数据不会被篡改和重复调用,实现方案主要围绕Token、时间戳和Sign三个机制展开设计。

1. Token授权机制
用户使用用户名密码登录后服务器给客户端返回一个Token(必须要保证唯一,可以结合UUID和本地设备标示),并将Token-UserId以键值对的形式存放在缓存服务器中(我们是使用Redis),并要设置失效时间。服务端接收到请求后进行Token验证,如果Token不存在,说明请求无效。Token是客户端访问服务端的凭证。

# uuid 是手机设备的唯一标示
String token = UUID.randomUUID().toString() + "_" + uuid;

2. 时间戳超时机制
用户每次请求都带上当前时间的时间戳timestamp,服务端接收到timestamp后跟当前时间进行比对,如果时间差大于一定时间(比如30秒),则认为该请求失效。时间戳超时机制是防御重复调用和爬取数据的有效手段。
当然这里需要注意的地方是保证客户端和服务端的“当前时间”是一致的,我们采取的对齐方式是客户端第一次连接服务端时请求一个接口获取服务端的当前时间A1,再和客户端的当前时间B1做一个差异化计算(A1-B1=AB),得出差异值AB,客户端再后面的请求中都是传B1+AB给到服务端。

Long timeStamp = RequestHeaderContext.getInstance().getTimeStamp();
boolean checkTime = checkTime(timeStamp, 30 * 1000);
if (!checkTime) {
    return responseErrorAPISecurity(response);
}


public static boolean checkTime(Long time, Integer variable){
    Long currentTimeMillis = System.currentTimeMillis();
    Long addTime = currentTimeMillis + variable;
    Long subTime = currentTimeMillis - variable;
    if (addTime > time && time > subTime){
        return true;
    }
    return false;
}

3. API签名机制
将“请求的API参数”+“时间戳”+“盐”进行MD5算法加密,加密后的数据就是本次请求的签名signature,服务端接收到请求后以同样的算法得到签名,并跟当前的签名进行比对,如果不一样,说明参数被更改过,直接返回错误标识。签名机制保证了数据不会被篡改。

StringBuffer urlSign = new StringBuffer();

if ("POST".equals(request.getMethod()) || "PUT".equals(request.getMethod())) {
    String bodyStr = RequestReaderUtil.ReadAsChars(request);
    String bodySign = "";
    if (!StringUtils.isEmpty(bodyStr)){
        bodySign = DigestUtils.md5DigestAsHex((bodyStr).getBytes());
    }
    urlSign = new StringBuffer(bodySign);
} else if ("GET".equals(request.getMethod()) || "DELETE".equals(request.getMethod())) {
    String params = request.getQueryString();
    if (params == null){
        params = "";
    }
    urlSign = new StringBuffer(params);
}

String sign = DigestUtils.md5DigestAsHex(urlSign.append(timeStamp).append(salt).toString().getBytes());


if (signature.equals(sign)) {
    return true;
} else {
    return false;
}

4. 注意事项
1、因为用户登录的Token是和设备唯一标示绑定的,所以一个用户有可能会有多个有效的Token,那么当用户在修改登录密码时需要把所有的Token删除,我的做法是在Redis保存了一个value是List(值是该用户所有的有效的Token)的Key,当修改密码时会把该Key下的所有Token干掉。
2、客户端每次请求,在Header里面有timeStamp的值,签名中也是用这个timeStamp组合签名的,要确保这两个值是一致的。因为我们在实际开发中,发现客户端的同事在加密时通过函数获取了当前时间A,在请求时也通过函数获取了当前时间B,有时候这两个当前时间会差几毫秒,导致签名校验失败。

private String token;


private String signature;

 
private String udid;


private Long timeStamp;

5. 安全保障总结
在以上机制下,
如果有人劫持了请求,并对请求中的参数进行了修改,签名就无法通过;
如果有人使用已经劫持的URL进行DOS攻击和爬取数据,那么他也只能最多使用30s;
如果签名算法都泄露了怎么办?可能性很小,因为这里的“盐”值只有我们的CTO知道。

相关推荐: CWE-486 使用名称来比较对象

CWE-486 使用名称来比较对象 Comparison of Classes by Name 结构: Simple Abstraction: Variant 状态: Draft 被利用可能性: High 基本描述 The program compares c…

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年11月18日17:15:58
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   安全|API接口安全性设计(防篡改和重复调用)http://cn-sec.com/archives/638930.html