背景
平常工做中会大量应用正则表达式,用正则表达式去定义规则,而后去匹配数据。这里先看两个安全场景下的正则应用需求正则表达式
场景1,FTP帐号被成功暴力破解后数据遭窃取缓存
• 数据源:FTP服务器日志安全
• 关联逻辑:针对特定帐号暴力破解,而后利用该特定帐号登陆成功,以后利用该特定帐号下载大量文件服务器
• 告警内容:FTP帐号${user_name}被成功暴力破解后窃取数据网络
• 告警级别:高危框架
场景1中,正则表达式用于在日志中匹配屡次帐户登陆的行为上。分布式
场景2,Deep packet inspection (DPI) ,例如过滤网络威胁和违反安全策略的流量等函数
• 数据源:网络数据包工具
• 检测规则条件:数据命中规则集性能
场景2中,正则表达式用于时间序列上的多个数据包之间的安全检测。
其实,场景1中只列举了FTP被攻击的一种方式,FTP攻击还有不少其余手段,因此检测FTP被攻击的正则匹配场景的另外一个特征就是整个规则集可能很大;场景2中,利用已知的入侵行为构建模式集合,经过检测网络数据包,发现是否存在不符合安全策略的行为或被攻击的迹象,这须要对数据包的载荷部分进行检测,要求匹配的速度很是快,不然将会影响用户体验。
另外一方面,这里用到的正则与传统用法又不太同样,对正则的传统用法是,给定一个文本,用一个或少数几个正则规则,去匹配文本,找出文本中匹配的数据。而如今面对的问题,首先是规则的数量大,上千上万或者超过十万的规则集,若是仍然采用以前的作法,用|分割,或者外层用循环去匹配,那么处理的时间将很长,对资源的消耗也很大,基本不可接受;其次在匹配的时候,待匹配的数据不是一个完整的总体,好比说网络数据包,是一个一个接收的,这是一个流式的形式,传统的正则处理引擎不能很好的处理流式数据,须要缓存一批数据去匹配,这样匹配就不够及时,并且目前正则处理有个很大的问题,若是正则表达式写的很差,那么匹配会很慢。因此,须要一个解决方案来应对如下这些挑战:
• 规则数量多
• 匹配速度要快
• 支持流式数据
• 资源消耗不能太大
Hyperscan算子介绍
针对上述正则匹配中遇到的挑战,通过调研和对比测试市面上的主流正则匹配引擎,咱们最终选择了Hyperscan。
Hyperscan是Intel开源的高性能正则表达式匹配库,提供了C语言API,目前已经在不少商业项目和开源项目中获得应用。
Hyperscan具有这些特性:
• 支持大部分PCRE正则语法(若是使用Chimera库,那将支持全部语法)
• 支持流式匹配
• 支持多模匹配
• 采用特定指令集加速匹配
• 易于扩展
• 内部多种引擎结合
Hyperscan在设计之初就是为了更好的处理流式匹配和多模匹配,对流模式的支持极大的方便了正则用户,再也不须要用户去维护接收到的数据,无需缓存数据;多模匹配容许把多个正则表达式传入并在同一时间进行匹配。
由于须要特定的指令集,因此Hyperscan对CPU有要求,以下图:
CPU最低要支持SSSE3指令集,最下面一行的指令集能加速匹配
和大多数正则引擎相似,Hyperscan也包括编译和匹配阶段,编译是把正则表达式解析而后构建成内部须要的database,后续能够屡次使用这个database去匹配;若是是多模匹配,编译时每一个正则表达式须要有一个惟一的标识id,id在匹配的时候会用到。编译过程以下图所示:
匹配的时候Hyperscan默认会返回全部命中的结果,这点不像有些正则引擎,指定贪婪的时候返回贪婪的匹配结果,指定懒惰的时候返回懒惰的结果。匹配时若是有命中,那么会以回调函数的形式通知用户在哪一个位置命中了哪一个正则表达式id。匹配过程以下图所示:
Hyperscan的缺点是只能是单机执行,没有分布式能力,其能够解决延迟的问题,但没法解决吞吐的问题,解决吞吐问题,能够依靠主流实时计算框架Flink。Flink 是一个在无界和有界数据流上进行状态计算的框架和分布式处理引擎。无界就是有开始但没有结束的数据,无界的数据流计算即流式计算,有界就是有开始有结束的数据,有界的数据流计算即批处理。
Flink能够用于不少的计算场景中,这里列举了3个,Flink能够处理事件驱动的程序,除了简单事件,Flink还提供了CEP库能够处理复琐事件;Flink还能够做为数据管道,作一些数据清洗筛选、转换等操做,把数据从一个存储系统转移到另外一个系统;Flink能够作流或批式数据的分析、指标计算,用于大屏展现等。Flink已经成为业界公认的流式处理的第一选择。
把正则匹配引擎整合到Flink中,借助Flink强大的分布式能力,强强联合,那么将会发挥更大威力。因此提供了这样的解决方案,以下图所示:
该解决方案实现了一个自定义的UDF算子,算子支持指定只匹配输入数据中的某几个字段,算子的输出是待匹配的字段文本,匹配最终状态,包括命中,不命中,错误,超时四种状态,若是是命中的状态,那么还会返回匹配中的正则表达式的id,输出还包括输入原始数据,若是有后续处理,这样不受影响;为了进一步方便用户使用,扩展了一个新的datastream,称之为Hyperscanstream,它把算子封装进了进去,用户在使用时只须要把datastream转换为Hyperscanstream,而后经过调用一个方法就可使用正则的算子了。整个解决方案以一个独立的jar包提供给用户,这样能够保持原来编写Flink做业的习惯,而且与Flink的核心框架解耦。
数据流转的过程是这样,数据源读取到一条记录后交给下游的Hyperscan算子,Hyperscan算子把数据交给Hyperscan子进程,子进程匹配完成后把结果返回给Hyperscan算子,而后Hyperscan算子把原始记录和匹配的结果传递给后续算子。
算子使用说明
私有化部署
针对私有化部署场景,用法以下,用户首先须要去编辑正则表达式文件,而后用工具把正则表达式编译为database而且序列化为本地文件,若是部署的环境中有HDFS,那么能够把序列化后的文件上传至HDFS,若是没有那就不用上传,而后开发Flink做业,引用序列化的文件去匹配数据。
为何要有工具编译并序列化这一步呢,编辑完正则表达式,直接在Flink做业中使用不行吗?前面说了,Hyperscan执行包括编译和匹配阶段,若是做业中只引用正则表达式,假设做业设置了并行度为5,那么每一个task都须要编译一次,一共须要编译5次,浪费资源;并且编译在hyperscan中是个相对缓慢的动做,因此把编译过程单独出来也为了加速flink做业在尽快执行。编译提早进行也有利于提早知道正则表达式是否有语法错误,或者不支持的状况,而不是做业启动后才知道。
私有化部署时,hyperscan相关依赖程序会提供给用户,依赖程序经过全静态编译而来因此无需再添加依赖,只需机器支持须要的指令集便可。
公司内部使用
使用示例
假设如今要匹配的是HTTP报文中的Host字段和Referer字段,以下图所示:
代码示例以下图:
整个逻辑分为四步,第一步要从数据源构建输入流,第二步把输入流转换为Hyperscanstream,第三步调用hyperscan方法进而使用Hyperscan算子,在第一个参数HyperscanFunction中指定要匹配的是Host和Referer字段,第四步使用匹配返回的结果,返回的结果是Tuple2对象,其中第一个字段Event是原始记录,在本例中就是整个HTTP报文,第二个字段是HyperScanRecord组成的List,HyperScanRecord类中包括匹配的字段,例如本例中Host或Referer,匹配命中的正则表达式id(若是匹配命中的话)和匹配的最终状态。
使用1万条规则集以及不一样大小的待匹配样本通过测试,方案达到了指望的性能,测试结果以下图,
使用Hyperscan算子的一些建议,以下图:
前面提到,在不使用himera库时,Hyperscan有部分PCRE语法不支持的状况,在使用时要注意,下图列举了不支持的语法(使用Chimera库将会影响匹配性能)
将来展望
一方面,目前Hyperscan算子在安全、威胁感知的场景中已经获得运用,但但愿能在更多场景中进行检验,理论上在全部正则匹配的场景中都能使用,比方说文本审核、内容提取等。
另外一方面,也在提高Hyperscan算子易用性,比方说如今的规则发生变化时,须要重启做业才能生效,后续但愿能作到规则的动态热加载。