跳转至

嵌入式系统安全概述

概述

随着物联网和智能设备的普及,嵌入式系统安全已成为产品开发中不可忽视的关键要素。从智能家居到工业控制,从医疗设备到汽车电子,安全漏洞可能导致严重的后果。完成本文学习后,你将能够:

  • 理解嵌入式系统面临的主要安全威胁
  • 掌握安全需求分析的基本方法
  • 了解安全设计的核心原则
  • 熟悉主要的安全标准和规范
  • 掌握基本的安全防护措施

背景知识

为什么嵌入式安全如此重要?

嵌入式系统安全的重要性体现在多个方面:

1. 物理接触风险 - 设备通常部署在无人看管的环境 - 攻击者可能直接接触硬件 - 调试接口可能被恶意利用

2. 长生命周期 - 设备可能运行数年甚至数十年 - 难以及时更新和修补漏洞 - 需要考虑长期的安全威胁

3. 资源受限 - 计算能力和存储空间有限 - 难以实现复杂的安全机制 - 需要在安全性和性能间平衡

4. 安全影响广泛 - 智能家居:隐私泄露 - 工业控制:生产中断 - 医疗设备:生命安全 - 汽车电子:行车安全

前置知识要求

本文假设你已经掌握: - 嵌入式系统基础知识 - 基本的编程能力 - 计算机网络基础概念

核心内容

1. 嵌入式系统安全威胁

1.1 物理攻击

物理攻击是指攻击者通过直接接触设备进行的攻击。

常见物理攻击类型

  1. 调试接口攻击
  2. JTAG/SWD接口未关闭
  3. UART调试端口暴露
  4. 通过调试接口读取固件

  5. 侧信道攻击

  6. 功耗分析(Power Analysis)
  7. 电磁辐射分析(EM Analysis)
  8. 时序分析(Timing Analysis)
  9. 通过物理特征推断密钥

  10. 硬件篡改

  11. 芯片解封装
  12. 探针攻击
  13. 故障注入(Fault Injection)
  14. 修改硬件电路

防护措施: - 禁用或保护调试接口 - 使用安全芯片存储敏感数据 - 实施物理防篡改检测 - 采用屏蔽和加固技术

1.2 软件攻击

软件攻击通过利用代码漏洞来获取系统控制权。

常见软件攻击类型

  1. 缓冲区溢出
  2. 栈溢出攻击
  3. 堆溢出攻击
  4. 覆盖返回地址或函数指针

  5. 代码注入

  6. 命令注入
  7. SQL注入(如果使用数据库)
  8. 脚本注入

  9. 恶意固件

  10. 固件替换
  11. 固件篡改
  12. 后门植入

防护措施: - 输入验证和边界检查 - 使用安全的编程实践 - 固件签名验证 - 代码审计和静态分析

1.3 网络攻击

随着物联网的发展,网络攻击成为主要威胁。

常见网络攻击类型

  1. 中间人攻击(MITM)
  2. 拦截通信数据
  3. 篡改传输内容
  4. 窃取敏感信息

  5. 拒绝服务攻击(DoS)

  6. 资源耗尽
  7. 系统崩溃
  8. 服务不可用

  9. 重放攻击

  10. 捕获合法数据包
  11. 重新发送以欺骗系统
  12. 绕过认证机制

  13. 固件劫持

  14. 拦截固件更新
  15. 注入恶意固件
  16. 远程控制设备

防护措施: - 使用加密通信(TLS/DTLS) - 实施身份认证和授权 - 防重放机制(时间戳、随机数) - 安全的固件更新机制

1.4 供应链攻击

供应链攻击在开发和生产过程中植入恶意代码。

攻击途径: - 第三方库和组件 - 开发工具链 - 生产环境 - 物流运输

防护措施: - 供应商安全评估 - 代码和组件审计 - 安全的开发环境 - 生产过程监控

2. 安全需求分析

2.1 CIA三要素

信息安全的核心目标可以用CIA三要素概括:

1. 机密性(Confidentiality) - 确保信息不被未授权访问 - 数据加密存储和传输 - 访问控制机制

2. 完整性(Integrity) - 确保数据未被篡改 - 数据校验和签名 - 防止未授权修改

3. 可用性(Availability) - 确保系统正常运行 - 防止拒绝服务攻击 - 故障恢复机制

