首发小米安全中心公众号:『安全开发教学』Github泄露扫描系统开发
概述
github敏感信息泄露一直是企业信息泄露和知识产权泄露的重灾区,安全意识薄弱的同事经常会将公司的代码、各种服务的账户等极度敏感的信息『开源』到github中,github也是黑、白帽子、安全工程师的必争之地,作为甲方的安全工程师,我们需要一套可以定期自动扫描特定的关键字系统,以期第一时间发现猪队友同事泄露出去的敏感信息。
积极响应开源号召的同学请开自己业余的项目,公司的产品代码、各系统账户属于公司的资产,擅自对外界公布侵犯了公司的知识产权,属于违法行为,造成后果严重者,不仅会被公司开除,还需承担相应的法律责任。
接下来我们一起来看看如何写一款github泄露扫描系统。
功能需求
虽然写代码可以一把梭,但一把梭之前需要先把要写的功能清单列一下,我们的github扫描系统会实现以下功能:
- 双引擎搜索,github code接口搜索全局github以及本地搜索例行监控的repos
- 支持对指定的用户、仓库、组织进行监控
- 提供WEB管理界面,支持规则管理(github搜索规则及本地repos搜索规则)
- 支持github token管理和用户管理
- 扫描结果审核
已经完成的项目的地址为:
https://github.com/MiSecurity/x-patrol
实现过程
引擎1 - github code搜索模块的实现
github对API调用的速率限制如下:
- 对未验证的请求,每小时的限速为60次,使用token认证后,可以把速率提升为每小时5000次。
- 对于搜索API,未验证的请求的速率限制为10次每分钟,认证后,可以提高到30次每分钟。
所以在使用github的sdk前,我们需要先准备好token管理模块,方便我们随机获取到额度没用完的token建立client去请求数据,github token的数据结构如下:
每个token初始化时,默认额度为最大值5000,然后在使用的过程中根据返回值动态实时更新remaining的值:
我们在使用github client前,从数据库中先拉取当前额度还大于50的token
然后用这些当前额度够用的Token创建一个map[string]*Client,每次使用时随机获取一个github client,代码如下所示:
在github client建立好后,我们就可以使用关键字进行全局代码搜索了,暂定只取前500条记录,相关的代码片断如下:
在实际使用中,我们的系统中会存在大量的规则需要github code 搜索引擎执行,以下为相应的任务管理代码:
ScheduleTasks(duration time.Duration)
函数是定时任务管理,duration指定了多久进行一次github code 搜索GenerateSearchCodeTask() (map[int][]models.Rules, error)
函数为任务分割函数,因为github search api的速率限制为每分钟30次,我们将其按25个分发几批RunSearchTask(mapRules map[int][]models.Rules, err error)
函数会按批次执行搜索任务,每次执行完都会查看用时,如果小于1分钟就等待到1分钟,以便我们永远不会超出search api的速率限制Search(rules []models.Rules) ()
函数的作用是以并发的方式进行github code搜索,并将搜索结果保存到数据库中。
引擎2 - 本地repos高速搜索模块
我们在使用password等通用的敏感关键字进行github code搜索时,往往会搜索到大量与要监控的目标无关的结果,从里面排查出我们想监控的目标的敏感信息如同大海捞针一般。为了避免这个问题,我们的解决方案是:
- 只关注与目标相关的用户、组织与仓库,比如搜集小米公司员工的用户名,组织,然后通过github sdk查出这些组织与用户所有的仓库,定期拉到本地用更详细的关键字进行深入扫描;
- 利用与目标相关的关键字进行github code搜索,将搜索结果中的库放到引擎2中定期地进行本地深入扫描。
引擎2的高速搜索算法来自一个开源项目https://github.com/etsy/hound,该算法最初来自google的大神Russ Cox 的一篇文章Regular Expression Matching with a Trigram Index or How Google Code Search Worked,有兴趣了解算法的同学可以仔细阅读一下,我直接将该算法封装为了一个SearchRepos函数,传递一条规则和一批仓库进去,会返回该规则的搜索结果。
以下为引擎2的任务调度核心代码:
代码解读:
SegmentationTask(reposConfig []models.RepoConfig) (map[int][]models.RepoConfig)
的作用是将需要扫描的仓库按配置的MAX_Concurrency_REPOS的数量分成批次DistributionTask(tasksMap map[int][]models.RepoConfig, rules []models.Rules)
会将任务按批次分别传给Run(reposConfig []models.RepoConfig, rule models.Rules)
执行Run(reposConfig []models.RepoConfig, rule models.Rules)
的本意是并发执行代码搜索任务,比如有10000个仓库,每次并发100,100次就查完了。想想这个速率就美滋滋。SaveSearchResult(responses map[string]*index.SearchResponse, rule models.Rules, err error)
函数的作用是将搜索结果去重保存到数据库中ScheduleTasks(duration time.Duration)
为定时任务的调度函数,每隔指定的时间后重新获取最新的仓库及规则并进行本地代码搜索。
规则管理
前面我们已经实现了github code搜索与本地repos的深入扫描功能,接下来需要提供一个规则管理模块了,利用规则对引擎1和引擎2进行调度。
为了兼容gitrob
的规则文件,我们把规则的数据结果定义如下,并提供增、改、删、查、禁用、启用等功能
程序启动时,如果发现规则表为空,则会默认插入当前目录中conf/gitrob.json
中规则,代码如下:
我们再提供一个自定义规则管理的WEB界面,以下为规则相关的路由信息,详细实现请直接参考github仓库。
最后的效果图如下,需要注意的是在为github code搜索填写规则时,因为我代码中为了兼容正则,没有直接加精确搜索,需要在配置规则时手工加上双引号表示精确搜索。
资产管理及仓库管理
对于github泄露检测来说,资产就是我们需要监控的用户、组织与仓库,这些信息会最终转化为仓库列表中,供本地检测模块使用。
以下代码为将录入资产列表中的用户、组织的仓库全部查询出来并插入到仓库表中。
仓库管理表中的信息为引擎2的扫描目标,允许修改禁用、启用状态,在结果审核时,忽略的仓库的状态会设为禁用状态,下次扫描时将会忽略。token管理、用户管理、仓库管理、结果审核展示界面的WEB实现的占用篇幅较大就不细说了,详细请参考github中完整的代码,最终的效果如下:
仓库管理:
命令行
到目前为止,我们的github泄露巡航系统的核心功能及WEB管理功能已经一把梭完了,接下来用github.com/urfave/cli
库再给这些功能加上命令行外壳,把WEB启动功能与扫描功能分开。
最终我们的程序的命令行如下:
使用说明
- 配置好conf/app.ini中的参数后启动WEB。
默认会监听到本地的8000端口,默认的管理员账户和密码分别为:xsec
和x@xsec.io
。
- 然后在WEB中录入github token、规则。
- 启动搜索功能:
- scan指令表示只启动github搜索
- scan -m local,表示只启动本地代码搜索功能
- scan -m all,表示同时启动github代码搜索与本地Repos搜索功能
- 审核结果 github code搜索结果审核:
本地repos详细搜索结果审核:
第3种选择
sourcegraph是非常专业的代码搜索服务商,他们提供的Sourcegraph Server是免费的代码搜索服务器,通过docker的方式部署,支持无限扩展,支持对GitHub, BitBucket, GitLab等仓库的代码搜索。搜索内容包括仓库代码、diff、commit。
Sourcegraph Server还提供了GraphQL API,可直接通过API提交代码搜索请求。利用Sourcegraph Server代替引擎2的功能应该会有不错的效果,有兴趣的同学可以尝试一下。