api 接口 fuzz 測(cè)試初探

Alt pic

目標(biāo)

在日常測(cè)試工作中,經(jīng)常會(huì)有api接口的測(cè)試,除了正向流程的測(cè)試之外,我們經(jīng)常還需要覆蓋一些異常情況。

例如:

  • 不合法字符串
  • 字符串超長(zhǎng)
  • 應(yīng)該是數(shù)字類型的,傳入了字母
  • 參數(shù)為空
  • 傳入了中文,標(biāo)點(diǎn)符號(hào)等
  • sql注入等等

事實(shí)上,我們組的接口測(cè)試demo框架中,在dataprovider中也經(jīng)常能夠看到諸如下面的例子。

@DataProvider(name = "testIllegalName")
    public static Object[][] testIllegalName(){

    return new Object[][]{
            
            //name 
            {null, 400, "域名為空或者域名非法"},
            {"", 400, "域名為空或者域名非法"},
            {"abcdefghijilmnopqrstu", 400, "域名為空或者域名非法"},
            {" ", 400, "域名為空或者域名非法"},
            {"12", 400, "域名為空或者域名非法"},
            {"-12", 400, "域名為空或者域名非法"},
            {"0.2", 400, "域名為空或者域名非法"},
            {"abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcd.com", 400, "域名為空或者域名非法"},
            {"zxq.qa.com", 400, "域名為空或者域名非法"},
            {"zxq_qa.com", 400, "域名為空或者域名非法"},

                            
        };
    }

此處是看看接口在傳入非期望值的時(shí)候,能不能夠很好的處理類似請(qǐng)求。

除此以外,還有一些和業(yè)務(wù)場(chǎng)景強(qiáng)相關(guān)的值類型,比如網(wǎng)絡(luò)測(cè)試的時(shí)候,我們會(huì)關(guān)心cidr的格式;計(jì)費(fèi)測(cè)試的時(shí)候,又特別關(guān)注數(shù)字的類型。

一方面,給每個(gè)接口增加類似的異常接口測(cè)試相對(duì)比較無趣;另一方面,我們作為人,考慮問題,不管是開發(fā)還是測(cè)試,都難免掛一漏萬,有一些邊邊角角的case沒能考慮到。

既然如此,我們能否統(tǒng)一抽象出來一種接口異常測(cè)試的框架,自動(dòng) 注入各種類型的異常,然后將凡是服務(wù)沒有捕獲的,拋出trace, exception 的,記錄下請(qǐng)求的payload,為后續(xù)驗(yàn)證覆蓋提供支撐。

原理

主要使用了模糊測(cè)試技術(shù)(fuzz testing, fuzzing)。其核心思想是自動(dòng)或半自動(dòng)的生成隨機(jī)數(shù)據(jù)輸入到一個(gè)程序中,并監(jiān)視程序異常,如崩潰,斷言(assertion)失敗,以發(fā)現(xiàn)可能的程序錯(cuò)誤,比如內(nèi)存泄漏。(摘抄之維基百科)

簡(jiǎn)單的模糊測(cè)試隨機(jī)輸入數(shù)據(jù),而更加高效的模糊測(cè)試,需要理解對(duì)象結(jié)構(gòu)或者協(xié)議。通過向數(shù)據(jù)內(nèi)容,結(jié)構(gòu),消息,序列中引入一些異常,來人為的構(gòu)造聰明的模糊測(cè)試。

如果你持續(xù)關(guān)注文件系統(tǒng)或內(nèi)核技術(shù),你一定注意過這樣一篇文章:Fuzzing filesystem with AFL。Vegard Nossum 和 Quentin Casasnovas 在 2016 年將用戶態(tài)的 Fuzzing 工具 AFL(American Fuzzing Lop)遷移到內(nèi)核態(tài),并針對(duì)文件系統(tǒng)進(jìn)行了測(cè)試。