2.2 威胁建模

威胁建模是识别和评估安全风险的系统方法。

STRIDE威胁模型

威胁类型 说明 示例
**S**poofing(欺骗) 伪装成其他实体 伪造设备ID
**T**ampering(篡改) 未授权修改数据 修改配置参数
**R**epudiation(抵赖) 否认操作行为 无法追踪操作记录
**I**nformation Disclosure(信息泄露) 暴露敏感信息 密钥泄露
**D**enial of Service(拒绝服务) 使系统不可用 资源耗尽
**E**levation of Privilege(权限提升) 获取更高权限 普通用户获取管理员权限

威胁建模步骤

  1. 识别资产
  2. 敏感数据(密钥、用户信息)
  3. 关键功能(认证、控制)
  4. 系统资源(CPU、内存)

  5. 识别威胁

  6. 使用STRIDE模型
  7. 分析攻击面
  8. 考虑攻击者能力

  9. 评估风险

  10. 威胁发生概率
  11. 潜在影响程度
  12. 风险等级划分

  13. 制定对策

  14. 消除威胁
  15. 降低风险
  16. 转移风险
  17. 接受风险

2.3 安全等级划分

根据系统的安全需求,可以划分不同的安全等级。

通用安全等级

  • Level 0:无安全要求
  • 公开信息
  • 无敏感数据
  • 无安全威胁

  • Level 1:基本安全

  • 基本访问控制
  • 简单加密
  • 日志记录

  • Level 2:中等安全

  • 强认证机制
  • 数据加密
  • 安全启动
  • 固件签名

  • Level 3:高安全

  • 多因素认证
  • 硬件安全模块
  • 安全隔离
  • 实时监控

  • Level 4:极高安全

  • 军事级加密
  • 物理防护
  • 形式化验证
  • 严格审计

3. 安全设计原则

3.1 纵深防御(Defense in Depth)

不依赖单一安全措施,而是建立多层防护。

多层防护示例

┌─────────────────────────────────────┐
│  物理层:防篡改、屏蔽、加固          │
├─────────────────────────────────────┤
│  硬件层:安全芯片、加密引擎          │
├─────────────────────────────────────┤
│  启动层:安全启动、固件验证          │
├─────────────────────────────────────┤
│  系统层:访问控制、权限管理          │
├─────────────────────────────────────┤
│  应用层:输入验证、安全编码          │
├─────────────────────────────────────┤
│  网络层:加密通信、防火墙            │
└─────────────────────────────────────┘

实施要点: - 每层独立防护 - 层与层之间相互支持 - 单层失效不影响整体安全 - 增加攻击难度和成本

3.2 最小权限原则(Principle of Least Privilege)

每个组件只拥有完成任务所需的最小权限。

应用示例

  1. 进程权限
  2. 普通任务以低权限运行
  3. 只在必要时提升权限
  4. 及时降低权限

  5. 文件访问

  6. 只读权限用于配置文件
  7. 写权限仅限必要目录
  8. 执行权限严格控制

  9. 网络访问

  10. 限制可访问的端口
  11. 白名单机制
  12. 最小化攻击面

3.3 默认安全(Secure by Default)

系统默认配置应该是安全的。

设计要点

  1. 默认关闭不必要的功能
  2. 调试接口默认禁用
  3. 不必要的服务不启动
  4. 测试功能不进入生产版本

  5. 强制安全配置

  6. 强制修改默认密码
  7. 必须启用加密
  8. 自动安全更新

  9. 安全的默认值

  10. 最严格的访问控制
  11. 最强的加密算法
  12. 最短的超时时间

3.4 故障安全(Fail-Safe)

系统在出现故障时应该保持安全状态。

设计策略

  1. 安全失效模式
  2. 认证失败时拒绝访问
  3. 加密失败时停止通信
  4. 验证失败时拒绝执行

  5. 异常处理

  6. 捕获所有异常
  7. 记录异常信息
  8. 安全地恢复或关闭

  9. 看门狗机制

  10. 检测系统异常
  11. 自动重启恢复
  12. 防止系统挂死

3.5 开放设计(Open Design)

安全性不应依赖于算法的保密,而应依赖于密钥的保密。

