python接口自动化 -10. 接口签名处理(加密接口)

前言:

服务端与客户端进行http通讯时,为了防止被爬虫,数据安全性等,传参数的时候,不会明文的传输,先对接口加密,返回的数据也加密返回。

对称加密:

对称加密算法是共享密钥加密算法,在加密解密过程中,使用的密钥只有一个。发送和接收双方事先都知道加密的密钥,均使用这个密钥对数据进行加密和解密。

数据加密:在对称加密算法中,数据发送方将明文 (原始数据) 和 加密密钥一起经过加密处理,生成复杂的密文进行发送。

数据解密:数据接收方收到密文后,使用加密的密钥及相同算法的逆算法对加密的密文进行解密,将使其恢复成可读明文。

非对称加密(主流)

非对称加密算法,有两个密钥,一个称为公开密钥 (publickey),另一个称为 私有密钥 (private key),加密和解密使用的是两个不同的密钥,所以这种算法称为非对称加密算法

如果使用公钥对数据进行加密,只有用对应的私钥才能进行解密。

如果使用私钥对数据进行加密,只有用对应的公钥才能进行解密。

常见加密算法的处理方式

根据上述常见的加密算法,测试人员在测试不同的加密接口可采用下述的方法处理加密接口

摘要算法(MD5.SHA1 ):造接口数据前调用MD5,SHA1进行编码,服务端对比编码后的字符串是否一致。

对称加密算法(AES,DES ):造接口数据前从开发获取对称公钥,基于对称公钥可以加密请求数据,解密响应报文。

非对称加密算法(RSA):造接口数据前从开发获取公钥私钥去加密解密接口数据

用户认证

一般的接口测试工具都会提供一个User Auth/Authorization的选项,以Postman为栗子,你可以看到以下的选项:

基本认证(Basic Auth)

摘要认证(Digest Auth)

OAuth 1.0a

OAuth 2.0(最常见,现在的网站接口多数提供此用户认证方式)

在对应的工具上,你可以选取对应的用户认证选项,这里CC先生主要介绍如果用Python如何实现用户认证。

1 首先安装Requests库,Requests库的get()和post()方法均提供有auth参数,用于设置用户签名

2 假定我们有一个接口为添加一个新的公告,接口需要认证:auth=("username","password")

nid 或 name两个参数二选一

3 伪代码:

def test_get_notice_list_nid_sucess(self):
    auth_user = ('admin' , 'admin123456')
    r = requests.get(self.base_url, auth = auth_user, params = {'nid' : 1})
    result = r.json()
    self.assertEqual(result['status'], 200)

....

数字签名(sign)

在使用 HTTP/SOAP 协议传输数据的时候,签名作为其中一个参数,可以起到关键作用:

先来一个简单的,通过客户的密钥,服务端的密钥匹配;

这个很有好理解,例如一个接口传参为:http://127.0.0.1:8000/api/?a=1&b=2

假设签名的密钥为:@signpassword

加上签名之后的接口参数为:http://127.0.0.1:8000/sign/?a=1&b=2&sign=@signpassword

但是,这样的sign 参数明文传输是不安全的,一般会选择一些加密算法,比如MD5 算法(MD5算法是不可逆向的),比如MD5代码如下:

import hashlib
 
md5 = hashlib.md5()
sign_str = "@signpassword"
sign_bytes_utf8 = sign_str.encode()
md5.update(sign_bytes_utf8)
sign_md5 = md5.hexdigest()
print(sign_md5)

执行后得到:6648e929329e53e7a91c50ae685a88b5

此时带签名的接口为:

http://127.0.0.1:8000/sign/?a=1&b=2&sign=6648e929329e53e7a91c50ae685a88b5

所以,当服务器接收到请求后,同样需要对“signpassword”进行 MD5 加密,然后,比对与调用者传来的 sign 加密串是否一致,从而来鉴别调用者是否有权限使用该接口。

接着,我们来理解一个复杂一点的:把sign参数传递为api key(申请获取)+timestramp(时间戳)同样需要 用代码来实现,原理和上面这个一致的。(伪代码)

def setUp(self):
 
    self.base_url = "http://127.0.0.1:8000/api/sec_add_notice/"
    # app_key
    self.api_key = "&APIkey"
    # 当前时间
    now_time = time()
    self.client_time = str(now_time).split('.')[0]
    # sign
    md5 = hashlib.md5()
    sign_str = self.client_time + self.api_key
    sign_bytes_utf8 = sign_str.encode(encoding="utf-8")
    md5.update(sign_bytes_utf8)
    self.sign_md5 = md5.hexdigest()
接口AES加密

通常接口会使用更复杂一点的方式来进行加密的操作,常见的是AES的使用,放一张图让大家感受一下AES加解密的过程:

Python里面有一个很好的黑魔法,叫PyCrypto库,支持常见的 DES、AES 加密以及 MD5、SHA 各种 HASH 运算。

官方网站下载最新版本: https://www.dlitz.net/software/pycrypto/
另外,也可以在 PyPi 仓库中下载安装: https://pypi.python.org/pypi/pycrypto

