50万奖金+官方证书,深圳国际金融科技大赛正式启动,点击报名 了解详情
写点什么

如何提高大规模正则匹配的效能

  • 2021-03-10
  • 本文字数:3283 字

    阅读完需:约 11 分钟

如何提高大规模正则匹配的效能

背景

日常工作中会大量应用正则表达式,用正则表达式去定义规则,然后去匹配数据。这里先看两个安全场景下的正则应用需求:


场景 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 相关依赖程序会提供给用户,依赖程序通过全静态编译而来所以无需再添加依赖,只需机器支持需要的指令集即可。

公司内部使用



公司内部使用相对简单,用户可以在奇麟平台编辑正则表达式,或者把编写好的正则表达式文件上传到奇麟平台,奇麟平台负责把正则表达式编译为 database 上传到 HDFS,然后开发作业进行匹配。

使用示例

假设现在要匹配的是 HTTP 报文中的 Host 字段和 Referer 字段,如下图所示:



代码示例如下图:



整个逻辑分为四步,第一步要从数据源构建输入流,第二步把输入流转换为 Hyperscanstream,第三步调用 hyperscan 方法进而使用 Hyperscan 算子,在第一个参数 HyperscanFunction 中指定要匹配的是 Host 和 Referer 字段,第四步使用匹配返回的结果,返回的结果是 Tuple2 对象,其中第一个字段 Event 是原始记录,在本例中就是整个 HTTP 报文,第二个字段是 HyperScanRecord 组成的 List,HyperScanRecord 类中包括匹配的字段,例如本例中 Host 或 Referer,匹配命中的正则表达式 id(如果匹配命中的话)和匹配的最终状态。


使用 1 万条规则集以及不同大小的待匹配样本经过测试,方案达到了期望的性能,测试结果如下图,



使用 Hyperscan 算子的一些建议,如下图:



前面提到,在不使用 Chimera 库时,Hyperscan 有部分 PCRE 语法不支持的情况,在使用时要注意,下图列举了不支持的语法(使用 Chimera 库将会影响匹配性能)


未来展望

一方面,目前 Hyperscan 算子在安全、威胁感知的场景中已经得到运用,但希望能在更多场景中进行检验,理论上在所有正则匹配的场景中都能使用,比方说文本审核、内容提取等。

另一方面,也在提升 Hyperscan 算子易用性,比方说现在的规则发生变化时,需要重启作业才能生效,后续希望能做到规则的动态热加载。


本文转载自:360 技术(ID:qihoo_tech)

原文链接:如何提高大规模正则匹配的效能

2021-03-10 07:003484

评论

发布
暂无评论
发现更多内容

阿里面试官内部题库,阿里发布2022年Java岗(正式版)面试题

程序知音

Java java面试 后端技术 秋招 Java面试八股文

数据治理的核心:维度建模下的数仓构建

小鲸数据

数据仓库 维度建模 维度 数仓分层 分层划域

编译器优化那些事儿(6):别名分析概述

openEuler

开源 编译器 openEuler 毕昇 JDK

如何在笔记本上安装openEuler 22.03 LTS

openEuler

开源 操作系统 openEuler

Java高手怎样炼成?阿里大牛一份火爆GitHub的1046页笔记帮你解决

钟奕礼

Java 程序员 架构 后端 java面试

面试凉凉,阿里学长甩我一份24w字Java核心技术面试手册,真香

钟奕礼

Java 架构 后端 java面试

开源实习 | 毕昇JDK发布国密算法实习任务

openEuler

开源 openEuler 毕昇 JDK

StratoVirt 中的 PCI 设备热插拔实现

openEuler

开源 操作系统 虚拟机 openEuler

小程序容器,组装式应用的一种方案

Geek_99967b

小程序

八家知名大厂联合手写的Java面试手册刚上线!竟就到达巅峰?

钟奕礼

Java 架构 后端 java面试

GitHub获百万推荐的面试涨薪秘籍(Java岗)惨遭封杀?

钟奕礼

Java 后端 java面试 后端架构

开源之夏 | 【结项报告】毕昇Fortran编译器内联动态库函数str_copy

openEuler

开源 操作系统 openEuler 毕昇 JDK

软件测试 | 测试开发 | 测试面经 | 从测试螺丝钉到大厂测试开发,三点成长心得和面试经验

测吧(北京)科技有限公司

测试

测试开发面试真题 | 测试老兵进阶突破,成功挑战大厂 P7 Offer!

测吧(北京)科技有限公司

测试

公司内部分享文档应该怎么写?看这篇就够了

Baklib

阿里被转载上100W次的Java面试题教程!已助我拿下9家大厂offer!

钟奕礼

Java 架构 后端 java面试

面试突击87:说一下 Spring 事务传播机制?

王磊

Java 面试

22年程序员更卷了,金九银十“面试必备小册”最新开源

程序知音

Java 阿里 后端技术 秋招 Java面试题

揭开HPC应用的神秘面纱

openEuler

开源 openEuler

小程序怎样影响传媒产业的数字化

Geek_99967b

小程序

概述服务网格的优劣势

穿过生命散发芬芳

服务网格 9月月更

2021 金三银四面试必备?体系化带你学习:分布式进阶技术手册

钟奕礼

Java 架构 后端 java面试

iMazing怎么恢复备份?iMazing恢复备份教程分享

淋雨

ios iphone

一次 Rancher 和 openEuler 的上云之旅

openEuler

Linux 开源 openEuler rancher suse

openEuler 资源利用率提升之道 04:CPU 抢占和 SMT 隔离控制

openEuler

开源 openEuler

从融云社交泛娱乐出海白皮书,看「社交+X」的全球攻略

融云 RongCloud

即时通讯 白皮书 泛娱乐社交

BATJ互联网月薪38K的Java岗面试题首曝光,掌握这些大厂Offer指定跑不了

程序知音

Java java面试 后端技术 秋招 Java面试八股文

别让你的 SaaS 产品由赋能变为“负能”

产品海豚湾

产品设计 产品运营 SaaS平台 B端产品 9月月更

从规模化平台工程实践,我们学到了什么?

SOFAStack

iMazing高效便捷的数据转移功能

淋雨

ios iphone

Embedded SIG | 树莓派的UEFI支持和网络启动

openEuler

开源 树莓派 操作系统 openEuler

如何提高大规模正则匹配的效能_文化 & 方法_360技术_InfoQ精选文章