Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

移动接口安全规范

$
0
0

接口一律采用https , 如:

https://api.xxx.cn/version/controller/action 请求参数 字段名 变量名 类型 描述 业务动作 action String(32) 操作, 如: add 业务内容 content String(32) 业务数据, json对象 控制模块 controller String(32) 业务模块, 如: user 随机字符串 nonceStr String(32) 随机字符串, 不长于32位。推荐随机数生成算法 签名 sign String(32) 签名,详见签名生成算法 签名类型 sign_type String(32) 签名类型, 如HMAC-SHA256或MD5, 默认为MD5 服务Id serviceId String(32) 用于服务器快速定位用户的临时数据, 在app第一次注册的时候获取 返回结果 字段名 变量名 类型 描述 业务动作 action String(32) 操作, 如: add 业务内容 content String(32) 业务数据, json对象 控制模块 controller String(32) 业务模块, 如: user 返回状态码 returnCode String(16) SUCCESS/FAIL 此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断 返回信息 returnMsg String(128) 返回信息,如非空,为错误原因签名失败参数格式校验错误 签名算法

第一步

将所有的数据(剔除签名字段), 按照key值的ASCII码从小到大排序, 直接输出成json字符串stringA.

参数名ASCII码从小到大排序(字典序); 参数名区分大小写;

第二步

在stringA的后面拼接和服务器约定的密钥, 使用密码. 得到stringATemp, 再对stringATemp进行MD5运算, 将得到的字符串全部转换成大写, 得到sign的值

假设待提交的数据:

{ "action": "add", "nonceStr": "ibuaiVcKdpRxkhJA", "content": { "userName": "111", "passWord":"123" }, "controller": "user", }

第一步之后得到字符串

stringA = {"action": "add","content": {"userName": "111","passWord":"123" },"controller": "user","nonceStr":"ibuaiVcKdpRxkhJA"}

第二步拼接key

stringATemp = stringA + password sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7"

最终得到的发出去的数据为:

{ "action": "add", "nonceStr": "ibuaiVcKdpRxkhJA", "content": { "userName": "111", "passWord":"123" }, "controller": "user", "sign": "9A0A8659F005D6984697E2CA0A9CF3B7" } key值的约定

key值的约定

由于服务器的密码存储一般是 md5(password + salt) 记为 A 值, 但是在login的时候, 这个 A 值会在网络层中传输一次, 如果攻击者劫持了login接口, 那么就获取了key值,

所以建议在服务器表中除了 A , 再多存储一个 md5(password + salt2) 记为 B ,

这个 B 值作为客户的与服务器约定密码key值, 即传输报文的salt.

随机数

主要保证签名不可预测

java public static String random() { return UUID.randomUUID().toString().replace("-", ""); } oc + (NSString *)random { return [[[NSUUID UUID] UUIDString] stringByReplacingOccurrencesOfString:@"-" withString:@""]; } 实现 Android

实现算法的第一步和第二步 (使用 fastjson , 可自动实现key值排序)

/** * 计算jsonObject的json值, 并添加key * * @param jsonObject * @param uuid, 和服务器约定的密钥, 可用password代替 * @return */ publicstaticStringencode(JSONObject jsonObject, String uuid){ StringBuilder sb = new StringBuilder(); sb.append(jsonObject.toJSONString()); sb.append(uuid); String md5 = MD5.stringMD5(sb.toString()); return md5; }

调用

/** * 对发送的json加密 * * @param jsonObject * @param uuid * @param serviceId * @return */ publicstaticJSONObjectencryptJSON(JSONObject jsonObject, String uuid, String serviceId){ jsonObject.put("sign", encode(jsonObject, uuid)); jsonObject.put("serviceId", serviceId); return jsonObject; }

校验服务器返回数据

/** * 检查服务器返回json是否正确 * * @param jsonObject * @param uuid, 和服务器约定的密钥, 一般可用用户密码 * @return */ publicstaticbooleancheckEncrypt(JSONObject jsonObject, String uuid){ String checkNumService = jsonObject.getString("sign"); jsonObject.remove("sign"); return checkNumService != null && checkNumService.equals(encode(jsonObject, uuid)); } iOS

