实现 : rails5、 ruby2.3.1、golang 1.14.2、elasticsearch6.2.4、mysql、redis、hadoop、sidekiq、kafka、bootstrap3、jquery、nginx(web服务器)、puma(应用服务器)
项目介绍: 这是我自工作以来参与过最重要、最有荣誉感的产品,产品名称叫FOFA(佛法),老对手shodan.io的意思是撒旦,所以我们取了FOFA这个名字。
FOFA是一个互联网资产搜索引擎,什么是互联网资产呢?只要是网络上有IP地址的就是互联网资产,例如连接网络的打印机、摄像头、网站等。
FOFA主要应用于安全行业,可以快速协助安全人员或国家重要单位找到网络的资产,例如现在爆发了一个漏洞,影响某款型号的路由器,那么可以通过FOFA快速找到网络上的该款路由器所有资产。
由于FOFA在安全行业的重要作用,所以除了针对个人用户提供服务外,更多的是针对国家大型企事业单位提供服务,基于FOFA定制产品。
FOFA的核心功能就是物联网资产的采集和检索,目前FOFA有25亿资产数据,一共15T。
项目架构:
FOFA主要分前后端两块,后端负责资产的识别和采集,前端负责对采集数据的检索、FOFA语法转换等基本功能。
1) 后端介绍:后端之前用ruby实现,后来改为golang实现,该模块主要负责资产的识别和采集,分为portscan(扫描)、grab(协议识别)、checkurl(检测url是否可以爬取)、crawler(爬虫)、store(入库)。
各模块之间通过kafka任务的消费来关联,如下:
portscan负责扫描ip段内开放的端口,扫描过程中会不断下发开放的ip端口到kafka
grab通过kafka获取到portscan下发的ip和端口,然后通过协议识别程序对端口进行协议识别。将识别的协议下发到kafka,这里分两种情况,如果协议识别的结果是http和https协议,那么同时会下发kafka通知checkurl和store去消费,如果是其他协议则只下发store去存储。
checkurl去检测该url是否最近爬取过,如果最近爬取过则忽略,如果没有,则下发任务到kafka,由下一步crawler来消费。
cralwer会消费checkurl推送的任务,对网址进行爬取,获取网址的首页、header等内容,然后下发kafka通知store入库。
store获取到grab协议识别和crawler爬虫的结果,然后再通过ip地理位置库对ip进行解析,将这些内容存储到elasticsearch中。
设计优势:各模块之间相互独立,每个模块只负责一件事,只需要定义自己的输入和输出就可以,降低了耦合性。这样的设计也有助于扩展集群,例如项目的端口扫描推送的任务多,协议识别程序处理不过来,那么就可以再加新的服务器启动新的协议识别程序来消费kafka的任务。
2) 前端介绍:
这里的前端不是web开发里的css、js这样的前端,是相对前面提到的后端来说的,这里包括如下几部分:
1> 资产搜索及展示:
语法搜索:
初级搜索: 为了用户搜索方便,系统提供了语法搜索,语法包括搜索字段、搜索内容、符号三部分组成,这样设计的优势是"使用简单"以及"通过符号可以组合成复杂的搜索语句"。这些搜索语法最终会转换为elasticsearch的搜索语法。举个简单的例子,假如要查找所有使用了bootstrap的产品,经过观察发现所有使用bootstrap的网站页面中都带有bootstrap.js或者bootstrap.min.js,那么就可以这样搜索body="bootstrap.js" || body="bootstrap.min.js",表示搜索所有页面中包含bootstrap.js或者bootstrap.min.js的资产,这样就可以搜索到所有使用了bootstrap框架的资产。
搜索进阶:上面的事例还有更简单的搜索方式,系统会把某个产品的搜索语法存储起来,并且起一个名称,上面事例中的名称是Bootstrap,所以可以这样搜索app="Bootstrap",系统会在数据库中查找名称为Bootstrap的数据,然后在找到它的具体语法,最后转换为elasticsearch的语法。
结果展示:
展示包括三部分、具体资产的列表展示、聚合展示、图形展示(饼图、柱状图、地图等)。展示的数据获取都是通过elasticsearch的语法实现。
2> 单点登录系统:
由于我们的项目比较多,所以我们采用了单点登录系统的方式实现,这样可以使多个项目都使用一套登录系统,单点登录通过devise和casino等插件实现,这里不详述了
3> 个人中心:
个人中心包括规则管理、下载记录、提现等功能,这里简单描述下载功能,用户搜索到相关资产后,可以把这些资产下载下来,下载功能时首先需要获取数据,这是通过elasticsearch的scroll搜索实现的,下载过程中会根据已获取的数据量计算进度,整个下载任务通过sidekiq的队列任务进行。
4> api
系统系统了很多api,使用grape实现api的框架,根据api的功能、面对客户不同使用了不同的权限校验方式,包括以下几种:
1)通过用户名和key、timestamp生成的token来校验
2) api请求有效时长,例如校验timestamp是否在有效期内
3) 通过ip白名单校验
此外api通过路径中的v1、v2来判断api版本。
5> 管理员后台:
这里包含了很多功能,但是不详述了。
职责:
1)FOFA项目的代码重构: 这是我刚去公司进行的,我首先按照业务的不同通过namespace分为了admin(管理员)、user(用户中心)、client(客户端)等几个命名空间,然后去掉并且修改了项目的大量代码。为了确保代码重构的顺利进行,我首先了解业务场景,然后根据场景通过rspec和capybara写了大量的测试用例,最终测试覆盖度达到了72%以上。
2)FOFA功能开发和维护: 这是我的日常工作,这里简单介绍几个我开发的重要功能。
ip聚合功能:该功能是以ip为单位,展示ip下开放的端口及其协议、组件 ,通过elasticsearch搜索ip的所有资产数据就可以获取所有展示的数据。ip聚合功能的亮点是防止爬虫爬取组件信息,组件信息通过json请求获取,请求时必须携带请求页面是分配的key,而且key是有时效性的。 该功能示例
端口联合搜索:FOFA存储的数据是以ip和端口为单位的,但是某些情况下我们也需要基于ip来搜索,例如 搜索同时开放 80、443、22端口的ip,通过ip和端口的基础数据无法实现。所以我新增了一个elasticsearch索引,这个索引存储的数据以ip为单位,ip索引字段里新增ports,ports为nested,在这个基础上,然后再实现搜索语法的转换。例如打开fofa.so搜索ports="80,443"可以搜索到开启80、443端口的ip。
自动生成组件: 组件是什么?组件就是产品的搜索语法,前面提到的app="组件名称"就是通过组件来搜索的,有很多产品需要各种各样的搜索语句来搜索,我们把这些记录下来,并且取个名称,这就是组件。这是我们产品竞争力的核心部分。创建组件分为两种方式,手动录入、自动生成,我这里实现的就是自动生成。例如通过观察发现华为某个系列的产品都有个特点就是页面的js代码中包含var product=产品名称。那么此时我们就可以获取所有页面中包含var product=...的资产,然后依次通过正则来解析名称,最后自动生成这些产品的规则。这个功能是后台数据人员使用的功能,主要通过elasticsearch搜索、正则、sidekiq队列任务实现。
搜索优化:随着搜索并发量和ES资产数据的增长,搜索速度越来越慢,在19年底的时候开始了搜索的优化。优化方式如下:
1> 分析业务场景,按照业务场景切分索引
2>增加elasticsearch集群,重新分片,并且每个服务器启动两个实例
3>分析日志,统计各个搜索引擎普通搜索和聚合搜索的次数,然后禁止搜索引擎爬取聚合搜索结果(聚合搜索占用资源多),并且通过robot通知搜索引擎爬取的时间
4>由于聚合搜索占用资源多,所以所有的聚合搜索引导到备份的Elasticsearch上搜索
5>搜索语法优化
整个优化过程坚持通过数据论证的方式进行,每一步的优化效果都要分析优化前、优化后支持的并发量,这是我在搜索优化中学到的最重要的方式。
3)负责多个基于FOFA定制项目的开发,由于这些项目都是涉密项目,所以我就不详述了。通过这几个项目我积累了写标书、竞标、和客户沟通需求、交付项目的经验,获得了不少提升。