解决的问题:检测公司发布系统之中,遗漏的接口路径地址。
采取的方法:爬取yapi管理后台,爬取所有的接口路径,同时检测这个path路径是否在发布系统之中(如果不在发布系统之后,通过域名访问,会提示没有权限)
好了了解了背景之后 我就开始开工了。
一、了解yapi
在这里小编看到了 左边是产品 右边是产品对应的模块名称 点击模块名称 我们可以看到很多个接口地址。
二、分析问题
了解到yapi的大致情况之后,我们知道点击产品,之后点击产品所属的模块,每个模块下面有对应模块的接口地址,相比就知道了,就二个循环解决的问题
1.根据产品id(这里我叫做group_id ) 找寻所有的模块id ,分析如下
2.根据模块id,找寻所有的接口路径。
3.找到一个接口路径 就通过requests请求对应地址访问一下,如果权限不足的就保存下来。
初略的了解了一下之后,我们发现获取模块id,和获取接口路径哪里,可能会分页,为了避免进行翻页爬虫,简化脚本 小编这里先将分页条数(limit)扩大了,另外还有一个地方,就是检查权限不足的时候,返回的格式是byte的我们需要decode转成str字符串的格式,但是呢,有些接口返回的又不是byte,所以当你把所有的接口都强制decode的时候,可能会报异常,这里我通过使用"pass"得到了解决。
当然这里换种策略 不选择爬yapi后台的话,也可以连接数据库进行查询在校验的(mongodb)
三、代码部分
# author: xiejiangpeng # time:2018/11/7/20:35 # python:python3.6 # message: 连接api.host.com获取接口 校验权限 import requests import json import time class CheckUrlJurisdiction(object): """ 实例初始化字段说明 userapi: api后台登录账号 useradmin: base后台登录账号 passwdapi: api后台登录密码 passwdadmin: base后台登录密码 group_id: api后台需要检测产品id(base:35 团购:176) self.countTotal: path总数 self.countnum: path权限不足数量 self.pathmodle: path所属模块名称 self.pathname: path详细名称 self.time: 运行花费时间 """ def __init__(self, userapi, useradmin, passwdapi, passwdadmin, group_id): self.userapi = userapi self.useradmin = useradmin self.passwdapi = passwdapi self.passwdadmin = passwdadmin self.group_id = group_id self.countTotal = 0 self.countnum = 0 self.pathmodle = "" self.pathname = "" self.time = float() self.apiurl = "http://api.host.com" self.adminurl = "http://base.host.com" self.api = requests.session() self.admin = requests.session() """api_login""" loginjson = {'email': self.userapi, 'password': self.passwdapi} loginheader = {'Content-Type': 'application/json;charset=UTF-8'} print("api后台登录状态:", self.api.post(url=self.apiurl + "/api/user/login", data=json.dumps(loginjson), verify=False, headers=loginheader).json().get("errmsg")) """admin_login""" loginbody = {'name': useradmin, 'password': passwdadmin, 'verify_code': ''} print("base后台登录状态: %s" % self.admin.post(url=self.adminurl + "/superAdmin/loginSuper/login", data=loginbody, verify=False).json().get("status")) def geturlpath(self): """根据group_id获取对应模块id limit为每页模块条数""" print("正在检测发布系统遗漏的接口地址(本次程序检测所花费时间可能较长,请耐心等待)") starttime = time.clock() getid = {'group_id': self.group_id, 'page': '1', 'limit': '50'} ids = self.api.get(url=self.apiurl + "/api/project/list", verify=False, params=getid).json().get("data").get( "list") for i in range(0, len(ids)): self.pathmodle = ids[i].get("name") self.get_path_by_id(ids[i].get("_id")) timeend = time.clock() self.time = str("%.2f" % (timeend - starttime)) """通过模块id找寻path""" def get_path_by_id(self, _id): """避免翻页,条数设置为999""" details = self.api.get(self.apiurl + "/api/interface/list?page=1&limit=999&project_id=%s" % _id, verify=False).json() for j in range(0, len(details.get("data").get("list"))): self.countTotal += 1 path = details.get("data").get("list")[j].get("path") self.pathname = details.get("data").get("list")[j].get("title") self.check_path(path) """根据path提取出没有权限的信息""" def check_path(self, path): """检查是否有权限""" result = self.admin.get(url=self.adminurl + path, verify=False) if result.status_code == 200: try: if result.content.decode("utf-8").find("权限") != -1: print("%s url信息:%s/%s" % (path, self.pathmodle, self.pathname)) self.countnum += 1 except UnicodeDecodeError: pass if __name__ == "__main__": xiejiangpeng = CheckUrlJurisdiction("apiuser", "baseuser", "apipasswd", "basepasswd", "group_id") xiejiangpeng.geturlpath() print("总共检测path数 %s" % xiejiangpeng.countTotal) print("权限不足path数 %s" % xiejiangpeng.countnum) print("本次检测所需时间为 %s秒" % xiejiangpeng.time)
四、运行效果
八百五十七个接口检测完毕 总共花费时间三分钟不到 找出权限不足的数量为15个
好了分享到此结束,脚本需要优化的地方还有很多,比如:运行完毕之后通过邮件的形式发送给对应的人员,并且还可以优化一下产品id,以及模块下面对应的url采取分页爬虫的形式等等等。以后给大家分享其他的有关信息