51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

查看: 753|回复: 0
打印 上一主题 下一主题

测接口遇到APP加密?先来了解一下算法思路~

[复制链接]
  • TA的每日心情
    擦汗
    13 小时前
  • 签到天数: 942 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-6-17 13:58:03 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    背景
      服务端与客户端进行http通讯时,为了防止被爬虫,数据安全性等,引入APP通信加密,简单来说,就是引入签名sign,APP的所有请求都会经过加密签名校验流程。常见的加密方案有AES加密,RSA加密,MD5加密等。由于引入签名sign请求头,我们在测APP接口的时候,不填签名数据的话,都会被服务端加密签名校验所拦截,这对我们测接口造成了极大的困扰。
    1. {
    2.     "msg": "鉴权失败",
    3.     "code": 99999,
    4.     "data": {}
    5. }
    复制代码
     解决方案
      ·叫开发在测试环境关闭签名验证
      · 通过抓包把APP的请求头和请求参数抓下来,通过postman测试
      · 询问开发具体的加密过程,复写一套加密算法,自己生成加密数据

      方案分析
      测试环境关闭签名验证,可能对代码改动比较大,容易留下坑,有可能上线部署时忘记打开签名验证,这会造成极大的影响;也有可能改动不大,开发代码比较规范,上线比较规范;(每个公司情况不一样~)可以作为备选方案。
      每次抓包时,请求参数都是固定的,而签名数据又是随着请求参数变化而变化,这对于测试接口的多场景比较麻烦,每点一次就抓包一次;有时候前后端还没联调,让你提前介入接口测试,没有页面怎么抓包,抛弃。
      复写过程可能比较复杂,可能比较简单,需要开发协助说明整个加密过程(一杯奶茶就好了)复写过程中又可以锻炼自己的code能力,推荐。

      方案实现过程
      验签说明
      参数名按ASCII码从小到大排序(字典序)如果参数的值为空(null值,空格、空字符串)不参与签名,参数名区分大小写;如参数的类型为复合类型不参与签名(如:List[Map]类型或List[String]类型或List[int]类型)。
      使用URL键值对的格式(key1=value1&key2=value2…)拼接成待签名的字符串plain,在plain最后拼接上key(密钥),plain=plain+”&key=分配的密钥”,再将plain进行签名运算sign=Base64(MD5(plain))。
      sign字符串放在http报文头中X-Sign字段。

      验签步骤拆解
      · 参数名按ASCII码从小到大排序
      · 参数值为空(null值,空格、空字符串)不参与签名
      · 参数值为复合类型(对应[url=]Python[/url]中的dict、list)不参与签名
      · 拼接键值对,格式为key1=value1&key2=value2…
      · 键值对后面拼接key(密钥)
      · 键值对字符串先经过MD5加密,再进行base64编码

      py实现
    import hashlib
    import base64
    key = '123456'
    class Sign:
        @classmethod
        def get_sign(cls, data):

            # 不改变传入的data=>直接对字典进行复制
            data = data.copy()
            # 首先判断字典是否空,为空直接加密
            if not data:
                string = ''
            else:
                # 非空字典,过滤value为空和嵌套字典、列表
                for k, v in list(data.items()):
                    if isinstance(v,str):
                        v = v.replace(" ", "").replace("\n", "")
                        data[k] = v
                    if v == '' or v is None or isinstance(v,(dict,list)):
                        data.pop(k)

                # 对字典进行ASCII码排序
                new_list = sorted(data.items())
                alist = ['&' + str(i[0]) + '=' + str(i[1]) for i in new_list]
                string = ''.join(alist)
            return {"X-Sign": cls.encry(string)}

        @classmethod
        def encry(cls, string):
            """
            加密算法
            :param string: 要加密的字符串
            :return: 字符串
            """
            if string:
                sign = string[1:] + '&key=' + key
            else:
                sign = 'key=' + key
            m = hashlib.md5()
            m.update(sign.encode("utf8"))
            encodeStr = m.hexdigest()
            base_code = base64.b64encode(encodeStr.encode('utf-8'))
            return base_code.decode()


      因为有用到dict.pop()方法,这会对原来的data进行改变,所以我们传入data的时候,对data进行浅拷贝:
    1. data = data.copy()
    复制代码
    字符串要去除空格、换行符,实际服务端进行加密生成时,也会对字符串进行去除空格、换行符:

    1. if isinstance(v,str):
    2.     v = v.replace(" ", "").replace("\n", "")
    3.     data[k] = v
    复制代码
    算法验证

    import requests
        data ={
    "mercId": "888000000000001",
    "sysCnl": "IOS",
    "orderTypes": [0, 1, 2, 3, 4, 5, 6, 7, 9, 10],
    "platform": "LXMALL",
    "limit": 20,
    "page": 1,
    "timestamp": "1625881419"
    }
        sign_data = Sign.get_sign(data)
        headers = {
            "X-Token" : "08fa6dad125d30842c58d9bea6059ace",
            "X-APPVer": "1.5",
            "X-SignVer": "v1",
            "Content-Type": "application/json"
        }
        headers |= sign_data
        url = base_url + 'order/list'
        r = requests.post(url,json = data,headers = headers)
        print(r.json())

    成功打印输出返回参数,大功告成~

      api集成
      你可以将生成加密数据做成一个api提供给小伙伴使用,传入待加密的请求报文,返回加密数据,小伙伴复制粘贴加密数据到请求头,即可完成接口测试。

      jmeter集成
      因为jmeter里面的beanshell用的是Java语法,可以直接喊开发(Java后端或者Android开发)直接帮你打个jar包,直接调用即可。
    1. //找不到以前的脚本,大家可以去网上搜索一下的源码,大致是下面的
    2. import com.xxx.xxxx.Sign;
    3. String SamplerData=prev.getSamplerData();
    4. String signData = Sign.getSign(SamplerData);
    5. log.info("签名数据是"+signData);
    复制代码
     总结
      本期解决了APP接口测试的痛点,希望能对大家有帮助~





    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-5-6 22:45 , Processed in 0.060021 second(s), 22 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表