bayufedra/ipti

GitHub: bayufedra/ipti

一款多源 IP 威胁情报评估工具,在将公网 IP 加入白名单前提供基于证据的可解释威胁评分与决策建议。

Stars: 35 | Forks: 4

# IPTI — IP 威胁情报 [![Black Hat Arsenal MEA 2025](https://img.shields.io/badge/Black%20Hat%20Arsenal-MEA%202025-white.svg)](https://blackhatmea.com/speaker/bayu-fedra-abdullah) **IPTI** 在考虑将公共 IP 地址列入组织内部网络的白名单**之前**,通过**基于证据、可解释**的评估对其进行分析:包括威胁评分、证据覆盖率指标、身份置信度指标以及暴露风险指标。 ## 目录 1. [功能介绍](#what-it-does) 2. [安全限制](#security-limitations) 3. [提供商](#providers) 4. [自动启用提供商](#automatic-provider-enablement) 5. [安装说明](#installation) 6. [配置参考](#configuration-reference) 7. [CLI 用法](#cli-usage) 8. [Python 库用法](#python-library-usage) 9. [决策模型](#the-decision-model) 10. [地理与提供商策略](#geography--provider-policy) 11. [评分:威胁、身份、暴露、覆盖率](#scores) 12. [JSON 输出](#json-output) 13. [文本输出](#text-output) 14. [退出码](#exit-codes) 15. [缓存](#caching) 16. [错误处理](#error-handling) 17. [IPv4 和 IPv6 行为](#ipv4-and-ipv6-behavior) 18. [建议的白名单控制](#recommended-whitelist-controls) 19. [测试与质量](#testing--quality) 20. [从 v1 迁移](#migration-from-v1) ## 功能介绍 给定一个或多个公共 IP,IPTI 会: - 并发查询多达三个**威胁情报**提供商,并进行故障隔离(一个失败绝不会中止其他查询)。 - *仅*从返回了可用证据的提供商中推导出**威胁评分**(0–100),并根据配置的权重 × 置信度进行动态归一化。 - 衡量**证据覆盖率**,以便让您了解实际响应的已配置情报比例。 - 通过**身份**(ASN/所有者/FCrDNS)和**暴露**(开放服务/CVE)上下文进行丰富——这可能会提高警惕,但**绝不能**抵消真实的威胁证据。 - 输出**决策**(`APPROVE_WITH_CONTROLS` / `MANUAL_REVIEW` / `DENY` / `INCOMPLETE` / `NOT_APPLICABLE`)、**威胁分类**、原因以及建议的白名单控制措施。 ## 安全限制 - 威胁情报源存在覆盖盲区和报告延迟;没有证据并不代表没有威胁。 - 坚定的攻击者可能会使用任何情报源都尚未知晓的全新基础设施。 - 丰富信息(IPInfo/Shodan)属于时间点快照,可能已经过时。 - IPTI 不会自行扫描目标;它只查询第三方数据。 - 地理位置和托管提供商属于**策略上下文**,不能作为恶意行为的证明。 ## 提供商 | 提供商 | 角色 | 环境变量键 | 文档 | |---|---|---|---| | **AbuseIPDB** | 威胁情报 | `ABUSEIPDB_API_KEY` | | | **VirusTotal** | 威胁情报 | `VIRUSTOTAL_API_KEY` | | | **AlienVault OTX** | 威胁情报 | `ALIENVAULT_API_KEY` | | | IPInfo | 丰富信息(身份/地理) | `IPINFO_API_KEY` | | | Shodan | 丰富信息(暴露面) | `SHODAN_API_KEY` | | 每个提供商都是**可选且独立的**。默认情况下,必须有两个威胁提供商成功响应才能避免出现 `INCOMPLETE` 结果(`IPTI_MIN_SUCCESSFUL_THREAT_SOURCES=2`)——如果您有意仅使用单个提供商运行,请将其设置为 `1`(或传递 `--min-sources 1`),这会触发降低置信度的警告。 ### 隐私 / 匿名化检测(VPN / 代理 / Tor / 托管) 这些标志来自 IPInfo 的**隐私检测**,这是一个**付费附加功能**。 免费的/基础的 IPInfo token 仅返回地理位置信息——不包含 `privacy` 对象——因此这些标志会正确显示为 `unknown`(绝不会伪造为 `False`),并且 IPTI 会发出警告提示您这一点。**Tor 是个例外:**它也可以通过 **AbuseIPDB 的 `isTor`** 字段免费检测到,因此即使不升级 IPInfo,Tor 检测依然有效。每个解析出的标志都会显示其来源,例如 `Tor: True (via abuseipdb)`,完整的数据集位于 `enrichment.anonymization` 中。要获取 VPN/代理/中继/托管信息,请使用包含隐私检测的 IPInfo 套餐——一旦响应中包含该数据,IPTI 就会自动解析。 ## 自动启用提供商 每个提供商都有一个 `IPTI__ENABLED` 标志,接受 `auto` / `true` / `false`: | 模式 | 存在密钥 | 缺少密钥 | |---|---|---| | `auto`(默认) | **启用** | **禁用**(静默处理) | | `true` | **启用** | **禁用**,并发出警告(绝不会崩溃) | | `false` | **禁用** | **禁用** | **已禁用**的提供商永远不会被查询,并且会被**排除**在 `platforms_queried`、`platforms_succeeded`、`platforms_failed`、评分分母和归一化权重之外。**已启用但失败**的提供商*不会*被视为是干净的——相反,它会降低证据覆盖率。 ## 安装说明 要求 **Python 3.10+**。 ``` git clone https://github.com/bayufedra/ipti cd ipti python -m venv .venv && source .venv/bin/activate # 仅 runtime pip install -r requirements.txt # 或安装 package + console script + dev tools pip install -e ".[dev]" cp .env.example .env # then fill in the API keys you have ``` ## 配置参考 配置在加载时会进行验证;无效值会快速报错并附带清晰的提示信息。有关完整的注释列表,请参见 [`.env.example`](.env.example)。关键设置: | 变量 | 默认值 | 含义 | |---|---|---| | `IPTI__ENABLED` | `auto` | 每个提供商的 `auto`/`true`/`false` | | `_API_KEY` | – | 提供商 API token | | `IPTI_REQUEST_CONNECT_TIMEOUT` | `3.05` | 连接超时 (秒) | | `IPTI_REQUEST_READ_TIMEOUT` | `15` | 读取超时 (秒) | | `IPTI_REQUEST_RETRIES` | `2` | 瞬时故障重试次数 | | `IPTI_MAX_CONCURRENCY` | `8` | 最大并发 IP 数 / 连接数 | | `IPTI_CACHE_TTL_SECONDS` | `3600` | 成功证据的 TTL | | `IPTI_ERROR_CACHE_TTL_SECONDS` | `60` | 错误的 TTL(必须 ≤ 成功的 TTL) | | `IPTI_MIN_SUCCESSFUL_THREAT_SOURCES` | `2` | 低于此值 → `INCOMPLETE` | | `IPTI_MIN_THREAT_COVERAGE` | `0.67` | 低于此值 → `MANUAL_REVIEW` | | `IPTI_MAX_THREAT_AGE_DAYS` | `90` | 更旧的证据会被判定为 `STALE` | | `IPTI_ABUSEIPDB_WEIGHT` | `0.35` | 原始权重(总和无需为 1.0) | | `IPTI_VIRUSTOTAL_WEIGHT` | `0.40` | 原始权重 | | `IPTI_ALIENVAULT_OTX_WEIGHT` | `0.25` | 原始权重 | | `IPTI_THREAT_REVIEW_THRESHOLD` | `30` | 威胁评分 → 人工审核 | | `IPTI_THREAT_DENY_THRESHOLD` | `70` | 威胁评分 → 拒绝 | | `IPTI_EXPOSURE_REVIEW_THRESHOLD` | `60` | 暴露风险 → 人工审核 | | `IPTI_IDENTITY_MINIMUM_CONFIDENCE` | `85` | 身份置信度下限(提供所有者时生效) | | `IPTI_RESTRICTED_COUNTRIES` | – | 策略:强制拦截这些 ISO 代码(→ `DENY`) | | `IPTI_REQUIRE_APPROVAL_COUNTRIES` | – | 策略:对这些 ISO 代码进行人工审核(→ `MANUAL_REVIEW`) | | `IPTI_EXPECTED_COUNTRIES` | – | 策略:*不在此*列表中的任何国家/地区 → `MANUAL_REVIEW` | | `IPTI_BLOCK_TOR` | `true` | 强制拦截确认的 Tor | | `IPTI_BLOCK_OPEN_PROXY` | `true` | 强制拦截确认的开放/住宅代理 | | `IPTI_ALLOW_NON_GLOBAL` | `false` | 允许私有/保留 IP | 权重**无需**总和为 1.0;它们会根据实际返回可用证据的提供商进行动态归一化。已禁用的提供商永远不会归一化到评分中。 ## CLI 用法 ``` # 单个 IP ipti -i 8.8.8.8 # 多个 IP (IPv4 + IPv6),JSON 输出 ipti -i 1.1.1.1 2606:4700:4700::1111 -f json # 从文件读取,保存报告,仅摘要 ipti -l ips.txt -o report.txt -S # 覆盖 providers(CLI 优先于 env)并在 review 时 gate CI ipti -i 203.0.113.5 --disable-provider shodan --fail-on-review # 验证配置而不进行任何评估 ipti --config-check ``` 实用标志:`--no-color`、`--verbose`、`--debug`、`--show-disabled`(在文本报告中单独列出已禁用的提供商)、`--fail-on-review`(在出现审核/不完整状态时以非零状态退出)、`--config-check`、`--version`、`--claimed-owner NAME`、`--allow-non-global`、`--enable-provider NAME` / `--disable-provider NAME`(可重复使用;CLI 会覆盖环境变量),以及阈值覆盖参数 `--max-age`、`--review-threshold`、`--deny-threshold`、`--min-sources`、`--min-coverage`。颜色仅在交互式终端中输出,并可通过 `--no-color`、`NO_COLOR`、JSON 输出或文件输出禁用。 (`python main.py ...` 依然可用,作为控制台脚本的轻量级垫片实现。) ## Python 库用法 ### 异步引擎 — 单个与批处理(推荐) ``` import asyncio from ipti import AssessmentEngine, load_settings async def main(): settings = load_settings() # reads .env / environment async with AssessmentEngine(settings) as engine: a = await engine.assess("8.8.8.8", claimed_owner="Google") print(a.decision.value, a.threat_classification.value) print("threat", a.scores.threat, "coverage", a.scores.evidence_coverage) # Batch: bounded concurrency, input order preserved, duplicates removed. for r in await engine.assess_many(["1.1.1.1", "8.8.8.8", "1.1.1.1"]): print(r.ip, r.decision.value) asyncio.run(main()) ``` `assess()` 和 `assess_many()` 返回 `Assessment` 对象。`decision` 和 `threat_classification` 是**枚举类型**——使用 `.value` 获取字符串格式。 结构化字段:`a.scores`(`threat`、`identity_confidence`、`exposure_risk`、`evidence_coverage`)、`a.providers`(`dict[str, ProviderResult]`)、`a.enrichment`(`ipinfo`/`shodan`/`ptr`/`exposure`/`identity`/`policy`/`anonymization`)、`a.hard_blocks`、`a.review_reasons`、`a.recommended_controls`、`a.warnings` 以及 `a.is_safe`(已弃用)。 ### 一次性异步辅助函数 ``` import asyncio from ipti import assess_ip assessment = asyncio.run(assess_ip("1.1.1.1")) print(assessment.decision.value, assessment.scores.threat) ``` ### 转换为 JSON 可序列化的字典 ``` import asyncio from ipti import assess_ip from ipti.reporting import assessment_to_dict, render_json a = asyncio.run(assess_ip("8.8.8.8")) data = assessment_to_dict(a) # plain dict (what the CLI's -f json prints) print(data["decision"], data["scores"]["threat"]) print(render_json(a)) # pretty JSON string ``` ### 遗留同步 API(向后兼容) `IPTI(ip).ipti_check()` 依然可用,并返回一个属于旧模式**超集**的字典。它会发出 `DeprecationWarning`;`is_safe`/`safe_ratio` 已弃用——建议使用 `decision` 和 `scores`。 ``` from ipti import IPTI result = IPTI( "8.8.8.8", max_age_in_days=60, # mapped onto IPTI_MAX_THREAT_AGE_DAYS score_threshold=50, # mapped onto IPTI_THREAT_REVIEW_THRESHOLD user_threshold=5, # accepted for signature compat; no longer used safe_ratio=0.8, # accepted for signature compat; no longer used ).ipti_check() print(result["ip"], result["decision"], result["is_safe"]) print("threat:", result["scores"]["threat"], "coverage:", result["scores"]["evidence_coverage"]) ``` ## 决策模型 `decision` 是以下值之一: | 决策 | 含义 | |---|---| | `APPROVE_WITH_CONTROLS` | 无硬性阻断;威胁低于审核阈值;覆盖率达标;应用建议的控制措施 | | `MANUAL_REVIEW` | 存在可疑证据、覆盖率低、所需来源不可用/过时、暴露风险高、身份置信度低,或触发策略标记 | | `DENY` | 触发硬性阻断、威胁 ≥ 拒绝阈值,或 ≥2 个相互印证的恶意来源 | | `INCOMPLETE` | 成功响应的威胁来源数量少于要求——**绝不会被**判定为“安全” | | `NOT_APPLICABLE` | 输入的 IP 不是有效的、全球可路由的 IP | 单独的 `threat_classification` 描述了证据本身:`NO_KNOWN_THREAT`、`LOW_RISK`、`SUSPICIOUS`、`HIGH_RISK`、`CONFIRMED_MALICIOUS`、`UNKNOWN`。 **硬性阻断**(→ `DENY`)包括:任何提供商给出高置信度的恶意结论、两个独立的恶意结论、恶意结论 + 独立的可疑印证、在策略拦截时确认使用了 Tor/开放代理、触发受限国家策略,或实质性的所有权不匹配。上下文元数据(PTR、地理位置提供商信誉、标准开放端口)可能会引发担忧或降低置信度,但**绝不能**降低已计算出的威胁评分或抵消直接的威胁证据。 ## 地理与提供商策略 地理位置和托管提供商属于**策略上下文**,绝不是威胁证据。在 v1 中,“高风险国家”会在暗中降低安全评分,并将 CN/RU/OVH 等硬编码为高风险——这会导致误报,现已被移除。在 v2 中: - 开箱即用时,没有任何国家或提供商被判定为高风险(所有策略列表均为空)。 - 您可以根据组织需求通过 `IPTI_RESTRICTED_COUNTRIES`(→ `DENY`)、`IPTI_REQUIRE_APPROVAL_COUNTRIES`(→ `MANUAL_REVIEW`)和 `IPTI_EXPECTED_COUNTRIES`(列表外的任何国家/地区 → `MANUAL_REVIEW`)进行选择性加入。 - 触发策略会将**决策**推向审核/拒绝——它**绝不会**改变威胁评分。结果是透明的(显示在*策略评估*部分和 `enrichment.policy` 中),而不是隐藏的数值惩罚。 ``` # "仅 approve 这些国家/地区的 IP,deny 这些,review 其余部分" IPTI_EXPECTED_COUNTRIES=US,GB,SG IPTI_RESTRICTED_COUNTRIES=KP ipti -i 203.0.113.5 ``` ## 评分 三个独立的测量值加上一个覆盖率数据——绝不会平均为一个容易产生误导的数字: - **威胁(0–100)**——有多少证据表明存在当前/近期的恶意活动。经过置信度加权,并在提供可用证据的提供商中进行动态归一化。 - **身份置信度(0–100)**——该 IP 映射到其预期所有者/角色(ASN、组织、正向确认反向 DNS、可选的声明所有者)的置信程度。 - **暴露风险(0–100)**——主机暴露的程度(公共服务、未加密/管理/数据库端口、已知 CVE、代理/VPN/Tor)。当不存在 Shodan 记录时为 `null`(即**未知**,而非低风险)。 - **证据覆盖率(0.0–1.0)**——实际返回了可用证据的已配置启用权重。已禁用的提供商不会降低该值;而已失败的提供商会降低该值。 ## JSON 输出 ``` { "schema_version": "2.0", "ip": "8.8.8.8", "ip_version": 4, "checked_at": "2026-07-15T05:00:00+00:00", "decision": "APPROVE_WITH_CONTROLS", "threat_classification": "NO_KNOWN_THREAT", "is_safe": true, // deprecated: == (decision APPROVE_WITH_CONTROLS) "scores": { "threat": 1.0, "identity_confidence": 95.0, "exposure_risk": 9.0, "evidence_coverage": 1.0 }, "platform_summary": { "platforms_available": 3, "platforms_enabled": 3, "platforms_disabled": 0, "platforms_queried": 3, "platforms_succeeded": 3, "platforms_failed": 0 }, "providers": { // Each provider object also carries: data_freshness_days, queried_at, // observed_at, duration_ms, threat_categories, raw_metrics, error_code, // error_message. Disabled providers appear with enabled=false and a // "disabled_*" status (never omitted). "abuseipdb": { "enabled": true, "status": "clean", "risk_score": 0.0, "confidence": 0.95, "reasons": ["Abuse confidence score is 0"] }, "virustotal": { "enabled": true, "status": "clean", "risk_score": 0.0, "confidence": 0.95, "reasons": ["0 engines flagged malicious ..."] }, "alienvault_otx": { "enabled": true, "status": "clean", "risk_score": 5.0, "confidence": 0.70, "reasons": ["No active recent threat pulses"] } }, "enrichment": { "ipinfo": { "enabled": true, "status": "clean", "data": { "country": "US", "city": "Mountain View", "org": "AS15169 Google LLC", "asn": "AS15169" } }, "shodan": { "enabled": true, "status": "clean", "data": { "ports": [53, 443] } }, "ptr": { "status": "organization_ptr", "ptr": "dns.google", "forward_addresses": ["8.8.8.8"] }, "exposure": { "exposure_risk": 9.0, "risk_level": "low", "open_ports": [53, 443], "critical_services": [], "cves": [] }, "identity": { "identity_confidence": 95.0, "network_type": "unknown", "ptr_status": "organization_ptr" }, "policy": { "country": "US", "geographic_policy": "neutral", "geographic_reason": "US is not on any policy list", "provider_context": "AS15169 Google LLC" }, "anonymization": { "is_vpn": null, "is_proxy": null, "is_tor": null, "is_relay": null, "is_hosting": null, "sources": {} } }, "hard_blocks": [], "review_reasons": [], "recommended_controls": [ "Allow only the required destination host and port", "Use a /32 IPv4 rule or /128 IPv6 rule", "Require application-level authentication", "Enable connection logging and alerting", "Configure an expiration date and schedule periodic re-review" ], "warnings": [] } ``` 提供了 `schema_version`,以便未来的变更能够被安全管理。 ## 文本输出 ``` === IP Threat Intelligence Assessment === IP Address : 8.8.8.8 IP Version : 4 Checked At : 2026-07-15T05:00:00+00:00 Schema Version : 2.0 Decision : APPROVE_WITH_CONTROLS Threat Class : LOW_RISK --- Scores --- Threat Score : 1.0 / 100 Evidence Coverage : 100% Identity Conf. : 95.0 / 100 Exposure Risk : 9.0 / 100 --- Threat Providers --- Available 3 Enabled 3 Disabled 0 Queried 3 Succeeded 3 Failed 0 abuseipdb : clean risk=0 conf=0.95 - Abuse confidence score is 0 virustotal : clean risk=0 conf=0.95 alienvault_otx : clean risk=5 conf=0.70 --- Server Information --- Country : US City : Mountain View Organization : AS15169 Google LLC Hostname : dns.google Privacy (unknown = provider plan does not report it, not False): Proxy : unknown VPN : unknown Tor : unknown (or e.g. "True (via abuseipdb)") Relay : unknown Hosting : unknown --- Policy Assessment --- Geographic Policy : neutral — US is not on any policy list Provider/Network : AS15169 Google LLC --- Identity / Ownership --- Identity Conf. : 95.0 / 100 Network Type : unknown PTR Record : dns.google (organization_ptr) --- Exposure / Ports --- Exposure Risk : 9.0 / 100 (low) Open Ports : 53, 443 --- Recommended Whitelist Controls --- → Allow only the required destination host and port ... ``` 失败的提供商(`timeout`/`error`/`rate_limited`)始终会显示其状态;**已禁用**的提供商会在计数行中进行汇总,并且仅在指定 `--show-disabled` 时才会单独列出。 ## 退出码 | 代码 | 含义 | |---|---| | `0` | 完成且无拒绝 | | `1` | 一个或多个 IP 被**拒绝**(或在使用 `--fail-on-review` 时出现审核/不完整状态) | | `2` | 一个或多个 IP 需要**人工审核 / 不完整 / 不适用** | | `3` | 输入或配置无效 | | `4` | 意外的内部故障 | ## 缓存 结果会在当前会话中按 `(provider, ip)` 进行缓存。成功的证据使用 `IPTI_CACHE_TTL_SECONDS`;操作错误使用较短的 `IPTI_ERROR_CACHE_TTL_SECONDS`;**身份验证失败绝不会被缓存**(以免之后被误判为干净)。批处理运行会去重相同的 IP 并复用连接池中的 HTTP 连接。缓存命中会在调试级别进行记录。 ## 错误处理 - 单个提供商的失败会被转换为带有明确状态(`timeout`、`rate_limited`、`error`)的标准化结果——它绝不会中止其他提供商的查询。 - 瞬时故障(429/5xx、超时、连接重置)将使用指数退避 + 抖动进行重试,并遵守 `Retry-After`;4xx(429 除外)不会进行重试。 - 缺少 API 密钥绝不会导致 IPTI 崩溃;相关提供商仅会被禁用。 - API token 和授权头会从所有日志中脱敏处理。 ## IPv4 和 IPv6 行为 两者均使用标准库的 `ipaddress` 模块进行验证。默认情况下,仅评估全球可路由的公共地址;私有/环回/链路本地/多播/保留/未指定/文档地址段将被拒绝(可使用 `--allow-non-global` / `IPTI_ALLOW_NON_GLOBAL` 进行覆盖)。AlienVault OTX 会自动使用 IPv4 或 IPv6 指标 endpoint。 采用 GPL-3.0-or-later 许可。欢迎在 参与贡献。
标签:GitHub, IP情报, Python, 威胁情报, 实时处理, 开发者工具, 文档结构分析, 无后门, 计算机取证, 逆向工具