1.什么是接口签名?前端
答:接口开发是各系统之间对接的重要方式,其数据是经过开放的互联网传输,对数据的安全性要有必定要求。为了提升传输过程参数的防篡改性,签名sign的方式是目前比较经常使用的方式。java
重点:请求身份是否合法?请求参数是否被恶意篡改,请求是否惟一.web
2.怎么提供一个安全性高的接口?redis
答:算法
2.1. 请求身份:json
Appkey(公钥)开发者标识肯定惟一,AppSecret密钥(用于接口加密,确保生成参数不被猜想)有个缺点密钥让人家知道仍是完蛋了.安全
2.2. 接口加参数服务器
接口加个参数timestamp时间戳,格式为yyyy-mm-dd HH:mm:ss,例如:2013-05-06 13:52:03(建议传入当前时间),防止同一时间内点击两次.app
接口加个参数Appkey(公钥).优化
接口加个参数sign签名字段:签名sign具体算法为:根据传入参数名称(签名sign除外)将全部请求参数按照首字母前后顺序排序,md5(密钥+除密钥外排好序的参数串+密钥)后转换为大写字母,注意编码,统一为utf8。注意要有AppSecret密钥.
相似下面这个样子:
method=xx
&appkey=xx
×tamp=2014-12-26 10:27:26
&format=json
&sign=生成的签名
以上两步,解决了防止防止篡改,参数正确,可是没有解决着重复请求参数伪造二次请求的隐患。也就是重放攻击。
2.3.timestamp+nonce方案
nonce指惟一的随机字符串,用来标识每一个被签名的请求。经过为每一个请求提供一个惟一的标识符,服务器可以防止请求被屡次使用(记录全部用过的nonce以阻止它们被二次使用)。
然而,对服务器来讲永久存储全部接收到的nonce的代价是很是大的。可使用timestamp来优化nonce的存储。
假设容许客户端和服务端最多能存在6分钟的时间差,同时追踪记录在服务端的nonce集合。当有新的请求进入时,首先检查携带的timestamp是否在6分钟内,如超出时间范围,则拒绝,而后查询携带的nonce,如存在已有集合,则拒绝。不然,记录该nonce,并删除集合内时间戳大于6分钟的nonce(可使用redis的expire,新增nonce的同时设置它的超时失效时间为6分钟)。
另外除了公钥密钥,还能够用token身份验证。
服务端验证帐号密码,生成token,存储30分钟,token发给前端,前端每次请求带上这个token。可是仍然存在着安全隐患,token被劫持,伪造请求和篡改参数。
解决方法:Token+(AppKey参数加密),即便Token被劫持,对方不知道AppKey和签名算法,就没法伪造请求和篡改参数。再结合上述的重发攻击解决方案,即便请求参数被劫持也没法伪造二次重复请求。
系统能够设定一些规则:
还有就是排序参数,若是是只调用web接口不超过3个,建议不要写字母排序方法,浪费时间.
下面提供md5utils类:
使用: MD5Utils.MD5Encode(str,"utf8");
import java.security.MessageDigest;
public class MD5Utils {
private static final String hexDigIts[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
/**
* MD5加密
*
* @param origin 字符
* @param charsetname 编码
* @return
*/
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (null == charsetname || "".equals(charsetname)) {
resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
} else {
resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));
}
} catch (Exception e) {
}
return resultString;
}
public static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
public static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n += 256;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigIts[d1] + hexDigIts[d2];
}
}
另外相同的参数,相同的接口重复的调用,能够直接返回:
{
"code": -1,
"msg": "调用频率太过频繁"
}
虽然有些攻击仍是防不到,可是能让你装装逼,让客户看到哇,我此次选的系统接口,很专业。(确实,让我访问接口每访问一次都要加签名什么的,搞得我很难受。)
内容借鉴:(简书做者:Joker_Coding,连接:https://www.jianshu.com/p/ad410836587a)