Kerckhoffs原则: - 使用公开的、经过验证的加密算法 - 不要自己发明加密算法 - 安全性依赖于密钥管理 - 算法公开有利于发现漏洞

实践建议: - 使用标准加密算法(AES、RSA) - 使用成熟的安全协议(TLS) - 参与安全社区审查 - 定期安全审计

3.6 完全中介(Complete Mediation)

每次访问都必须经过权限检查。

实施要点

  1. 不信任缓存的权限
  2. 每次访问都验证
  3. 不依赖之前的检查结果
  4. 防止权限变更后的漏洞

  5. 集中的访问控制

  6. 统一的权限检查点
  7. 一致的安全策略
  8. 便于审计和维护

  9. 无法绕过的检查

  10. 所有访问路径都检查
  11. 防止直接访问
  12. 强制执行安全策略

4. 安全标准与规范

4.1 通用安全标准

ISO/IEC 27001 - 信息安全管理体系 - 适用于所有类型的组织 - 关注管理和流程

Common Criteria (CC) - 信息技术安全评估标准 - 评估保证级别(EAL1-EAL7) - 国际通用认证

NIST Cybersecurity Framework - 美国国家标准与技术研究院 - 网络安全框架 - 识别、保护、检测、响应、恢复

4.2 行业特定标准

汽车行业 - ISO 26262:功能安全 - ISO/SAE 21434:网络安全 - AUTOSAR:汽车软件架构

工业控制 - IEC 62443:工业自动化和控制系统安全 - NERC CIP:关键基础设施保护

医疗设备 - IEC 62304:医疗设备软件生命周期 - FDA Guidance:医疗设备网络安全指南

物联网 - ETSI EN 303 645:消费类物联网安全 - IoT Security Foundation:物联网安全最佳实践

4.3 编码安全标准

MISRA C - 嵌入式C编程规范 - 避免未定义行为 - 提高代码可靠性和安全性

CERT C Coding Standard - 安全编码规则 - 常见漏洞防范 - 由CERT/CC维护

CWE (Common Weakness Enumeration) - 软件弱点分类 - 常见安全漏洞 - 漏洞数据库

5. 基本防护措施

5.1 安全启动

安全启动确保只有经过验证的固件才能运行。

安全启动流程

┌──────────────┐
│  ROM Bootloader  │  ← 硬件信任根
│  (不可修改)      │
└────────┬─────────┘
         │ 验证签名
┌──────────────┐
│  Bootloader   │
│  (可更新)     │
└────────┬─────────┘
         │ 验证签名
┌──────────────┐
│  Application  │
│  Firmware     │
└──────────────┘

实施要点: - 使用数字签名验证固件 - 建立信任链 - 防止回滚攻击 - 失败时拒绝启动

5.2 固件保护

代码保护: - 读保护(RDP) - 写保护 - 调试接口禁用 - 防止固件提取

固件加密: - 存储加密 - 传输加密 - 密钥管理 - 防止逆向工程

5.3 安全通信

传输层安全: - TLS/DTLS协议 - 证书验证 - 加密通信 - 防止中间人攻击

应用层安全: - 消息认证码(MAC) - 数字签名 - 时间戳和随机数 - 防重放攻击

5.4 密钥管理

密钥生成: - 使用硬件随机数生成器 - 足够的密钥长度 - 定期更新密钥

密钥存储: - 使用安全芯片(SE/TEE) - 加密存储 - 访问控制 - 防止提取

密钥分发: - 安全的密钥交换协议 - 证书管理 - 密钥撤销机制

5.5 访问控制

身份认证: - 密码认证 - 证书认证 - 生物特征认证 - 多因素认证

授权管理: - 基于角色的访问控制(RBAC) - 访问控制列表(ACL) - 最小权限原则 - 权限审计

5.6 安全日志

日志记录: - 记录安全事件 - 登录尝试 - 权限变更 - 异常行为

日志保护: - 防篡改 - 加密存储 - 定期备份 - 安全传输

日志分析: - 实时监控 - 异常检测 - 安全审计 - 事件响应

实践示例

示例1:简单的安全启动验证

以下是一个简化的安全启动验证示例:

#include <stdint.h>
#include <string.h>

