第 15 章 安全与保护
《计算机系统:系统架构与操作系统的高度集成》
Umakishore Ramachandran & William D. Leahy Jr. 著
15.1 计算机安全概述
计算机安全(Computer Security):
保护计算机系统和数据免受未授权访问、使用、修改或破坏
安全的三个核心目标(CIA 三元组):
C(Confidentiality,机密性):
信息只对授权用户可见
防止未授权的信息泄露
例:加密通信、访问控制
I(Integrity,完整性):
信息不被未授权修改
保证数据的准确性和一致性
例:数字签名、哈希校验
A(Availability,可用性):
授权用户可以访问信息和系统
防止拒绝服务攻击
例:冗余系统、备份
安全威胁的来源:
外部攻击者:黑客、网络犯罪分子
内部威胁:恶意员工、误操作
恶意软件:病毒、蠕虫、木马、勒索软件
物理威胁:硬件损坏、自然灾害
安全机制:
认证(Authentication):验证身份(你是谁?)
授权(Authorization):控制访问权限(你能做什么?)
加密(Encryption):保护数据机密性
审计(Auditing):记录安全事件
入侵检测(Intrusion Detection):检测攻击行为
15.2 安全威胁与攻击类型
15.2.1 缓冲区溢出
缓冲区溢出(Buffer Overflow):
向缓冲区写入超过其容量的数据
覆盖相邻内存区域(包括返回地址)
攻击者可以控制程序执行流程
栈溢出攻击原理:
函数调用时的栈布局:
高地址
┌─────────────────────────────┐
│ 调用者的栈帧 │
├─────────────────────────────┤
│ 返回地址(攻击目标!) │ ← 攻击者想覆盖这里
├─────────────────────────────┤
│ 保存的 RBP │
├─────────────────────────────┤
│ buffer[64] │ ← 从这里开始写
├─────────────────────────────┤ 低地址
如果 input 超过 64 字节:
→ 覆盖保存的 RBP
→ 覆盖返回地址(指向攻击者的代码)
→ 函数返回时,跳转到攻击者的代码执行!
防御措施:
1. 使用安全函数(strncpy, snprintf, strlcpy)
2. 栈金丝雀(Stack Canary):在返回地址前放置随机值
3. ASLR(地址空间布局随机化):随机化内存布局
4. NX/DEP(数据执行保护):数据区域不可执行
5. CFI(控制流完整性):防止控制流劫持
6. 代码审查和静态分析工具
c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* 缓冲区溢出漏洞演示 */
/* 危险的函数(存在缓冲区溢出漏洞)*/
void vulnerable_function(char *input) {
char buffer[64];
/* 危险!没有检查输入长度 */
/* strcpy(buffer, input); */ /* 注释掉,避免实际崩溃 */
/* 安全版本 */
strncpy(buffer, input, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
printf("输入: %s\n", buffer);
}
/* 演示各种安全函数 */
void safe_string_operations() {
printf("=== 安全字符串操作 ===\n\n");
char dest[16];
const char *src = "Hello, World! This is a very long string!";
/* 危险:strcpy 不检查长度 */
/* strcpy(dest, src); */ /* 缓冲区溢出! */
/* 安全:strncpy 限制长度 */
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
printf("strncpy 结果: '%s'\n", dest);
/* 更安全:snprintf */
char buf[32];
int n = snprintf(buf, sizeof(buf), "Value: %d, Name: %s", 42, "Alice");
printf("snprintf 结果: '%s'(写入%d字节)\n", buf, n);
/* 检查 snprintf 是否截断 */
if (n >= (int)sizeof(buf)) {
printf("警告:输出被截断!\n");
}
/* 整数溢出导致的缓冲区溢出 */
printf("\n整数溢出示例:\n");
unsigned int size = 0xFFFFFFFF; /* 最大无符号整数 */
unsigned int total = size + 1; /* 溢出为0! */
printf("size=%u, size+1=%u(溢出!)\n", size, total);
/* 如果用 total 分配内存,会分配0字节,然后写入大量数据 → 溢出 */
}
/* 栈金丝雀演示(GCC 自动添加)*/
/* 编译时加 -fstack-protector 选项 */
void stack_canary_demo() {
printf("\n=== 栈金丝雀保护 ===\n");
printf("GCC 使用 -fstack-protector 自动添加栈金丝雀\n");
printf("在返回地址前放置随机值\n");
printf("函数返回前检查金丝雀值是否被修改\n");
printf("如果被修改,调用 __stack_chk_fail() 终止程序\n");
}
/* 格式字符串漏洞 */
void format_string_demo() {
printf("\n=== 格式字符串漏洞 ===\n\n");
char user_input[] = "Hello, World!";
/* 安全:格式字符串是固定的 */
printf("安全: %s\n", user_input);
/* 危险:用户输入直接作为格式字符串 */
/* printf(user_input); */ /* 如果 user_input 包含 %s, %x 等,会泄露内存! */
/* 演示格式字符串的危险 */
char malicious[] = "正常文本 %x %x %x";
printf("危险示例(如果直接 printf(malicious)):\n");
printf(" 会打印栈上的数据: ");
printf(malicious); /* 这会打印栈上的值! */
printf("\n");
}
int main() {
safe_string_operations();
stack_canary_demo();
format_string_demo();
return 0;
}
15.2.2~15.2.4 病毒、木马、拒绝服务
病毒(Virus):
自我复制的恶意代码,附着在合法程序上
需要用户执行宿主程序才能传播
类型:
文件病毒:感染可执行文件
引导扇区病毒:感染磁盘引导扇区
宏病毒:感染 Office 文档中的宏
多态病毒:每次复制时改变自身代码(逃避检测)
蠕虫(Worm):
自我复制,通过网络自动传播
不需要宿主程序
例:WannaCry(2017),利用 SMB 漏洞传播
特洛伊木马(Trojan Horse):
伪装成合法程序的恶意软件
用户主动安装,但包含隐藏的恶意功能
例:键盘记录器、远程控制工具(RAT)
勒索软件(Ransomware):
加密用户文件,要求支付赎金
例:WannaCry, REvil, LockBit
拒绝服务攻击(DoS/DDoS):
使目标系统无法为合法用户提供服务
DoS(Denial of Service):单一攻击源
DDoS(Distributed DoS):多个攻击源(僵尸网络)
攻击方式:
SYN Flood:发送大量 SYN 包,耗尽服务器连接资源
UDP Flood:发送大量 UDP 包,耗尽带宽
HTTP Flood:发送大量 HTTP 请求,耗尽服务器资源
防御:
速率限制(Rate Limiting)
IP 黑名单
CDN 和流量清洗
SYN Cookie(防止 SYN Flood)
15.3 操作系统保护机制
15.3.1 访问控制
访问控制(Access Control):
控制谁可以访问什么资源,以及如何访问
访问控制模型:
1. DAC(Discretionary Access Control,自主访问控制):
资源所有者决定谁可以访问
Unix 权限模型(rwxrwxrwx)
优点:灵活,用户可以控制自己的资源
缺点:权限可以传递(特洛伊木马问题)
2. MAC(Mandatory Access Control,强制访问控制):
系统强制执行访问策略,用户无法修改
基于安全标签(如:绝密、机密、秘密、公开)
Bell-LaPadula 模型(保密性):
- 不上读(No Read Up):主体不能读取高于自己级别的客体
- 不下写(No Write Down):主体不能写入低于自己级别的客体
Biba 模型(完整性):
- 不下读(No Read Down):主体不能读取低于自己级别的客体
- 不上写(No Write Up):主体不能写入高于自己级别的客体
实现:SELinux, AppArmor
3. RBAC(Role-Based Access Control,基于角色的访问控制):
权限分配给角色,用户分配到角色
例:
角色:管理员、普通用户、访客
管理员:可以读写所有文件
普通用户:可以读写自己的文件
访客:只能读取公共文件
优点:易于管理(修改角色权限,自动影响所有该角色用户)
15.3.2~15.3.4 保护域、访问控制矩阵、能力机制
c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* 访问控制矩阵实现 */
#define MAX_SUBJECTS 5 /* 主体(用户/进程)数量 */
#define MAX_OBJECTS 5 /* 客体(文件/资源)数量 */
/* 访问权限位 */
#define PERM_READ (1 << 0) /* 读 */
#define PERM_WRITE (1 << 1) /* 写 */
#define PERM_EXECUTE (1 << 2) /* 执行 */
#define PERM_OWNER (1 << 3) /* 所有者 */
/* 访问控制矩阵 */
int acm[MAX_SUBJECTS][MAX_OBJECTS] = {
/* file1 file2 file3 printer database */
{PERM_READ|PERM_WRITE|PERM_OWNER, PERM_READ, 0, PERM_READ|PERM_WRITE, PERM_READ}, /* alice */
{PERM_READ, PERM_READ|PERM_WRITE|PERM_OWNER, PERM_READ, 0, PERM_READ}, /* bob */
{PERM_READ, PERM_READ, PERM_READ|PERM_WRITE|PERM_OWNER, PERM_READ|PERM_WRITE, PERM_READ}, /* charlie */
{PERM_READ|PERM_WRITE, PERM_READ|PERM_WRITE, PERM_READ|PERM_WRITE, PERM_READ|PERM_WRITE|PERM_OWNER, PERM_READ|PERM_WRITE}, /* dave */
{PERM_READ|PERM_WRITE, PERM_READ|PERM_WRITE, PERM_READ|PERM_WRITE, PERM_READ|PERM_WRITE, PERM_READ|PERM_WRITE|PERM_OWNER} /* admin */
};
const char *subjects[] = {"alice", "bob", "charlie", "dave", "admin"};
const char *objects[] = {"file1", "file2", "file3", "printer", "database"};
/* 访问控制检查 */
int check_access(int subject, int object, int permission) {
return (acm[subject][object] & permission) != 0;
}
/* 打印访问控制矩阵 */
void print_acm() {
printf("=== 访问控制矩阵 ===\n\n");
printf("%-10s", "");
for (int j = 0; j < MAX_OBJECTS; j++) {
printf("%-12s", objects[j]);
}
printf("\n");
for (int i = 0; i < MAX_SUBJECTS; i++) {
printf("%-10s", subjects[i]);
for (int j = 0; j < MAX_OBJECTS; j++) {
char perms[8] = "";
if (acm[i][j] & PERM_READ) strcat(perms, "r");
if (acm[i][j] & PERM_WRITE) strcat(perms, "w");
if (acm[i][j] & PERM_EXECUTE) strcat(perms, "x");
if (acm[i][j] & PERM_OWNER) strcat(perms, "o");
if (!acm[i][j]) strcat(perms, "-");
printf("%-12s", perms);
}
printf("\n");
}
}
/* 访问控制检查演示 */
void access_control_demo() {
printf("\n=== 访问控制检查 ===\n\n");
struct {
int subject, object, permission;
const char *perm_name;
} tests[] = {
{0, 0, PERM_READ, "读"}, /* alice 读 file1 */
{0, 0, PERM_WRITE, "写"}, /* alice 写 file1 */
{1, 0, PERM_WRITE, "写"}, /* bob 写 file1 */
{0, 2, PERM_READ, "读"}, /* alice 读 file3 */
{4, 4, PERM_WRITE, "写"}, /* admin 写 database */
{1, 3, PERM_READ, "读"}, /* bob 读 printer */
};
for (int i = 0; i < 6; i++) {
int result = check_access(tests[i].subject, tests[i].object,
tests[i].permission);
printf("%s %s %s: %s\n",
subjects[tests[i].subject],
tests[i].perm_name,
objects[tests[i].object],
result ? "✓ 允许" : "✗ 拒绝");
}
}
/* Unix 权限模型 */
void unix_permissions_demo() {
printf("\n=== Unix 权限模型 ===\n\n");
/* 权限:rwxrwxrwx(所有者/组/其他)*/
int modes[] = {0755, 0644, 0600, 0777, 0000};
const char *descriptions[] = {
"可执行程序(所有者rwx,组r-x,其他r-x)",
"普通文件(所有者rw-,组r--,其他r--)",
"私有文件(所有者rw-,组---,其他---)",
"完全开放(所有人rwx)",
"完全禁止(所有人---)"
};
for (int i = 0; i < 5; i++) {
int mode = modes[i];
printf("权限 %04o: %c%c%c%c%c%c%c%c%c %s\n",
mode,
(mode & 0400) ? 'r' : '-',
(mode & 0200) ? 'w' : '-',
(mode & 0100) ? 'x' : '-',
(mode & 0040) ? 'r' : '-',
(mode & 0020) ? 'w' : '-',
(mode & 0010) ? 'x' : '-',
(mode & 0004) ? 'r' : '-',
(mode & 0002) ? 'w' : '-',
(mode & 0001) ? 'x' : '-',
descriptions[i]);
}
printf("\n特殊权限位:\n");
printf(" SetUID (4000): 以文件所有者权限执行(如 /usr/bin/passwd)\n");
printf(" SetGID (2000): 以文件所属组权限执行\n");
printf(" Sticky (1000): 只有文件所有者才能删除(/tmp 目录使用)\n");
}
/* 能力(Capability)机制 */
void capability_demo() {
printf("\n=== 能力(Capability)机制 ===\n\n");
printf("传统 Unix 权限:\n");
printf(" root 用户拥有所有权限(全有或全无)\n");
printf(" 普通用户权限受限\n\n");
printf("Linux Capabilities(细粒度权限):\n");
printf(" CAP_NET_BIND_SERVICE: 绑定 < 1024 的端口\n");
printf(" CAP_NET_RAW: 使用原始套接字\n");
printf(" CAP_SYS_ADMIN: 系统管理操作\n");
printf(" CAP_CHOWN: 修改文件所有者\n");
printf(" CAP_KILL: 向任意进程发送信号\n");
printf(" CAP_SYS_TIME: 修改系统时间\n\n");
printf("优点:\n");
printf(" 最小权限原则:程序只获得所需的最小权限\n");
printf(" 即使程序被攻击,攻击者也只能获得有限权限\n\n");
printf("示例:Web 服务器\n");
printf(" 传统:以 root 运行(危险!)\n");
printf(" 改进:只给予 CAP_NET_BIND_SERVICE(绑定80端口)\n");
printf(" 其他操作以普通用户权限执行\n");
}
int main() {
print_acm();
access_control_demo();
unix_permissions_demo();
capability_demo();
return 0;
}
15.4 密码学基础
15.4.1 对称加密
c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
/* 对称加密(Symmetric Encryption):
加密和解密使用相同的密钥
速度快,适合大量数据加密 */
/* 简单的 XOR 加密(仅用于演示,实际不安全)*/
void xor_encrypt(uint8_t *data, int len, uint8_t key) {
for (int i = 0; i < len; i++) {
data[i] ^= key;
}
}
/* 凯撒密码(历史上的对称加密)*/
void caesar_encrypt(char *text, int shift) {
for (int i = 0; text[i]; i++) {
if (text[i] >= 'a' && text[i] <= 'z') {
text[i] = 'a' + (text[i] - 'a' + shift) % 26;
} else if (text[i] >= 'A' && text[i] <= 'Z') {
text[i] = 'A' + (text[i] - 'A' + shift) % 26;
}
}
}
void caesar_decrypt(char *text, int shift) {
caesar_encrypt(text, 26 - shift); /* 反向移位 */
}
/* 现代对称加密算法(概念介绍)*/
void symmetric_encryption_demo() {
printf("=== 对称加密演示 ===\n\n");
/* XOR 加密 */
printf("1. XOR 加密(简单演示):\n");
uint8_t data[] = "Hello, World!";
uint8_t key = 0x42;
int len = strlen((char *)data);
printf(" 原文: %s\n", data);
xor_encrypt(data, len, key);
printf(" 加密: ");
for (int i = 0; i < len; i++) printf("%02X ", data[i]);
printf("\n");
xor_encrypt(data, len, key); /* 再次 XOR 解密 */
printf(" 解密: %s\n\n", data);
/* 凯撒密码 */
printf("2. 凯撒密码(移位3):\n");
char text[] = "Hello World";
printf(" 原文: %s\n", text);
caesar_encrypt(text, 3);
printf(" 加密: %s\n", text);
caesar_decrypt(text, 3);
printf(" 解密: %s\n\n", text);
/* 现代算法介绍 */
printf("3. 现代对称加密算法:\n");
printf(" AES(Advanced Encryption Standard):\n");
printf(" 密钥长度:128/192/256 位\n");
printf(" 块大小:128 位\n");
printf(" 模式:ECB, CBC, CTR, GCM\n");
printf(" 用途:文件加密、TLS、磁盘加密\n\n");
printf(" ChaCha20:\n");
printf(" 流密码,速度快\n");
printf(" 用途:TLS 1.3(与 Poly1305 配合)\n\n");
printf(" 3DES(已废弃):\n");
printf(" 三次 DES 加密,安全但慢\n");
printf(" 已被 AES 取代\n");
}
15.4.2 非对称加密
c
/* 非对称加密(Asymmetric Encryption):
使用公钥加密,私钥解密(或私钥签名,公钥验证)
速度慢,适合密钥交换和数字签名 */
/* RSA 算法原理(简化演示)*/
void rsa_concept_demo() {
printf("\n=== 非对称加密(RSA)概念 ===\n\n");
printf("RSA 密钥生成:\n");
printf(" 1. 选择两个大素数 p 和 q\n");
printf(" 2. 计算 n = p × q(模数)\n");
printf(" 3. 计算 φ(n) = (p-1)(q-1)\n");
printf(" 4. 选择 e(公钥指数),满足 gcd(e, φ(n)) = 1\n");
printf(" 5. 计算 d(私钥指数),满足 e×d ≡ 1 (mod φ(n))\n");
printf(" 公钥:(n, e),私钥:(n, d)\n\n");
/* 小数字演示(实际RSA使用2048位以上的数)*/
printf("小数字演示(实际RSA使用2048位以上):\n");
long p = 61, q = 53;
long n = p * q; /* 3233 */
long phi_n = (p-1) * (q-1); /* 3120 */
long e = 17; /* 公钥指数 */
long d = 2753; /* 私钥指数(17 × 2753 mod 3120 = 1)*/
printf(" p=%ld, q=%ld, n=%ld, φ(n)=%ld\n", p, q, n, phi_n);
printf(" 公钥: (n=%ld, e=%ld)\n", n, e);
printf(" 私钥: (n=%ld, d=%ld)\n\n", n, d);
/* 加密:c = m^e mod n */
long m = 65; /* 明文 */
long c = 1;
for (int i = 0; i < e; i++) c = (c * m) % n;
printf(" 加密: m=%ld → c=%ld(c = m^e mod n)\n", m, c);
/* 解密:m = c^d mod n */
long decrypted = 1;
for (int i = 0; i < d; i++) decrypted = (decrypted * c) % n;
printf(" 解密: c=%ld → m=%ld(m = c^d mod n)\n\n", c, decrypted);
printf("现代非对称加密算法:\n");
printf(" RSA:最广泛使用,密钥长度2048/4096位\n");
printf(" ECC(椭圆曲线密码):更短的密钥,更高的安全性\n");
printf(" ECDSA:数字签名\n");
printf(" ECDH:密钥交换\n");
printf(" 用途:TLS握手、数字签名、密钥交换\n");
}
15.4.3 数字签名与证书
c
/* 数字签名(Digital Signature):
使用私钥签名,公钥验证
保证数据完整性和不可否认性 */
/* 哈希函数(Hash Function):
将任意长度的数据映射为固定长度的摘要
单向函数(不可逆)
碰撞抵抗(不同输入产生不同输出)*/
/* 简单哈希函数(仅用于演示,实际使用 SHA-256 等)*/
unsigned int simple_hash(const char *str) {
unsigned int hash = 5381;
int c;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
}
return hash;
}
/* 密码存储(使用哈希+盐值)*/
typedef struct {
char username[32];
char salt[16];
unsigned int password_hash;
} UserRecord;
void store_password(UserRecord *record, const char *username,
const char *password) {
strncpy(record->username, username, sizeof(record->username)-1);
/* 生成随机盐值(实际应使用密码学安全的随机数)*/
for (int i = 0; i < 15; i++) {
record->salt[i] = 'a' + (rand() % 26);
}
record->salt[15] = '\0';
/* 计算 hash(password + salt) */
char salted[256];
snprintf(salted, sizeof(salted), "%s%s", password, record->salt);
record->password_hash = simple_hash(salted);
printf("存储用户: %s\n", record->username);
printf(" 盐值: %s\n", record->salt);
printf(" 哈希: %u\n", record->password_hash);
}
int verify_password(UserRecord *record, const char *password) {
char salted[256];
snprintf(salted, sizeof(salted), "%s%s", password, record->salt);
return simple_hash(salted) == record->password_hash;
}
void cryptography_demo() {
printf("\n=== 密码学综合演示 ===\n\n");
symmetric_encryption_demo();
rsa_concept_demo();
printf("\n=== 数字签名概念 ===\n\n");
printf("数字签名过程:\n");
printf(" 签名方:\n");
printf(" 1. 计算消息的哈希值: h = Hash(message)\n");
printf(" 2. 用私钥加密哈希值: sig = Encrypt(h, private_key)\n");
printf(" 3. 发送: message + sig\n\n");
printf(" 验证方:\n");
printf(" 1. 用公钥解密签名: h' = Decrypt(sig, public_key)\n");
printf(" 2. 计算消息的哈希值: h = Hash(message)\n");
printf(" 3. 比较 h == h'(相等则签名有效)\n\n");
printf("=== 密码存储演示 ===\n\n");
UserRecord user;
store_password(&user, "alice", "mypassword123");
printf("\n验证密码:\n");
printf(" 正确密码: %s\n",
verify_password(&user, "mypassword123") ? "✓ 验证成功" : "✗ 验证失败");
printf(" 错误密码: %s\n",
verify_password(&user, "wrongpassword") ? "✓ 验证成功" : "✗ 验证失败");
printf("\n为什么需要盐值?\n");
printf(" 没有盐值:相同密码产生相同哈希(彩虹表攻击)\n");
printf(" 有盐值:相同密码产生不同哈希(防止彩虹表)\n");
}
int main() {
cryptography_demo();
return 0;
}
15.5 安全操作系统设计原则
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* 安全设计原则(Saltzer & Schroeder,1975)*/
void security_principles_demo() {
printf("=== 安全操作系统设计原则 ===\n\n");
printf("1. 最小权限原则(Principle of Least Privilege):\n");
printf(" 每个程序和用户只拥有完成工作所需的最小权限\n");
printf(" 例:Web服务器不需要root权限\n");
printf(" 数据库用户只需要访问特定表的权限\n\n");
printf("2. 默认拒绝(Default Deny):\n");
printf(" 默认情况下拒绝所有访问,只允许明确授权的操作\n");
printf(" 例:防火墙默认拒绝所有入站连接\n");
printf(" 文件系统默认不允许访问\n\n");
printf("3. 完全仲裁(Complete Mediation):\n");
printf(" 每次访问都必须经过权限检查\n");
printf(" 不能缓存权限(权限可能已被撤销)\n");
printf(" 例:每次文件访问都检查权限,而不是只在打开时检查\n\n");
printf("4. 开放设计(Open Design):\n");
printf(" 安全性不依赖于设计的保密性\n");
printf(" '通过隐藏实现安全'是不可靠的\n");
printf(" 例:AES 算法是公开的,安全性依赖于密钥的保密\n\n");
printf("5. 权限分离(Separation of Privilege):\n");
printf(" 需要多个条件才能获得权限\n");
printf(" 例:双因素认证(密码+手机验证码)\n");
printf(" 核武器发射需要两个人同时操作\n\n");
printf("6. 最小公共机制(Least Common Mechanism):\n");
printf(" 最小化多个用户共享的机制\n");
printf(" 共享越少,安全风险越小\n");
printf(" 例:不同用户使用独立的进程,而不是共享进程\n\n");
printf("7. 心理可接受性(Psychological Acceptability):\n");
printf(" 安全机制不应该使资源比没有安全机制时更难访问\n");
printf(" 安全性和易用性需要平衡\n");
printf(" 例:密码太复杂,用户会写在纸上(反而不安全)\n\n");
printf("现代安全技术:\n");
printf(" ASLR(地址空间布局随机化):\n");
printf(" 随机化内存布局,防止利用固定地址\n");
printf(" 使缓冲区溢出攻击更难成功\n\n");
printf(" DEP/NX(数据执行保护):\n");
printf(" 数据区域不可执行\n");
printf(" 防止代码注入攻击\n\n");
printf(" Stack Canary(栈金丝雀):\n");
printf(" 在返回地址前放置随机值\n");
printf(" 检测栈溢出攻击\n\n");
printf(" CFI(控制流完整性):\n");
printf(" 限制程序的控制流转移\n");
printf(" 防止 ROP(Return-Oriented Programming)攻击\n\n");
printf(" 沙箱(Sandbox):\n");
printf(" 隔离不可信代码的执行环境\n");
printf(" 例:浏览器沙箱、Docker 容器\n\n");
printf(" SELinux/AppArmor(强制访问控制):\n");
printf(" 基于策略的强制访问控制\n");
printf(" 即使程序被攻击,也限制其能做的事情\n");
}
/* 安全编程实践 */
void secure_coding_demo() {
printf("\n=== 安全编程实践 ===\n\n");
printf("1. 输入验证:\n");
printf(" 永远不要信任用户输入\n");
printf(" 验证长度、类型、范围、格式\n\n");
/* 输入验证示例 */
char user_input[] = "123abc";
int is_valid_number = 1;
for (int i = 0; user_input[i]; i++) {
if (user_input[i] < '0' || user_input[i] > '9') {
is_valid_number = 0;
break;
}
}
printf(" 输入 '%s' 是否为纯数字: %s\n\n",
user_input, is_valid_number ? "是" : "否");
printf("2. 错误处理:\n");
printf(" 检查所有函数的返回值\n");
printf(" 不要泄露敏感信息(如内存地址、系统路径)\n\n");
printf("3. 内存安全:\n");
printf(" 使用安全函数(strncpy, snprintf)\n");
printf(" 检查整数溢出\n");
printf(" 释放后不使用(Use-After-Free)\n");
printf(" 双重释放(Double-Free)\n\n");
printf("4. 密码学:\n");
printf(" 使用经过验证的密码库(OpenSSL, libsodium)\n");
printf(" 不要自己实现密码算法\n");
printf(" 使用强随机数生成器\n");
printf(" 密码存储使用 bcrypt/scrypt/Argon2\n\n");
printf("5. 最小权限:\n");
printf(" 程序以最低必要权限运行\n");
printf(" 尽早放弃不需要的权限\n\n");
printf("6. 安全默认值:\n");
printf(" 默认配置应该是安全的\n");
printf(" 用户需要主动降低安全性\n");
}
int main() {
security_principles_demo();
secure_coding_demo();
return 0;
}
本章小结
| 知识点 | 核心内容 | 重要程度 |
|---|---|---|
| CIA三元组 | 机密性/完整性/可用性 | ⭐⭐⭐⭐⭐ |
| 缓冲区溢出 | 覆盖返回地址,控制执行流程 | ⭐⭐⭐⭐⭐ |
| 防御措施 | 安全函数/栈金丝雀/ASLR/NX | ⭐⭐⭐⭐⭐ |
| 病毒vs蠕虫 | 需要宿主vs自动传播 | ⭐⭐⭐⭐ |
| DoS/DDoS | 耗尽资源,使服务不可用 | ⭐⭐⭐⭐ |
| DAC/MAC/RBAC | 三种访问控制模型 | ⭐⭐⭐⭐⭐ |
| 访问控制矩阵 | 主体×客体的权限表 | ⭐⭐⭐⭐⭐ |
| Unix权限 | rwxrwxrwx,SetUID/GID/Sticky | ⭐⭐⭐⭐⭐ |
| 对称加密 | 同一密钥,速度快(AES) | ⭐⭐⭐⭐⭐ |
| 非对称加密 | 公钥加密私钥解密(RSA/ECC) | ⭐⭐⭐⭐⭐ |
| 数字签名 | 私钥签名公钥验证,不可否认 | ⭐⭐⭐⭐⭐ |
| 哈希函数 | 单向,碰撞抵抗(SHA-256) | ⭐⭐⭐⭐⭐ |
| 密码存储 | 哈希+盐值,防彩虹表 | ⭐⭐⭐⭐⭐ |
| 最小权限原则 | 只给必要权限 | ⭐⭐⭐⭐⭐ |
| 安全编程实践 | 输入验证/错误处理/内存安全 | ⭐⭐⭐⭐⭐ |
思考题
- 为什么密码存储需要加盐(Salt)?只用哈希不行吗?
- 对称加密和非对称加密各有什么优缺点?TLS 如何结合两者?
- 最小权限原则为什么重要?举一个违反该原则导致安全问题的例子。
- ASLR 如何防御缓冲区溢出攻击?它有什么局限性?
- MAC(强制访问控制)相比 DAC(自主访问控制)有什么优势?
全书知识点索引
第2章:ISA、LC-2200、数据表示(补码、IEEE 754)、指令类型
第3章:布尔代数、逻辑门、全加器、D触发器、ALU、流水线、冒险
第4章:编译器五阶段、栈帧、调用约定、递归、汇编器、链接器
第5章:Cache(直接映射/组相联/全相联)、替换策略、写策略、虚拟内存、TLB、多级页表
第6章:I/O方式(轮询/中断/DMA)、总线、磁盘调度(FCFS/SSTF/SCAN)、RAID
第7章:中断分类(硬件/软件/异常)、中断向量表、精确中断、LC-2200中断
第8章:进程(PCB/状态/fork/exec)、调度算法(FCFS/SJF/优先级/RR)、线程、上下文切换
第9章:竞态条件、临界区、Peterson算法、TAS/CAS、信号量、管程、条件变量、死锁(四条件/预防/避免/检测)
第10章:内存分配(首次/最佳适应)、分页、TLB、多级页表、缺页处理、页面置换(FIFO/LRU/OPT/Clock)、工作集、抖动
第11章:文件属性、目录结构、inode、物理结构(连续/链式/索引)、空闲空间管理、Unix权限、FAT/ext4/NTFS
第12章:单体内核/微内核/混合内核、系统调用、BIOS/UEFI、GRUB、内核初始化、虚拟化(全/半/硬件辅助)
第13章:OSI七层、TCP/IP、TCP三次握手、套接字编程、CAP定理、RPC、分布式文件系统
第14章:SMP/NUMA/多核、Cache一致性(MESI协议)、目录协议、内存一致性(顺序/TSO)、内存屏障、多处理器调度、工作窃取
第15章:CIA三元组、缓冲区溢出、访问控制(DAC/MAC/RBAC)、对称/非对称加密、数字签名、哈希、安全设计原则
参考文献:Umakishore Ramachandran & William D. Leahy Jr.,《计算机系统:系统架构与操作系统的高度集成》