fpoirotte/idmef

PHP 的 IDMEF 库

v1.0.0 2020-05-05 07:30 UTC

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.AnalyzerAlert.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文件以获取更多信息。