結(jié)果是相當(dāng)驚人的。Btrfs,作為 SLES(SUSE Linux Enterprise Server)的默認(rèn)文件系統(tǒng),僅在測(cè)試中堅(jiān)持了 5 秒鐘就掛了。而 ext4 堅(jiān)持時(shí)間最長(zhǎng),但也僅有 2 個(gè)小時(shí)而已。(https://zhuanlan.zhihu.com/p/28828826)

所以基于此,在api接口測(cè)試中引入模糊測(cè)試?yán)碚撋弦彩强尚械模沂怯行У摹?/p>

舉例

經(jīng)過一番調(diào)研和搜索之后,發(fā)現(xiàn)了以下這個(gè)項(xiàng)目在接口fuzz測(cè)試中,有比較好的上手體驗(yàn)。

pip install PyJFuzz
git clone https://github.com/dzonerzy/PyJFAPI.git

我對(duì) PyJFAPI 稍微進(jìn)行了一些修改,包括日志記錄,以及異常判斷的地方,只記錄服務(wù)器返回500錯(cuò)誤的情況等。

首先需要準(zhǔn)本一個(gè)請(qǐng)求的模板。

cat request.text

POST /v2.0/networks.json HTTP/1.1
Host: pubbeta1-iaas.service.163.org
X-Auth-Token: 6645b224a8314d0c89e09a011cbddf53
Content-Type: application/json
Accept: application/json


***{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}}***

這里是一個(gè) post請(qǐng)求,定義了一些請(qǐng)求頭和請(qǐng)求體。星號(hào)之間的請(qǐng)求json體,為異常注入點(diǎn)。
它會(huì)自動(dòng)分析你的json格式,生成各種 payload。理論上來說,你只需要給它提供一份接口的scheme就行(要是所有接口都可以從Swagger直接導(dǎo)出那就很方便了)。

運(yùn)行:

python pjfapi.py -H pubbeta1-iaas.service.163.org  -P 9797  -T request.txt 

返回

Alt pic

我們可以手工請(qǐng)求一下這些導(dǎo)致異常的payload。
實(shí)例1:

(hzx_env) hzhuangzhexiao@pubbeta1-nova10:~$ curl -i http://pubbeta1-iaas.service.163.org:9797/v2.0/networks.json -X POST -H "X-Auth-Token: 7d52816088e84e5392019ed00c2f386f" -H "Content-Type: application/json" -H "Accept: application/json" -H "User-Agent: python-protonclient" -d '{"network": {"cidr": "20.010.0.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}}'
HTTP/1.1 500 
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 9586
Date: Tue, 14 Nov 2017 05:59:42 GMT
Connection: close

An unknown exception occurred.
java.lang.IllegalArgumentException: '20.010.0.0' is not an IP string literal.
    at com.google.common.net.InetAddresses.formatIllegalArgumentException(InetAddresses.java:1035)
    at com.google.common.net.InetAddresses.forString(InetAddresses.java:154)
    at com.netease.cns.proton.server.utils.HelperUtils.convertIpStringToInt(HelperUtils.java:107)
    at com.netease.cns.proton.server.utils.HelperUtils.getIp4NetworkProtoBuf(HelperUtils.java:189)
    at com.netease.cns.proton.server.utils.HelperUtils.getIp4NetworkProtoBuf(HelperUtils.java:194)

事實(shí)上,我們本來已經(jīng)對(duì)這個(gè)cidr參數(shù)進(jìn)行了一些異常值的測(cè)試。包括:

"cidr": "20.256.0.0/16"    不規(guī)范的類型
"cidr": "20.ssss.0.0/16"  部分為字符串
"cidr": "ssssss"   全為字符串
"cidr": ""          為空

等等??梢园l(fā)現(xiàn),還是有部分特殊情形沒有考慮到。

實(shí)例2

(hzx_env) hzhuangzhexiao@pubbeta1-nova10:~$ curl -i http://pubbeta1-iaas.service.163.org:9797/v2.0/security-group-rules.json -X POST -H "X-Auth-Token: af75ed821eeb4d5c9e88fb4ba804ff48" -H "Content-Type: application/json" -H "Accept: application/json" -H "User-Agent: python-protonclient" -d '{"security_group_rule": {"direction": "ingress", "protocol": "tcp", "ethertype": "IPv4", "port_range_max": "6660", "security_group_id": "48b9cc1e-53f8-4f7e-8983-bffb209153f3", "port_range_min": "80", "remote_ip_prefix": "0.0.0.0/"}}'
HTTP/1.1 500 
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 6310
Date: Tue, 14 Nov 2017 05:59:10 GMT
Connection: close