// 简化的RSA签名验证(实际应使用加密库)
typedef struct {
    uint8_t signature[256];
    uint32_t firmware_size;
    uint32_t firmware_crc;
} firmware_header_t;

// 验证固件签名
int verify_firmware_signature(const firmware_header_t *header,
                              const uint8_t *firmware,
                              const uint8_t *public_key) {
    // 实际实现应使用RSA或ECDSA验证
    // 这里仅作示意

    // 1. 计算固件哈希
    uint32_t calculated_hash = calculate_hash(firmware, header->firmware_size);

    // 2. 使用公钥验证签名
    int valid = rsa_verify(public_key, header->signature, 
                          (uint8_t*)&calculated_hash, sizeof(calculated_hash));

    return valid;
}

// 安全启动流程
void secure_boot(void) {
    firmware_header_t *header = (firmware_header_t*)FIRMWARE_HEADER_ADDR;
    uint8_t *firmware = (uint8_t*)FIRMWARE_START_ADDR;
    uint8_t *public_key = (uint8_t*)PUBLIC_KEY_ADDR;

    // 验证固件签名
    if (!verify_firmware_signature(header, firmware, public_key)) {
        // 签名验证失败,拒绝启动
        error_handler("Firmware signature verification failed");
        while(1);  // 停止启动
    }

    // 验证固件版本(防回滚)
    if (header->firmware_version < get_minimum_version()) {
        error_handler("Firmware version too old");
        while(1);
    }

    // 签名验证成功,跳转到应用程序
    jump_to_application(FIRMWARE_START_ADDR);
}

代码说明: - 验证固件数字签名 - 检查固件版本防止回滚 - 验证失败时拒绝启动 - 实际应用需使用成熟的加密库

示例2:安全的数据存储

#include <stdint.h>
#include <string.h>

// 加密存储结构
typedef struct {
    uint8_t iv[16];           // 初始化向量
    uint8_t encrypted_data[256];
    uint8_t mac[32];          // 消息认证码
} secure_storage_t;

// 安全存储数据
int secure_store_data(const uint8_t *data, uint32_t len,
                     const uint8_t *key) {
    secure_storage_t storage;

    // 1. 生成随机IV
    generate_random(storage.iv, sizeof(storage.iv));

    // 2. 使用AES-CBC加密数据
    aes_cbc_encrypt(data, len, key, storage.iv,
                   storage.encrypted_data);

    // 3. 计算MAC保证完整性
    hmac_sha256(storage.encrypted_data, len, key, storage.mac);

    // 4. 写入存储
    return write_to_flash(&storage, sizeof(storage));
}

// 安全读取数据
int secure_read_data(uint8_t *data, uint32_t len,
                    const uint8_t *key) {
    secure_storage_t storage;

    // 1. 从存储读取
    if (read_from_flash(&storage, sizeof(storage)) != 0) {
        return -1;
    }

    // 2. 验证MAC
    uint8_t calculated_mac[32];
    hmac_sha256(storage.encrypted_data, len, key, calculated_mac);

    if (memcmp(storage.mac, calculated_mac, sizeof(storage.mac)) != 0) {
        // MAC验证失败,数据可能被篡改
        return -1;
    }

    // 3. 解密数据
    aes_cbc_decrypt(storage.encrypted_data, len, key,
                   storage.iv, data);

    return 0;
}

代码说明: - 使用AES加密保护机密性 - 使用HMAC保护完整性 - 使用随机IV防止模式攻击 - 先验证MAC再解密

示例3:输入验证

#include <stdint.h>
#include <stdbool.h>
#include <ctype.h>

// 验证用户名(只允许字母数字和下划线)
bool validate_username(const char *username, uint32_t max_len) {
    if (username == NULL) {
        return false;
    }

    uint32_t len = 0;
    while (username[len] != '\0') {
        // 检查长度
        if (len >= max_len) {
            return false;
        }

        // 检查字符合法性
        char c = username[len];
        if (!isalnum(c) && c != '_') {
            return false;
        }

        len++;
    }

    // 检查最小长度
    if (len < 3) {
        return false;
    }

    return true;
}

// 安全的字符串复制
void safe_string_copy(char *dest, const char *src,
                     uint32_t dest_size) {
    if (dest == NULL || src == NULL || dest_size == 0) {
        return;
    }

    uint32_t i;
    for (i = 0; i < dest_size - 1 && src[i] != '\0'; i++) {
        dest[i] = src[i];
    }
    dest[i] = '\0';  // 确保字符串终止
}