工具方法

// 获取随机数 + (NSString *)random { return [[[NSUUID UUID] UUIDString] stringByReplacingOccurrencesOfString:@"-" withString:@""]; } // MD5计算 + (NSString *)md5:(NSString *)str { const char *cStr = [str UTF8String];//转换成utf-8 unsigned char result[16];//开辟一个16字节(128位:md5加密出来就是128位/bit)的空间(一个字节=8字位=8个二进制数) CC_MD5(cStr, strlen(cStr), result); /* extern unsigned char *CC_MD5(const void *data, CC_LONG len, unsigned char *md)官方封装好的加密方法 把cStr字符串转换成了32位的16进制数列(这个过程不可逆转) 存储到了result这个空间中 */ return [NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15] ]; /* x表示十六进制,%02X 意思是不足两位将用0补齐,如果多余两位则不影响 NSLog("%02X", 0x888); //888 NSLog("%02X", 0x4); //04 */ } //字典转json格式字符串: + (NSString *)dictionaryToJson:(NSDictionary *)dic { NSError *parseError = nil; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError]; return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; }

实现算法第一步和第二步

// 加密 + (NSString *)encode:(NSMutableDictionary *)jsonObject uuid:(NSString *)uuid { jsonObject[NONCE_STR] = [self random]; //在原来的json里面添加随机字符串 // 对字典输出json string 精选排序 OrderedDictionary *orderedDictionary = [[OrderedDictionary alloc] initWithDictionary:jsonObject]; NSString *json = [orderedDictionary toJSONString]; NSString *jsonString = [json stringByAppendingString:uuid]; // 将json -> string NSLog(@"jsonString %@", jsonString); return [[self md5:jsonString] uppercaseString]; // -> md5 -> upper case }

NSDictionary按属性排序导出json string

OrderedDictionary 代码地址

调用

// 对发送数据加密 + (NSMutableDictionary *)encryptJSON:(NSMutableDictionary *)jsonObject uuid:(NSString *)uuid serviceId:(NSString *)serviceId { [jsonObject setObject:[self encode:jsonObject uuid:uuid] forKey:SIGN]; [jsonObject setObject:serviceId forKey:SERVICE_ID]; return jsonObject; }

校验

//校验收到的数据是否被篡改 + (BOOL)checkEncrypt:(NSMutableDictionary *)jsonObject uuid:(NSString *)uuid { NSString *sign = [jsonObject objectForKey:SIGN]; [jsonObject removeObjectForKey:SIGN]; return sign != nil && [sign isEqualToString:[self encode:jsonObject uuid:uuid]]; } 服务器

java 和Android一致, 只是不需要传serviceId

实现算法的第一步和第二步 (使用 fastjson , 可自动实现key值排序)

/** * 计算jsonObject的json值, 并添加key * * @param jsonObject * @param uuid, 和服务器约定的密钥, 可用password代替 * @return */ publicstaticStringencode(JSONObject jsonObject, String uuid){ StringBuilder sb = new StringBuilder(); sb.append(jsonObject.toJSONString()); sb.append(uuid); String md5 = MD5.stringMD5(sb.toString()); return md5; }

调用

/** * 对发送的json加密 * * @param jsonObject * @param uuid * @param serviceId * @return */ publicstaticJSONObjectencryptJSON(JSONObject jsonObject, String uuid){ jsonObject.put("sign", encode(jsonObject, uuid)); return jsonObject; }

校验服务器返回数据

/** * 检查客户的提交json是否正确 * * @param jsonObject * @param uuid, 和客户端约定的密钥, 一般可用用户密码 * @return */ publicstaticbooleancheckEncrypt(JSONObject jsonObject, String uuid){ String checkNumService = jsonObject.getString("sign"); jsonObject.remove("sign"); return checkNumService != null && checkNumService.equals(encode(jsonObject, uuid)); } 说明

由于iOS的 Dictionary 的无序性, 这里提供一个自己实现的OrderedDictionary, 源码献上 .


Viewing all articles
Browse latest Browse all 12749

Trending Articles