fpoirotte / idmef
PHP 的 IDMEF 库
Requires
- php: >=7.1
- ext-dom: *
- ext-filter: *
Requires (Dev)
- phpunit/phpunit: >=7.0
- squizlabs/php_codesniffer: *
Suggests
- ext-XMLReader: Provides support for XML unserialization
- ext-XMLWriter: Provides support for XML serialization
- ext-ffi: Provides support for sending IDMEF alerts to Prelude SIEM
This package is auto-updated.
Last update: 2024-09-14 03:06:16 UTC
README
简介
此仓库包含一个 PHP 库,实现了在 RFC 4765 中定义的入侵检测消息交换格式 (IDMEF)。
它支持 RFC 中定义的所有类和属性,支持 XML 序列化和反序列化,还包括将使用 IDMEF 生成的警报发送到 Prelude SIEM 的代码。
先决条件
对于基本使用,您只需要以下依赖项
- Composer 依赖管理器 Composer
- PHP >= 7.4
- PHP
DOM
扩展 - PHP
Filter
扩展
附加功能需要额外的依赖项。要使用 XML 序列化和反序列化,您还需要
- PHP
XMLReader
扩展 - PHP
XMLWriter
扩展
要向 Prelude SIEM 发送警报,您需要
- PHP
FFI
扩展
安装
使用 Composer 将库添加到项目的需求中
$ php /path/to/composer.phar require fpoirotte/idmef
使用方法
IDMEF 消息
关于 IDMEF 路径的一些说明
为了使处理 IDMEF 消息更简单,此库通过 getIterator()
方法实现了与 Prelude SIEM 相同的“IDMEF 路径”概念。(稍后详述)
该库还支持使用获取器和设置器直接访问属性。(更多信息请参阅下文)
通过 getName()
方法返回属性路径时,该库始终使用 IDMEF RFC 中定义的官方类/属性名称,例如 Alert.CorrelationAlert.name
。
但是,在设置 IDMEF 消息的属性时,您还可以使用 Prelude SIEM 的 IDMEF 路径,例如 alert.correlation_alert.name
。可以在 SECEF 网站 上找到 Prelude SIEM 和此库都识别的有效路径列表。
在使用获取器和设置器时,采取类似的方法,但有一些注意事项
PHP 的对象操作符(
->
)用于分隔 IDMEF 路径的各个部分,而不是点(.
)。因此,要获取 IDMEF 对象内部
Alert.CorrelationAlert.name
路径的值,请使用$name = $alert->CorrelationAlert->name
,Prelude 的路径名称($alert->correlation_alert->name
)也受支持。PHP 的数组操作符(
[]
)用于访问列表内部的条目,而不是 Prelude 的列表访问操作符(()
)。与 Prelude SIEM 一样,此库也支持负列表索引。因此,要使用获取器获取最后一个源节点的名称,可以使用以下调用:
$name = $alert->source[-1]->node->name;
。与 Prelude 的路径相比:alert.source(-1).node.name
。Prelude SIEM 的前缀(
<<
)和后缀(>>
)操作符也可使用。因此,以下调用将向警报追加一个新的源节点并为其命名:$alert->source['>>']->node->name = "foo";
。PHP 的数组操作符也可用于将新条目追加到列表中。因此,调用
$alert->source[]->node->name = "foo";
与调用$alert->source['>>']->node->name = "foo";
功能上相同。
与 Prelude SIEM 一样,此库从 0 开始索引 IDMEF 列表。因此,$alert->source[0]
指的是警报中的第一个源。
最后但同样重要的是,这个库的路径实现与Prelude SIEM的路径之间存在一个明显的区别,涉及到Analyzer
类。RFC声明分析器可以通过递归定义(Alert.Analyzer
、Alert.Analyzer.Analyzer
...)进行链式操作。为了使处理链式分析器更方便,Prelude SIEM将它们表示为一个列表(alert.analyzer(0)
、alert.analyzer(1)
、...)。为了尽可能接近IDMEF RFC,这个库使用递归方法来表示链式分析器。但是,API的一些部分也可能实现Prelude SIEM的这些表示方法,因此效果可能会有所不同。
数据类型
该库在可能的情况下自动将值转换为它们的预期类型。它还将PHP类型自动转换为IDMEF对应类型。
因此,可以将表示整数的字符串值传递给期望IDMEF整数的属性
// Import a few symbols use \fpoirotte\IDMEF\Types\IntegerType; use \fpoirotte\IDMEF\Types\StringType; // The following statements are okay: $alert->OverflowAlert->size = new IntegerType(42); // IDMEF integer object $alert->OverflowAlert->size = 42; // PHP integer $alert->OverflowAlert->size = '42'; // IDMEF integer value $alert->OverflowAlert->size = '0x2A'; // IDMEF (hexadecimal) integer value // The following statements will throw an exception: $alert->OverflowAlert->size = new StringType('42'); // The "size" attribute is an integer, not a string $alert->OverflowAlert->size = 42.0; // A floating-point value is not an integer either $alert->OverflowAlert->size = ''; // Invalid integer (value is missing) $alert->OverflowAlert->size = '0x'; // Invalid integer (hexadecimal number missing a value) $alert->OverflowAlert->size = '2A'; // Invalid integer (possibly an hexadecimal number missing the prefix, // or trailing data after the intended number)
然而,这仅当预期类型已知时才成立。对于可能不知道(例如附加数据)的情况,该库也将尝试自动转换类型,但您可能需要显式设置类型。
以下表格显示了原生PHP类型在转换为IDMEF对应类型后的情况。
以下类型在使用附加数据时必须手动管理
\fpoirotte\IDMEF\Types\ByteType
\fpoirotte\IDMEF\Types\ByteStringType
\fpoirotte\IDMEF\Types\CharacterType
\fpoirotte\IDMEF\Types\NtpstampType
\fpoirotte\IDMEF\Types\PortlistType
IDMEF消息操作
以下示例展示了如何创建一个警报,设置其一些属性,然后对其进行操作。
<?php // Include Composer's autoloader require '.' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; // Import a few symbols from the library use \fpoirotte\IDMEF\Classes\Alert; use \fpoirotte\IDMEF\Types\AbstractType; // Create the alert $alert = new Alert; // Set mandatory attributes $alert->analyzer->analyzerid = 'hq-dmz-analyzer01'; $alert->analyzer->node->category = 'dns'; $alert->analyzer->node->location = 'Headquarters DMZ Network'; $alert->analyzer->node->name = 'analyzer01.example.com'; $alert->create_time->ntpstamp = '0xbc722ebe.0x00000000'; // Set some optional attributes and provide additional data $alert->classification->text = "Houston, we've had a problem here"; $alert->additional_data[ ]->type = 'string'; $alert->additional_data[-1]->meaning = 'mission'; $alert->additional_data[-1]->data = 'Apollo 13'; $alert->additional_data[ ]->type = 'string'; $alert->additional_data[-1]->meaning = 'speaker'; $alert->additional_data[-1]->data = 'Jack Swigert'; // Display the alert's classification: echo $alert->classification->text . PHP_EOL; // Iterate over additional data and display each entry's meaning and data: foreach ($alert->additional_data as $ad) { echo $ad->meaning . ': ' . $ad->data . PHP_EOL; } // Same thing, but this time we use an explicit iterator and IDMEF paths: foreach ($alert->getIterator('alert.additional_data') as $ad) { echo $ad->meaning . ': ' . $ad->data . PHP_EOL; } // Dump the alert's contents, by iterating over instances // of the AbstractType class (the base class for all leaf nodes) foreach ($alert->getIterator('{' . AbstractType::class . '}', null, 0, -1) as $path => $node) { echo $path . ' => ' . $node . PHP_EOL; } // Look for nodes with a specific value: foreach ($alert->getIterator(null, 'Apollo 13', 0, -1) as $path => $node) { echo $path . PHP_EOL; // displays "Alert.AdditionalData(0).data" } // The 3rd ($minDepth) and 4th ($maxDepth) parameter to getIterator() // can be used to restrict iteration to nodes at a certain depth, // starting at 0 for the root object. // The following example will only dump the analyzer node's attribute // due to the restrictions. // Eg. path: Alert.Analyzer.Node.Name // depth: (0) (1) (2) (3) foreach ($alert->getIterator(null, null, 3, -1) as $path => $node) { echo $path . PHP_EOL; // displays "Alert.Analyzer.Node.category", // "Alert.Analyzer.Node.location" // and "Alert.Analyzer.Node.name" }
心跳消息和更专业的警报消息(CorrelationAlert、ToolAlert和OverflowAlert)遵循相同的模式。
XML(反)序列化
在将IDMEF消息序列化为XML时,必须创建一个特殊容器。
假设警报和心跳已经被创建并分别存储在变量$alert
和$heartbeat
中,以下示例可以用来将它们序列化为XML IDMEF消息
<?php // Import the container and the serializer use \fpoirotte\IDMEF\Classes\IDMEFMessage; use \fpoirotte\IDMEF\Serializers\Xml; // Create an instance of the container and add the messages to it $idmef = new IDMEFMessage; $idmef[] = $alert; $idmef[] = $heartbeat; // Create an instance of the serialization class and produce the output $serializer = new Xml; echo $serializer->serialize($idmef) . PHP_EOL;
同样,反序列化返回一个IDMEFMessage
容器。假设$xml
指向一个有效的XML IDMEF消息,其中包含一个警报和一个心跳(按此顺序),以下代码可以用来反序列化它们
<?php // Import the (un)serializer use \fpoirotte\IDMEF\Serializers\Xml; // Create an instance of the serialization class // and unserialize the message $serializer = new Xml; $idmef = $serializer->unserialize($xml); // The unserialization process maintains the objects' order $alert = $idmef[0]; $heartbeat = $idmef[1];
Prelude SIEM
要将IDMEF消息发送到Prelude SIEM,您必须首先为库注册一个具有idmef:w
权限的配置文件。
在prelude-manager
所在的机器上运行以下命令
sudo prelude-admin registration-server prelude-manager
并行,在将使用库的机器上运行以下命令
# Replace "php" with a custom name for the newly-created profile. # # Replace "localhost" with the hostname where prelude-manager is installed. # # Replace "clicky" & "users" respectively with the names of the user and group # that will execute the PHP script. # sudo prelude-admin register php idmef:w localhost --uid clicky --gid users
然后,按照两个命令打印的说明操作。
配置文件成功注册后,您可以使用以下代码将IDMEF消息发送到Prelude SIEM
<?php // Replace this value with your registered profile's name $profile = 'php'; // Create a new Prelude agent using that profile $agent = \fpoirotte\IDMEF\PreludeAgent::create($profile); // Send various alerts/heartbeats $agent->send($alert); $agent->send($correlation_alert); $agent->send($heartbeat); // and so on
注意
代理将自动以定期间隔(由代理配置文件定义)向prelude-manager
发送心跳消息。因此,不需要手动发送它们。
同样,代理将在不再使用时自动(干净地)关闭,作为PHP垃圾回收过程的一部分。您也可以使用以下代码片段手动强制关闭
<?php unset($agent); gc_collect_cycles();
许可证
本库采用GNU公共许可证版本2授权。请参阅仓库内的COPYING
文件以获取更多信息。