对于AES的加密来说,看一下用了PyCrypto库的结果

加密:

from Crypto.Cipher import AES# 加密
obj =AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')
message = "The answer is no"
ciphertext = obj.encrypt(message)
print(ciphertext)

程序运行后的结果为:b'\xd6\x83\x8dd!VT\x92\xaa`A\x05\xe0\x9b\x8b\xf1'

AES加密里面有两个关键,一个是key(必须为16,24,32位),一个是VI(必须为16位)

解密:解谜者必须要同时知道key和VI才可以解密

obj2 = AES.new('This is a key123', AES.MODE_CBC, 'This is an IV456')

s = obj2.decrypt(ciphertext)

print(s)

由于Crypto库的作者已停止维护,现推荐安装:

pip install pycrytodome

pycryptodome官方文档:

https://www.pycryptodome.org/en/latest/

下面以Jmeter为例介绍如何实现加密接口的处理,针对这种接口测试的时候,在发起请求以前,我们需要对接口进入如下特殊处理:

1.和开发沟通接口加密的算法以及需要的信息,如果是SHA加密,和开发沟通获取加密的秘钥,以及加密和编码的关键字符串

2.在发起接口请求以前,对接口明文数据进行预处理,在Jmeter的接口实现中,可以通过添加Beanshell脚本,在Beanshell脚本中基于上一个步骤获取到的信息,采用和服务端相同的加密方法和加密流程对接口明文进行加密和编码

3.基于加密后的密文发起加密请求

常见的是:

  • 引入 sign 签名

  • 常见的加密方案有AES加密,RSA加密,MD5加密等。

由于引入签名sign请求头,我们在测APP接口的时候,不填签名数据的话,都会被服务端加密签名校验所拦截,这对我们测接口造成了极大的困扰。也就是说,没有处理好签名,就不能进行下一步。

鉴权失败:

请求加密:

响应加密:

解决方案:

  • 让开发在测试环境关闭签名验证(一杯奶茶试试,但是基本开发也不会这样搞,测试环境关闭签名,有可能上线部署忘记打开签名,造成极大影响);

  • 通过抓包把请求的请求头和请求参数抓下来,通过postman或者jmeter测试(每次抓包,参数都是固定,但是签名随着请求参数变化而变化,每请求一次就就要抓包一次,也难搞。有时候页面没出来,包也抓不了);

  • 复写过程可能比较复杂,可能比较简单,需要开发协助说明整个加密过程(一杯奶茶就好了)复写过程中又可以锻炼自己的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编码

以下的 sign 签名处理的例子:

import hashlib
import base64
key = '12345678900'


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()


if __name__ == '__main__':
    body = {"X-Sign": ""}
    sign_data = Sign.get_sign(body)
    print(sign_data)



# 结果
# {'X-Sign': 'OTg2ZmUxYmUwYWNlY2QyODYyOTNiNzA2NGMxYzUxZjE='}

方案二:

(1)写个函数或者方法,把要加密的参数使用这个函数过滤一遍,等于就是说把数据丢进去,加密了之后,再通过这个加密好的

数据传输过去就可以了!!

(2)至于用什么加密算法,这个要根据产品和自己的业务场景和需求不管是AES或者公钥私钥也好看自己的选择

(3)也可能是编码的问题,就直接用base64码把需要传输加密的东西通过base64返回base64码,然后再放进去,然后再进行传输

(4)这是编码不是加密,真的要加密的话,首先把要用的参数加好密之后再被传输出去,传输的过程中把传输的数据进行一次加密和封装之后再发送过去

(5)用jmeter做接口测试用post-processor加beanshell进行加密解密,再从日志中查找参数,然后具体的加密算法要看需求

(6)每个测试工具提供的加密算法是不一样的,工具不一样加密算法也是不一样的

(7)如果不用工具的话应该怎么做?==》看如下的代码


import requests
import hashlib
import json

# 组建接口
# 第一步:拿到接口入参==》2个参数
# data  必须参数,对应的值是正常传递的参数组装成json字符串,组装的json字符串里面必须包含appId,
# sign  必须参数,对应得值是data值得md5值。
BASE_URL = 'http://118.24.XXX.XXX:8080/api/login?'
appId=4001
code='033qdUtz0LcJsc19tZxz09cTtz0qdUt6'
app_secret_key='dc6e426478b2d29a8e1ad70eb48660f4'


# 第二步:组装data参数
data={"appId":4001,"code":"033qdUtz0LcJsc19tZxz09cTtz0qdUt6"}
# a = str(data)
# print a
# b = '{'+a[45:58] + ','+a[1:43]+'}'
# print b

# 第三步:组装加密字符串拿到sign签名
value = 'data={"appId":4001,"code":"033qdUtz0LcJsc19tZxz09cTtz0qdUt6"}&key=dc6e426478b2d29a8e1ad70eb48660f4'
md5 = hashlib.md5()
md5.update(value)
sign = md5.hexdigest()
print sign