// 验证数值范围
bool validate_range(int32_t value, int32_t min, int32_t max) {
    return (value >= min && value <= max);
}

// 使用示例
void process_user_input(const char *username, int32_t age) {
    // 验证用户名
    if (!validate_username(username, 32)) {
        log_error("Invalid username");
        return;
    }

    // 验证年龄范围
    if (!validate_range(age, 0, 150)) {
        log_error("Invalid age");
        return;
    }

    // 安全复制
    char safe_username[32];
    safe_string_copy(safe_username, username, sizeof(safe_username));

    // 处理验证通过的数据
    process_validated_data(safe_username, age);
}

代码说明: - 严格的输入验证 - 防止缓冲区溢出 - 范围检查 - 字符合法性检查

深入理解

安全与性能的平衡

嵌入式系统资源有限,需要在安全性和性能之间找到平衡。

权衡考虑

  1. 加密算法选择
  2. 对称加密(AES):快速,适合大量数据
  3. 非对称加密(RSA/ECC):慢,适合密钥交换和签名
  4. 哈希算法(SHA-256):中等速度,用于完整性验证

  5. 硬件加速

  6. 使用硬件加密引擎
  7. 降低CPU负载
  8. 提高加密性能

  9. 安全级别分层

  10. 关键数据:高安全级别
  11. 一般数据:中等安全级别
  12. 公开数据:低安全级别

安全开发生命周期

安全应该贯穿整个开发过程。

SDL (Security Development Lifecycle)

  1. 需求阶段
  2. 安全需求分析
  3. 威胁建模
  4. 安全目标定义

  5. 设计阶段

  6. 安全架构设计
  7. 安全机制选择
  8. 设计审查

  9. 实现阶段

  10. 安全编码规范
  11. 代码审查
  12. 静态分析

  13. 测试阶段

  14. 安全测试
  15. 渗透测试
  16. 漏洞扫描

  17. 部署阶段

  18. 安全配置
  19. 密钥管理
  20. 安全更新机制

  21. 维护阶段

  22. 漏洞监控
  23. 安全补丁
  24. 事件响应

常见安全误区

误区1:隐藏算法可以提高安全性 - 错误:自己发明加密算法 - 正确:使用公开的标准算法,保护密钥

误区2:物理隔离就是安全的 - 错误:认为没有网络连接就安全 - 错误:忽视物理攻击和供应链攻击 - 正确:实施多层防护

误区3:安全是一次性工作 - 错误:产品发布后不再关注安全 - 正确:持续监控、更新和改进

误区4:安全功能可以后加 - 错误:先实现功能,后考虑安全 - 正确:从设计阶段就考虑安全

误区5:只关注外部威胁 - 错误:忽视内部威胁和供应链 - 正确:全面的威胁分析

常见问题

Q1: 嵌入式系统安全和IT系统安全有什么区别?

A: 主要区别包括:

嵌入式系统特点: - 资源受限(CPU、内存、存储) - 实时性要求高 - 长生命周期(难以更新) - 物理接触风险高 - 专用硬件和协议

IT系统特点: - 资源相对充足 - 可以频繁更新 - 标准化程度高 - 主要面对网络威胁

安全策略差异: - 嵌入式:更依赖硬件安全、轻量级算法 - IT系统:更依赖软件防护、复杂的安全机制

Q2: 如何选择合适的加密算法?

A: 选择加密算法需要考虑:

对称加密: - AES:标准选择,硬件支持好 - ChaCha20:软件实现快,适合无硬件加速的场景

非对称加密: - RSA:成熟稳定,但计算量大 - ECC:密钥短,性能好,推荐用于嵌入式

哈希算法: - SHA-256:标准选择 - SHA-3:新标准,更安全

选择建议: - 优先使用标准算法 - 考虑硬件加速支持 - 平衡安全性和性能 - 关注算法的生命周期

Q3: 什么是安全芯片?何时需要使用?

A: 安全芯片是专门用于安全功能的硬件模块。

类型: - SE (Secure Element):独立的安全芯片 - TEE (Trusted Execution Environment):CPU内的安全区域 - TPM (Trusted Platform Module):可信平台模块

