1. OTA升级包签名验证实战

任务目标

对汽车OTA系统进行威胁建模,识别架构层面的安全威胁并形成可追溯的风险清单。本案例以典型OTA架构为对象,输出STRIDE威胁分类结果与风险优先级排序,为后续安全设计提供输入。

OTA参考架构

本案例分析的OTA架构包含四个核心组件:

  • OEM云平台:负责升级包签名、策略管理与任务下发
  • 车机端OTA客户端:接收升级包、校验签名并执行安装
  • 通信通道:车机与云平台之间的TLS加密链路
  • ECU(电子控制单元):实际接收并运行新固件的目标控制器

威胁建模流程与实现

本案例采用数据流图(DFD)驱动的方式,将OTA系统分解为数据流元素,再对每个元素应用STRIDE威胁分类。以下Python代码实现了完整的威胁建模闭环:定义架构组件与数据流、自动生成STRIDE威胁候选、计算风险优先级(基于DREAD评分),并输出可排序的风险清单。

import json
from dataclasses import dataclass, field
from typing import List, Dict, Tuple

# ---------- 1. 架构定义 ----------
@dataclass
class DataFlowElement:
    """DFD元素:进程、数据存储、外部实体或数据流"""
    id: str
    name: str
    type: str  # process / datastore / external / flow

@dataclass
class ThreatCandidate:
    """威胁候选条目"""
    element_id: str
    element_name: str
    threat_type: str  # STRIDE分类
    description: str
    damage: int = 0      # DREAD: Damage (0-10)
    reproducibility: int = 0
    exploitability: int = 0
    affected_users: int = 0
    discoverability: int = 0

    @property
    def risk_score(self) -> float:
        """DREAD风险值 = (D+R+E+A+D) / 5"""
        return (self.damage + self.reproducibility + self.exploitability +
                self.affected_users + self.discoverability) / 5.0

# ---------- 2. 架构实例化 ----------
# 定义OTA系统DFD元素
elements = [
    DataFlowElement("E1", "OEM云平台", "external"),
    DataFlowElement("P1", "OTA客户端(车机)", "process"),
    DataFlowElement("P2", "ECU刷写代理", "process"),
    DataFlowElement("D1", "升级包存储区", "datastore"),
    DataFlowElement("F1", "TLS加密通道", "flow"),
]

# 定义数据流(信任边界内的交互)
flows = [
    ("E1", "F1", "下发升级任务与包"),
    ("F1", "P1", "接收加密升级包"),
    ("P1", "D1", "暂存升级包"),
    ("P1", "P2", "触发刷写"),
    ("P2", "D1", "读取升级包"),
]

# ---------- 3. STRIDE威胁规则库 ----------
# 每个规则: (适用元素类型, STRIDE类型, 威胁模板)
threat_rules = [
    ("external", "Spoofing", "{name} 身份被伪造,攻击者伪装成合法OTA服务器"),
    ("external", "Tampering", "{name} 下发内容被篡改,升级包完整性受损"),
    ("process", "Spoofing", "{name} 被恶意应用冒充,接收伪造升级指令"),
    ("process", "Tampering", "{name} 处理逻辑被篡改,绕过签名校验"),
    ("process", "Repudiation", "{name} 缺少操作审计,无法追溯非法刷写行为"),
    ("process", "InformationDisclosure", "{name} 内存中的升级包密钥或固件被窃取"),
    ("process", "DenialOfService", "{name} 被资源耗尽攻击,无法响应合法升级请求"),
    ("process", "ElevationOfPrivilege", "{name} 漏洞导致攻击者获得ECU刷写权限"),
    ("datastore", "Tampering", "{name} 中暂存的升级包被替换为恶意固件"),
    ("datastore", "InformationDisclosure", "{name} 明文存储升级包,固件内容泄露"),
    ("flow", "Tampering", "{name} 中间人攻击,篡改传输中的升级包"),
    ("flow", "InformationDisclosure", "{name} 加密强度不足,固件被窃听解密"),
    ("flow", "DenialOfService", "{name} 被流量阻断,升级任务无法完成"),
]

# ---------- 4. 威胁生成与DREAD评分 ----------
def generate_threats(elements: List[DataFlowElement],
                     flows: List[Tuple[str, str, str]],
                     rules: List[Tuple[str, str, str]]) -> List[ThreatCandidate]:
    """对每个元素应用匹配的STRIDE规则,生成威胁候选"""
    threats = []
    elem_map = {e.id: e for e in elements}

    # 为每个元素生成威胁
    for elem in elements:
        for rule_type, stride, template in rules:
            if rule_type == elem.type:
                threats.append(ThreatCandidate(
                    element_id=elem.id,
                    element_name=elem.name,
                    threat_type=stride,
                    description=template.format(name=elem.name)
                ))

    # 为每条数据流生成威胁(流本身作为元素处理)
    flow_elements = [e for e in elements if e.type == "flow"]
    for flow_elem in flow_elements:
        for rule_type, stride, template in rules:
            if rule_type == "flow":
                threats.append(ThreatCandidate(
                    element_id=flow_elem.id,
                    element_name=flow_elem.name,
                    threat_type=stride,
                    description=template.format(name=flow_elem.name)
                ))

    return threats

