| name | implementing-stix-taxii-feed-integration |
| description | STIX(结构化威胁信息表达式)和 TAXII(可信自动化情报信息交换)是 OASIS 开放标准,用于表示和传输网络威胁情报。 |
| domain | cybersecurity |
| subdomain | threat-intelligence |
| tags | ["threat-intelligence","cti","ioc","mitre-attack","stix","taxii","feed-integration","oasis"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
实现 STIX/TAXII Feed 集成
概述
STIX(结构化威胁信息表达式)和 TAXII(可信自动化情报信息交换)是 OASIS 开放标准,用于表示和传输网络威胁情报。本技能涵盖使用 Python 实现 STIX/TAXII 2.1 Feed 消费者和生产者,配置 TAXII 服务器发现,集合管理,轮询新情报,解析 STIX 2.1 对象,以及将 Feed 集成到 SIEM 和 TIP 平台。
前置条件
- Python 3.9+ 及
taxii2-client、stix2、cti-taxii-client 库
- 理解 STIX 2.1 数据模型(SDO、SCO、SRO)
- 理解 TAXII 2.1 协议(发现、API 根、集合)
- 可访问 TAXII 服务器(MITRE ATT&CK TAXII、Anomali STAXX)
- 可选:medallion 用于运行本地 TAXII 2.1 服务器
核心概念
TAXII 2.1 架构
TAXII 定义了三种服务类型的 RESTful API:
- 发现(Discovery):返回可用 API 根的信息
- API 根(API Root):包含集合并作为主要交互点
- 集合(Collection):通过 GET/POST 访问的 STIX 对象逻辑分组
STIX 2.1 对象模型
STIX 对象分为以下类别:
- SDO(STIX 领域对象):Indicator、Malware、Threat Actor、Campaign、Attack Pattern、Tool、Infrastructure、Vulnerability、Identity、Location、Note、Opinion、Report、Grouping
- SCO(STIX 网络可观测对象):IPv4-Addr、Domain-Name、URL、File、Email-Addr、Process、Network-Traffic、Artifact
- SRO(STIX 关系对象):Relationship、Sighting
- 元对象:标记定义(TLP)、语言内容、扩展定义
STIX Bundle
Bundle 是一组一起传输的 STIX 对象集合。Bundle 具有唯一 ID,包含对象数组。TAXII 集合响应 GET 请求时提供 Bundle。
实践步骤
步骤 1:TAXII 服务器发现
from taxii2client.v21 import Server, Collection, as_pages
server = Server("https://cti-taxii.mitre.org/taxii2/", user="", password="")
print(f"标题:{server.title}")
print(f"描述:{server.description}")
for api_root in server.api_roots:
print(f"\nAPI 根:{api_root.title}")
print(f" URL:{api_root.url}")
for collection in api_root.collections:
print(f" 集合:{collection.title}(ID:{collection.id})")
print(f" 可读:{collection.can_read}")
print(f" 可写:{collection.can_write}")
步骤 2:从集合获取 STIX 对象
from taxii2client.v21 import Collection, as_pages
import json
ENTERPRISE_ATTACK_ID = "95ecc380-afe9-11e4-9b6c-751b66dd541e"
collection = Collection(
f"https://cti-taxii.mitre.org/stix/collections/{ENTERPRISE_ATTACK_ID}/",
user="",
password="",
)
print(f"集合:{collection.title}")
all_objects = []
for envelope in as_pages(collection.get_objects, per_request=50):
objects = envelope.get("objects", [])
all_objects.extend(objects)
print(f" 已获取 {len(objects)} 个对象(总计:{len(all_objects)})")
print(f"\n总共获取对象:{len(all_objects)}")
type_counts = {}
for obj in all_objects:
obj_type = obj.get("type", "unknown")
type_counts[obj_type] = type_counts.get(obj_type, 0) + 1
for obj_type, count in sorted(type_counts.items()):
print(f" {obj_type}: {count}")
步骤 3:使用 stix2 库解析 STIX 2.1 对象
from stix2 import parse, Filter, MemoryStore
store = MemoryStore(stix_data=all_objects)
indicators = store.query([Filter("type", "=", "indicator")])
print(f"指标:{len(indicators)}")
for ind in indicators[:5]:
print(f" {ind.name}: {ind.pattern}")
malware_list = store.query([Filter("type", "=", "malware")])
print(f"\n恶意软件家族:{len(malware_list)}")
actors = store.query([Filter("type", "=", "intrusion-set")])
print(f"威胁行为者:{len(actors)}")
def get_related(store, source_id):
relationships = store.query([
Filter("type", "=", "relationship"),
Filter("source_ref", "=", source_id),
])
return relationships
apt28 = store.query([
Filter("type", "=", "intrusion-set"),
Filter("name", "=", ),
])
apt28:
rels = get_related(store, apt28[].)
rel rels:
target = store.get(rel.target_ref)
target:
()
步骤 4:实现自定义 TAXII 消费者
from taxii2client.v21 import Collection, as_pages
from stix2 import parse, Bundle
from datetime import datetime, timedelta
import json
class TAXIIConsumer:
"""消费 STIX/TAXII 2.1 Feed 并提取 IOC。"""
def __init__(self, collection_url, user="", password=""):
self.collection = Collection(collection_url, user=user, password=password)
self.last_poll = None
def poll_new_objects(self, added_after=None):
"""轮询特定时间戳之后添加的对象。"""
if added_after is None:
added_after = (
self.last_poll or
(datetime.utcnow() - timedelta(days=1)).strftime(
"%Y-%m-%dT%H:%M:%S.000Z"
)
)
all_objects = []
kwargs = {"added_after": added_after}
for envelope in as_pages(
self.collection.get_objects, per_request=100, **kwargs
):
objects = envelope.get("objects", [])
all_objects.extend(objects)
self.last_poll = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S.000Z")
return all_objects
def extract_indicators(self, objects):
indicators = []
obj objects:
obj.get() == :
indicators.append({
: obj.get(),
: obj.get(, ),
: obj.get(, ),
: obj.get(, ),
: obj.get(, ),
: obj.get(, ),
: obj.get(, []),
: obj.get(, ),
: obj.get(, []),
})
indicators
():
observables = []
observable_types = {
, , , ,
, , ,
}
obj objects:
obj.get() observable_types:
observables.append({
: obj[],
: obj.get(, ),
: obj.get(),
})
observables
consumer = TAXIIConsumer(
)
new_objects = consumer.poll_new_objects()
indicators = consumer.extract_indicators(new_objects)
()
步骤 5:使用 Medallion 设置本地 TAXII 服务器
TAXII_CONFIG = {
"backend": {
"module_class": "MemoryBackend",
},
"users": {
"admin": "admin_password",
"readonly": "readonly_password",
},
"taxii": {
"max_content_length": 10485760,
},
}
import requests
def push_to_taxii(server_url, collection_id, stix_bundle, user, password):
"""将 STIX Bundle 推送到 TAXII 2.1 集合。"""
url = f"{server_url}/collections/{collection_id}/objects/"
headers = {
"Content-Type": "application/stix+json;version=2.1",
"Accept": "application/taxii+json;version=2.1",
}
response = requests.post(
url,
json=stix_bundle,
headers=headers,
auth=(user, password),
timeout=30,
)
return response.json()
验证标准
- TAXII 服务器发现返回有效的 API 根和集合
- STIX 对象从 TAXII 集合正确获取和解析
- 指标提取包含有效的 STIX 模式
- 正确处理大型集合的分页
- 消费者跟踪轮询状态以进行增量更新
- 本地 TAXII 服务器接受并提供 STIX Bundle
参考资料