踩了不少坑,写个文档记录下。
如果要复制底代码请访问飞书链接:https://c6k50tuyg6.feishu.cn/wiki/D2XEwjOb3iZzaKkMOvgcBFudnNb?from=from\_copylink
https://github.com/metlo-labs/metlo/
官方介绍:
Metlo 是一款开源 API 安全工具,只需不到 15 分钟即可完成设置,它可以管理API、检测危险行为并实时阻止恶意流量。
实时检测 API 攻击。
自动阻止恶意行为者。
创建 API 端点和敏感数据清单。
在投入生产环境前主动测试 API。
我是部署在内网,通过cf的tunnel功能实现外网https安全访问。
melto支持多种语言、多种云原生环境的流量接入。
插件我已经写好了,动动小手复制一下。
yakit_output(MITM_PARAMS)#-----------------------MITM Hooks I/O-------------------------/*#如何使用插件参数?## 例如,如果你设置了一个参数为 url_keyword 的参数,可以通过 MITM_PARAMS 来使用它!urlKeyword = MITM_PARAMS["url_keyword"]# 如何输出给 Yakit 给用户查看?yakit_output(i: any) // 可以只输出到 "Console 界面"yakit_save(i: any) // 可以输出并保存到数据库中,在 "插件输出" 中查看*/#----------------MITM Hooks Test And Quick Debug-----------------/*# __test__ 是 yakit mitm 插件用于调试的函数 【注意:这个函数在 MITM hooks劫持环境下不会被导入】在这个函数中,你可以使用 yakit.GenerateYakitMITMHooksParams(method: string, url: string, opts ...http.Option) 来方便的生成可供 hooks 调用的参数,参考代码模版中的用法~*/#--------------------------WORKSPACE-----------------------------__test__ = func() { results, err := yakit.GenerateYakitMITMHooksParams("GET", "http://192.168.0.18:8080/admin/") if err != nil { return } isHttps, url, reqRaw, rspRaw, body = results mirrorHTTPFlow(results...) mirrorFilteredHTTPFlow(results...) mirrorNewWebsite(results...) mirrorNewWebsitePath(results...) mirrorNewWebsitePathParams(results...)}# mirrorHTTPFlow 会镜像所有的流量到这里,包括 .js / .css / .jpg 这类一般会被劫持程序过滤的请求mirrorHTTPFlow = func(isHttps /* bool */, url /* string */, req /* []byte */, rsp /* []byte */, body /* []byte */) { }# mirrorFilteredHTTPFlow 劫持到的流量为 MITM 自动过滤出的可能和 "业务" 有关的流量,会自动过滤掉 js / css 等流量mirrorFilteredHTTPFlow = func(isHttps /* bool */, url /* string */, req /* []byte */, rsp /* []byte */, body /* []byte */) { }# mirrorNewWebsite 每新出现一个网站,这个网站的第一个请求,将会在这里被调用!mirrorNewWebsite = func(isHttps /* bool */, url /* string */, req /* []byte */, rsp /* []byte */, body /* []byte */) { }# mirrorNewWebsitePath 每新出现一个网站路径,关于这个网站路径的第一个请求,将会在这里被传入回调mirrorNewWebsitePath = func(isHttps /* bool */, url /* string */, req /* []byte */, rsp /* []byte */, body /* []byte */) { api = "https://hostname/api/v1/log-request/single" key = "" destinationHost,destinationPort,err = str.ParseStringToHostPort(url) die(err) freq, err = fuzz.HTTPRequest(req, fuzz.https(isHttps)) params = freq.GetGetQueryParams() pathList = [] for k, v := range params { pathList = append(pathList, {"name": v.Name(), "value": v.Value()[0]}) } var keys = freq.GetHeaderKeys() headerList = [] for i, k := range keys { value := freq.GetHeader(k) headerList = append(headerList, {"name": k, "value": value}) } parseResp, err = str.ParseBytesToHTTPResponse(rsp) die(err) respHeader = [] for k, v := range parseResp.Header { respHeader = append(respHeader, {"name": k, "value": str.Join(v, ";")}) } rspBpdy,err=str.ExtractBodyFromHTTPResponseRaw(rsp) die(err) postBody = {"request": {"url": {"host": destinationHost, "path": freq.GetPath(), "parameters": pathList}, "headers": headerList, "method": freq.GetMethod(), "body": string(body)}, "response": {"status": parseResp.StatusCode, "headers": [{"name": "content-type", "value": "application/json; charset=utf-8"}], "body": string(rspBpdy)}, "meta": {"environment": "production", "incoming": true, "source": "127.0.0.1", "sourcePort": 17929, "destination": destinationHost, "destinationPort": destinationPort,"metloSource": "proxy"}} rsp, err = http.Post( api, http.header("Content-Type", "application/json; charset=UTF-8"), http.header("Authorization", key), http.json(postBody), ) die(err)}# mirrorNewWebsitePathParams 每新出现一个网站路径且带有一些参数,参数通过常见位置和参数名去重,去重的第一个 HTTPFlow 在这里被调用mirrorNewWebsitePathParams = func(isHttps /* bool */, url /* string */, req /* []byte */, rsp /* []byte */, body /* []byte */) { }# hijackHTTPRequest 每一个新的 HTTPRequest 将会被这个 HOOK 劫持,劫持后通过 forward(modifed) 来把修改后的请求覆盖,如果需要屏蔽该数据包,通过 drop() 来屏蔽# ATTENTION-DEMO:# hijacked = str.ReplaceAll(string(req), "abc", "bcd")# 1. forward(hijacked):确认转发# 2. drop() 丢包# 3. 如果 forward 和 drop 都没有被调用,则使用默认数据流# 4. 如果 drop 和 forward 在一个劫持中都被调用到了,以 drop 为准/*# Demo2 Best In PracticehijackHTTPRequest = func(isHttps, url, req, forward, drop) { if str.Contains(string(req), "/products/plugins/plugin_11") { forward(str.ReplaceAll(string(req), "/products/plugins/plugin_11", "/products/plugins/plugin_create")) } if str.Contains(string(req), "/products/plugins/plugin_12") { drop() } }*/hijackHTTPRequest = func(isHttps, url, req, forward /* func(modifiedRequest []byte) */, drop /* func() */) { }# hijackSaveHTTPFlow 是 Yakit 开放的 MITM 存储过程的 Hook 函数# 这个函数允许用户在 HTTP 数据包存入数据库前进行过滤或者修改,增加字段,染色等# 类似 hijackHTTPRequest# 1. hijackSaveHTTPFlow 也采用了 JS Promise 的回调处理方案,用户可以在这个方法体内进行修改,修改完通过 modify(flow) 来进行保存# 2. 如果用户不想保存数据包,使用 drop() 即可#/**案例:hijackSaveHTTPFlow = func(flow, modify, drop) { if str.Contains(flow.Url, "/admin/") { flow.Red() # 设置颜色 modify(flow) # 保存 }}*/hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop /* func() */) { }
在这里生成一个Key即可。
key和api地址复制到插件中,正常启动即可,不出意外可能会出意外,接收到流量说明平台部署没问题。
以下是测试数据
API统计分析
API管理
漏洞测试
api统计
敏感信息识别结果
这四个功能还在beta阶段
支持自定义敏感信息规则
支持自定义提醒
这个工具如果用于做漏洞扫描的话,规则不丰富,不如yakit,我觉得它的作用主要在于:
可视化、可视化、可视化
API管理(数据持久化存储)
敏感信息识别,支持自定义规则,这点很好
可以作为API安全产品类别一个参考。