# 第四步:发送接口请求
dic = {"data":'{"appId":4001,"code":"033qdUtz0LcJsc19tZxz09cTtz0qdUt6"}',"sign":sign}
params = {
    "code":'033qdUtz0LcJsc19tZxz09cTtz0qdUt6',
    "params":json.dumps(dic)
}
response = requests.get(url=BASE_URL,params=params)
print response.text
print response.url

接口返回结果:

{"data":"b363e1c071d84757bf164121bad91ede","statusCode":"200"}

api集成

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

jmeter集成

因为jmeter里面的beanshell用的是Java语法,可以直接喊开发(Java后端或者Android开发)直接帮你打个jar包,直接调用即可

import com.xxx.xxxx.Sign;
StringSamplerData=prev.getSamplerData();
String signData = Sign.getSign(SamplerData);
log.info("签名数据是"+signData);

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Python接口自动化中,POST请求是常见的一种请求方式。POST请求通常用于向服务器提交数据,比如表单数据、JSON数据等。在Python中,我们可以使用requests库来发送POST请求。具体步骤如下: 1. 导入requests库 ``` import requests ``` 2. 构造请求参数 POST请求通常需要传递一些参数,比如表单数据、JSON数据等。我们可以使用字典来构造请求参数。 ``` data = { 'username': 'admin', 'password': '123456' } ``` 3. 发送POST请求 使用requests库的post方法来发送POST请求,传递请求的URL和请求参数即可。 ``` url = 'http://www.example.com/login' response = requests.post(url, data=data) ``` 4. 处理响应结果 发送POST请求后,服务器会返回响应结果。我们可以通过response对象来获取响应结果。 ``` print(response.status_code) # 获取响应状态码 print(response.text) # 获取响应内容 ``` 以上就是Python接口自动化中发送POST请求的基本步骤。 ### 回答2: Python接口自动化中,Post请求是常用的一种请求方式,通常用于向服务器提交数据。Post请求与Get请求不同,它将请求参数放在请求体中,而不是放在URL的参数中。 在Python中,可以使用requests库来发送Post请求,其基本用法示例如下: ``` import requests # 构造请求参数 url = "http://www.example.com/api" data = { "username": "xxx", "password": "xxx" } # 发送Post请求 response = requests.post(url, data=data) # 解析响应数据 result = response.json() ``` 以上示例中,首先构造了请求参数,包括请求URL和请求数据。然后使用requests库发送Post请求,其中url参数传入请求URL,data参数传入请求数据。发送请求后,可以通过response.json()方法将响应数据解析为JSON格式的数据,方便后续操作。 在实际使用中,请求参数的构造可能更加复杂,包括请求头、cookies等,可以通过requests库提供的各种方法进行设置。另外,在接口自动化中,需要进行接口测试的时候,通常需要用到断言,可以使用Python自带的assert语句或其他第三方断言库来进行断言操作。 总之,在Python接口自动化中,Post请求是常见的请求方式,使用requests库可以轻松实现Post请求的发送和响应解析,同时也可以进行断言操作,方便进行接口测试。 ### 回答3: Python接口自动化中,使用POST请求是非常常见的操作。POST请求是HTTP协议中的一种请求方式,主要用于向服务器提交数据。相较于GET请求,POST请求通常用于提交表单数据、上传文件和请求创建新资源等场景。 下面我们来介绍Python中如何实现POST请求的操作。 首先需要导入requests库: ```python import requests ``` 接下来,我们可以定义一个POST请求的函数,传入URL、参数和请求头部(可选): ```python def post_request(url, data, headers=None): """ POST请求 """ response = requests.post(url=url, data=data, headers=headers) # 返回响应结果 return response ``` 以上代码中,我们使用requests.post()方法发送POST请求,传入URL、参数和请求头部。其中,URL表示请求的URL地址,data表示请求的数据,headers表示请求头部,可选。 接着,我们可以通过调用上面定义的函数,发送POST请求并获取响应结果: ```python url = "http://example.com/api/login" data = { "username": "testuser", "password": "testpass" } headers = { "User-Agent": "Mozilla/5.0" } # 发送POST请求 response = post_request(url=url, data=data, headers=headers) # 输出响应结果 print(response.text) ``` 以上代码中,我们定义了一个POST请求的URL地址和请求数据,以及一个请求头部(User-Agent)。然后,我们调用post_request()函数,传入URL、数据和请求头,发送POST请求并获取响应结果。最后,我们输出响应结果的文本内容。 除了以上的方式,我们还可以通过requests库的其他方法实现POST请求,如下所示: ```python # 使用JSON格式数据发送POST请求 response = requests.post(url=url, json=data, headers=headers) # 使用文件上传方式发送POST请求 files = {'file': open('file.png', 'rb')} response = requests.post(url=url, files=files, headers=headers) ``` 以上代码分别演示了通过JSON格式数据和文件上传方式发送POST请求的方法。 总的来说,Python实现接口自动化中的POST请求操作,其实就是通过requests库提供的post()方法或其他方法,传入请求的URL、数据和请求头部,即可实现。同时,需要注意请求参数的格式和请求头部的设置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值