功能: - 安全密钥存储 - 加密运算加速 - 安全启动 - 防篡改保护

使用场景: - 支付设备 - 身份认证 - 高价值数据保护 - 需要认证的产品

Q4: 如何实现安全的固件更新?

A: 安全固件更新的关键要素:

1. 固件签名 - 使用私钥签名固件 - 设备验证签名后才更新

2. 加密传输 - 使用TLS/DTLS加密通道 - 防止中间人攻击

3. 版本控制 - 防止回滚到旧版本 - 记录版本历史

4. 原子更新 - 更新失败能回滚 - 使用双分区或备份

5. 完整性验证 - 下载后验证哈希 - 写入后再次验证

Q5: 如何进行安全测试?

A: 安全测试应该包括:

1. 静态分析 - 代码审查 - 使用静态分析工具 - 检查编码规范

2. 动态测试 - 模糊测试(Fuzzing) - 边界测试 - 异常输入测试

3. 渗透测试 - 模拟攻击场景 - 漏洞扫描 - 社会工程测试

4. 硬件测试 - 侧信道分析 - 故障注入 - 物理篡改测试

5. 合规测试 - 标准符合性测试 - 认证测试 - 第三方审计

总结

核心要点

  1. 安全威胁多样化
  2. 物理攻击、软件攻击、网络攻击
  3. 供应链安全不容忽视
  4. 需要全面的威胁分析

  5. 安全需求分析

  6. CIA三要素:机密性、完整性、可用性
  7. 使用STRIDE等模型进行威胁建模
  8. 根据风险确定安全等级

  9. 安全设计原则

  10. 纵深防御:多层防护
  11. 最小权限:限制访问范围
  12. 默认安全:安全的默认配置
  13. 故障安全:失败时保持安全
  14. 开放设计:使用标准算法
  15. 完全中介:每次都验证

  16. 安全标准

  17. 通用标准:ISO 27001、Common Criteria
  18. 行业标准:ISO 26262、IEC 62443
  19. 编码标准:MISRA C、CERT C

  20. 基本防护措施

  21. 安全启动和固件保护
  22. 安全通信和密钥管理
  23. 访问控制和安全日志
  24. 输入验证和安全编码

实践建议

开发阶段: - 从设计开始就考虑安全 - 遵循安全编码规范 - 进行代码审查和测试 - 使用安全开发工具

部署阶段: - 禁用调试接口 - 使用强密码和密钥 - 启用所有安全功能 - 建立安全更新机制

维护阶段: - 持续监控安全事件 - 及时修补漏洞 - 定期安全审计 - 保持安全意识

下一步学习

掌握安全概述后,建议继续学习: - 功能安全基础(IEC 61508) - 信息安全基础知识 - 安全启动与固件验证 - 密钥管理与存储 - 安全通信协议实现

延伸阅读

推荐书籍

  • 《嵌入式系统安全》- 全面的嵌入式安全指南
  • 《物联网安全》- IoT设备安全实践
  • 《密码工程》- 密码学实践应用
  • 《安全编码》- 安全编程实践

在线资源

相关文章

  • 功能安全基础(IEC 61508)- 功能安全标准详解
  • 安全启动与固件验证 - 实现安全启动
  • 密钥管理与存储 - 密钥安全管理
  • 安全通信协议实现 - TLS/DTLS应用

参考资料

  1. ISO/IEC 27001:2013 - 信息安全管理体系
  2. Common Criteria v3.1 - 信息技术安全评估标准
  3. NIST SP 800-53 - 安全和隐私控制
  4. OWASP IoT Top 10 - IoT十大安全风险
  5. IEC 62443 - 工业自动化和控制系统安全

思考题

  1. 分析你正在开发的嵌入式系统,识别主要的安全威胁
  2. 使用STRIDE模型对系统进行威胁建模
  3. 评估当前系统的安全等级,制定改进计划
  4. 设计一个安全启动方案,包括签名验证和防回滚

实践项目

设计一个物联网设备的安全方案,要求: - 完整的威胁分析 - 安全架构设计 - 安全启动流程 - 安全通信机制 - 密钥管理方案 - 安全更新机制

下一步:建议学习 功能安全基础(IEC 61508)