An unknown exception occurred.
java.lang.ArrayIndexOutOfBoundsException: 1
    at com.netease.cns.proton.server.service.SecurityGroupServiceImpl.validateIpPrefix(SecurityGroupServiceImpl.java:385)
    at com.netease.cns.proton.server.service.SecurityGroupServiceImpl.createSecurityGroupRule(SecurityGroupServiceImpl.java:228)
    at sun.reflect.GeneratedMethodAccessor385.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)

我把程序構(gòu)造的部分異常打印出來,可以看到類型還是很豐富的。

{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1D, "admin_state_up"_ true}}
{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test-1", "admin_state_up": true}}
{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}}
{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_utrue}}
{"network-": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuup": true}}
{"network": {"cidr": "20.1.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}}
{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", : tru}e}
{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vp?-test1", "admin_state_up": true}}
{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1"?: true}}
{"network": {"cdr": "20100..0.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}}
{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_stap"::: true}}
{"network": {"cidr": "20.100.0.0/16", "name": hzx-vpc-test1", "admin_state_up": true}}
"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}}
{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_up":} true}}
{"neeeeeeeeeeeeeeeeeeeeeeeeeeeeeetwork": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_up""admin_state_up""admin_state_up""admin_state_up""admin_state_up""admin_state_up""admin_s,ate_up""admin_state_up""admin_state_up": true}}
{"network": }
"n{et???: r{"cidr": "0.}0.0/-34359738368", "naD: true}}
{"network": }
{"[network": {"cidr": "20.-214748364800.0.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}}
{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}{"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}{"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}}
{"network": {"cidr": "20.1000.0/+.16", "name": "hzx-vpc-test1", "admin_state_up": true}}
{"network": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_up""admin_state_up""admin_state_up": true}}
{"networ[k": {"cidr": "20.100.0.0/16", "name": "hzx-vpc-test1", "admin_state_up": true}}
{"network": {"cidr": "20.100.0.0/-4080", "name": "hzx-vpc-test1", "admin_state_up": true}}
{"network": {"cidr": "20.100.0.0/-4080", "name": "hzx-vpc-test1", "admin_state_up": true}}
{"network": {"cidr": "20.-25500.0.0/16", "name": "hzx-vpc-test1", "admin_stateeeeeeeeeeeeeeeeeeeee_up": true}}

pjfapi.py 腳本本身使用方法很簡(jiǎn)單。 -h 看下help為命令行參數(shù)的基本說明。

Alt pic

結(jié)論

本文簡(jiǎn)要的介紹了fuzz測(cè)試技術(shù),以及將其應(yīng)用到api接口測(cè)試中的實(shí)現(xiàn),給出了一個(gè)具體的fuzz接口測(cè)試?yán)印K灰欢軌蛲耆娲羧斯さ慕涌诋惓?,但是可以作為一個(gè)很好的補(bǔ)充。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,346評(píng)論 25 708
  • 去學(xué)校。 早上:帶梳子,2塊臉巾,充電器,充電寶,衣服,拖鞋,護(hù)膚品,牙膏牙刷。 要買:洗發(fā)水,沐浴露,衣架,杯子...
    看云的猴子閱讀 294評(píng)論 0 0
  • 今天很久不聯(lián)系的一個(gè)朋友,問我哥在不在我們老家的醫(yī)院上班,我說不在。然后告訴我她的媽媽明天要?jiǎng)邮中g(shù),是內(nèi)部隔膜爛了...
    三秋蟲閱讀 519評(píng)論 0 0
  • ? 朋友圈里,每天都是熙熙攘攘的曬幸福,仿佛流水線上整容一樣的演技。 我們給觀眾們看到的都是最美好的那一刻,而華麗...
    MissMatcha閱讀 427評(píng)論 0 0

友情鏈接更多精彩內(nèi)容