# ---------- 5. DREAD评分(基于架构特征自动赋值) ----------
def assign_dread_scores(threats: List[ThreatCandidate]) -> None:
    """根据威胁类型与元素类型自动评分,模拟专家评估"""
    # 评分基准:外部实体相关威胁可利用性更高
    for t in threats:
        # 基础分
        t.damage = 8 if t.threat_type in ("Tampering", "ElevationOfPrivilege") else 6
        t.reproducibility = 7 if t.element_name == "TLS加密通道" else 5
        t.exploitability = 8 if t.element_name == "OEM云平台" else 6
        t.affected_users = 9 if "ECU" in t.element_name else 7
        t.discoverability = 6

        # 特定威胁调整
        if t.threat_type == "Spoofing":
            t.damage = 9
            t.exploitability = 7
        elif t.threat_type == "DenialOfService":
            t.damage = 5
            t.reproducibility = 8
        elif t.threat_type == "InformationDisclosure":
            t.damage = 7
            t.discoverability = 5

# ---------- 6. 执行威胁建模 ----------
threats = generate_threats(elements, flows, threat_rules)
assign_dread_scores(threats)

# 按风险分数降序排序
threats.sort(key=lambda t: t.risk_score, reverse=True)

# ---------- 7. 输出结果 ----------
print("=" * 70)
print("OTA架构威胁建模结果 (STRIDE + DREAD)")
print("=" * 70)
print(f"{'优先级':<4} {'风险值':<6} {'STRIDE':<22} {'组件':<16} 威胁描述")
print("-" * 70)

for idx, t in enumerate(threats, 1):
    print(f"P{idx:<3} {t.risk_score:<6.1f} {t.threat_type:<22} {t.element_name:<16} {t.description}")

# 输出高风险项摘要(风险值 ≥ 7.0)
high_risks = [t for t in threats if t.risk_score >= 7.0]
print("\n" + "=" * 70)
print(f"高风险威胁 (风险值≥7.0): {len(high_risks)}项")
print("=" * 70)
for t in high_risks:
    print(f"  - [{t.risk_score:.1f}] {t.threat_type}: {t.description}")

# 保存为JSON供后续工具使用
output = {
    "architecture": [{"id": e.id, "name": e.name, "type": e.type} for e in elements],
    "threats": [
        {
            "element": t.element_name,
            "stride": t.threat_type,
            "description": t.description,
            "risk_score": t.risk_score,
            "dread": {
                "damage": t.damage,
                "reproducibility": t.reproducibility,
                "exploitability": t.exploitability,
                "affected_users": t.affected_users,
                "discoverability": t.discoverability
            }
        } for t in threats
    ]
}

with open("ota_threat_model.json", "w", encoding="utf-8") as f:
    json.dump(output, f, ensure_ascii=False, indent=2)
print("\n威胁模型已保存至 ota_threat_model.json")

运行方式

  1. 将上述代码保存为 ota_threat_model.py
  2. 执行 python ota_threat_model.py(需Python 3.7+,无第三方依赖)

验证方法

运行后观察输出:

  • 程序应输出按风险值降序排列的威胁清单,每条包含STRIDE分类、组件名与描述
  • 高风险摘要部分应列出风险值≥7.0的威胁项
  • 当前目录下生成 ota_threat_model.json 文件,包含结构化威胁数据

通过条件:威胁清单覆盖全部5个DFD元素和4条数据流,每条威胁均包含有效的STRIDE分类与DREAD风险值;JSON文件可正常解析且与终端输出一致。

预期输出示例

======================================================================
OTA架构威胁建模结果 (STRIDE + DREAD)
======================================================================
优先级 风险值  STRIDE                 组件              威胁描述
----------------------------------------------------------------------
P1   8.2   Spoofing              OEM云平台         OEM云平台 身份被伪造,攻击者伪装成合法OTA服务器
P2   7.8   Tampering             TLS加密通道       TLS加密通道 中间人攻击,篡改传输中的升级包
P3   7.6   ElevationOfPrivilege  OTA客户端(车机)   OTA客户端(车机) 漏洞导致攻击者获得ECU刷写权限
...

说明:以上为基于代码逻辑与输入规则可直接推出的确定性输出格式,实际风险值由DREAD评分函数计算得出,不同评分策略会产生不同排序结果。