从零实现一个Web应用防火墙:WAF的核心设计
2026/7/7 4:59:17 网站建设 项目流程

前言

你有没有想过:在用户请求到达你的Web服务器之前,是怎么被检查的?SQL注入、XSS攻击是怎么被拦截的?

Web应用防火墙(WAF) 是Web安全的第一道防线,专门分析HTTP请求,拦截恶意流量。

今天我们从零实现一个WAF的核心功能:

· HTTP请求解析
· 规则引擎(SQL注入/XSS检测)
· 正向/反向代理模式
· 频率限制
· IP黑白名单
· 日志与告警

---

一、WAF核心原理

1. WAF部署模式

```
┌─────────────────────────────────────────────────────────────┐
│ 用户请求 │
└─────────────────────────────────────────────────────────────┘

┌───────┴───────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ 透明代理 │ │ 反向代理 │
│ (网桥模式) │ │ (网关模式) │
└─────────────┘ └─────────────┘
│ │
└───────┬───────┘

┌─────────────┐
│ WAF引擎 │
│ 规则匹配 │
└─────────────┘


┌─────────────┐
│ Web服务器 │
└─────────────┘
```

2. 核心检测规则

攻击类型 检测特征 示例
SQL注入 ' OR '1'='1, UNION SELECT, sleep( ?id=1' OR '1'='1
XSS <script>, onerror=, alert( ?name=<script>alert(1)</script>
路径遍历 ../, .., /etc/passwd ?file=../../etc/passwd
命令注入 ; ls, whoami, $(cat)
SSRF http://169.254.169.254, localhost ?url=http://localhost/admin

---

二、完整代码实现

1. 基础数据结构

```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define MAX_HEADERS 64
#define MAX_HEADER_LEN 512
#define MAX_BODY_LEN 65536
#define MAX_RULES 1000
#define MAX_REQUEST_LEN 65536

// HTTP请求结构
typedef struct http_request {
char method[16];
char uri[1024];
char version[16];
char headers[MAX_HEADERS][MAX_HEADER_LEN];
int header_count;
char body[MAX_BODY_LEN];
int body_len;
char client_ip[32];
int port;
char host[256];
} http_request_t;

// 检测结果
typedef enum {
WAF_ALLOW = 0,
WAF_BLOCK = 1,
WAF_LOG = 2,
WAF_CHALLENGE = 3
} waf_action_t;

// 规则类型
typedef enum {
RULE_SQL_INJECTION = 0,
RULE_XSS,
RULE_PATH_TRAVERSAL,
RULE_CMD_INJECTION,
RULE_SSRF,
RULE_RATE_LIMIT,
RULE_IP_BLACKLIST,
RULE_IP_WHITELIST,
RULE_USER_AGENT,
RULE_HEADER
} rule_type_t;

// WAF规则
typedef struct waf_rule {
int id;
rule_type_t type;
char pattern[512];
char target[64]; // uri, body, header, user-agent, ip
waf_action_t action;
char severity[16];
char message[256];
int enabled;
struct waf_rule *next;
} waf_rule_t;

// 频率限制条目
typedef struct rate_limit_entry {
char key[128]; // ip+uri
int count;
time_t first_request;
time_t last_request;
int blocked;
time_t block_until;
struct rate_limit_entry *next;
} rate_limit_entry_t;

// WAF结构
typedef struct waf_engine {
waf_rule_t *rules;
rate_limit_entry_t *rate_limits;
int rule_count;
int block_count;
int log_count;
int mode; // 0: 学习, 1: 检测, 2: 拦截
char log_dir[256];
pthread_mutex_t mutex;
pthread_t cleanup_thread;
int running;
} waf_engine_t;
```

2. HTTP请求解析

```c
// 创建WAF引擎
waf_engine_t *waf_create(const char *log_dir) {
waf_engine_t *waf = malloc(sizeof(waf_engine_t));
memset(waf, 0, sizeof(waf_engine_t));
waf->mode = 1;
waf->running = 1;
strcpy(waf->log_dir, log_dir);
pthread_mutex_init(&waf->mutex, NULL);

printf("[WAF] 引擎初始化完成\n");
return waf;
}

// 解析HTTP请求
int parse_http_request(const char *raw, http_request_t *req) {
memset(req, 0, sizeof(http_request_t));

char *ptr = (char*)raw;

// 解析请求行
if (sscanf(ptr, "%s %s %s", req->method, req->uri, req->version) != 3) {
return -1;
}

// 解析Headers
char *line_start = strstr(ptr, "\r\n");
if (!line_start) return -1;
ptr = line_start + 2;

while (*ptr) {
char *line_end = strstr(ptr, "\r\n");
if (!line_end) break;

char *colon = strchr(ptr, ':');
if (colon) {
char *key = ptr;
int key_len = colon - ptr;
char *value = colon + 1;
while (*value == ' ') value++;
int value_len = line_end - value;

if (req->header_count < MAX_HEADERS) {
snprintf(req->headers[req->header_count], MAX_HEADER_LEN,
"%.*s: %.*s", key_len, key, value_len, value);
req->header_count++;
}
}

ptr = line_end + 2;

// 空行表示body开始
if (ptr[0] == '\r' && ptr[1] == '\n') {
ptr += 2;
break;
}
}

// Body
if (*ptr) {
req->body_len = strlen(ptr);
if (req->body_len < MAX_BODY_LEN) {
memcpy(req->body, ptr, req->body_len);
req->body[req->body_len] = '\0';
}
}

return 0;
}
```

3. 规则引擎

```c
// 添加规则
void waf_add_rule(waf_engine_t *waf, int id, rule_type_t type, const char *pattern,
const char *target, waf_action_t action, const char *severity,
const char *message) {
pthread_mutex_lock(&waf->mutex);

waf_rule_t *rule = malloc(sizeof(waf_rule_t));
rule->id = id;
rule->type = type;
strcpy(rule->pattern, pattern);
strcpy(rule->target, target);
rule->action = action;
strcpy(rule->severity, severity);
strcpy(rule->message, message);
rule->enabled = 1;
rule->next = waf->rules;
waf->rules = rule;
waf->rule_count++;

pthread_mutex_unlock(&waf->mutex);
printf("[规则] 添加规则 #%d: %s\n", id, message);
}

// 模式匹配(支持正则简化)
int pattern_match(const char *pattern, const char *text, int nocase) {
if (!pattern || !text) return 0;

// 纯字符串匹配
if (nocase) {
char p[512], t[512];
strcpy(p, pattern);
for (int i = 0; p[i]; i++) p[i] = tolower(p[i]);
strcpy(t, text);
for (int i = 0; t[i]; i++) t[i] = tolower(t[i]);
return strstr(t, p) != NULL;
}

return strstr(text, pattern) != NULL;
}

// 检查SQL注入模式
int detect_sql_injection(const char *text) {
const char *patterns[] = {
"select", "union", "insert", "update", "delete", "drop",
"from", "where", "order by", "group by", "--", ";",
"' or '1'='1", "' or 'a'='a", "\" or \"1\"=\"1",
"sleep(", "benchmark(", "waitfor delay",
"information_schema", "table_name", "column_name",
NULL
};

for (int i = 0; patterns[i]; i++) {
if (pattern_match(patterns[i], text, 1)) {
return 1;
}
}
return 0;
}

// 检查XSS模式
int detect_xss(const char *text) {
const char *patterns[] = {
"<script", "</script>", "javascript:", "onerror=",
"onload=", "onmouseover=", "onclick=", "onfocus=",
"alert(", "prompt(", "confirm(", "document.cookie",
"window.location", "eval(", "innerHTML", "document.write",
"&lt;script", "&#60;script", "&#x3c;script",
NULL
};

for (int i = 0; patterns[i]; i++) {
if (pattern_match(patterns[i], text, 1)) {
return 1;
}
}
return 0;
}

// 检查路径遍历
int detect_path_traversal(const char *text) {
const char *patterns[] = {
"../", "..\\", "/etc/passwd", "/etc/shadow",
"/proc/", "/var/log/", "boot.ini", "win.ini",
"%2e%2e%2f", "%2e%2e\\", "..%5c",
NULL
};

for (int i = 0; patterns[i]; i++) {
if (pattern_match(patterns[i], text, 0)) {
return 1;
}
}
return 0;
}

// 检查命令注入
int detect_cmd_injection(const char *text) {
const char *patterns[] = {
"; ls", "; whoami", "; cat", "; echo",
"| whoami", "| ls", "& whoami", "& ls",
"$(cat", "$(whoami", "`cat", "`whoami",
"; net user", "& net user", "| net user",
NULL
};

for (int i = 0; patterns[i]; i++) {
if (pattern_match(patterns[i], text, 0)) {
return 1;
}
}
return 0;
}

// 检查SSRF
int detect_ssrf(const char *text) {
const char *patterns[] = {
"http://127.0.0.1", "http://localhost", "http://0.0.0.0",
"http://169.254.169.254", "http://metadata", "http://[::1]",
"http://192.168.", "http://10.", "http://172.16.",
"file:///etc/", "gopher://", "dict://", "ftp://",
NULL
};

for (int i = 0; patterns[i]; i++) {
if (pattern_match(patterns[i], text, 0)) {
return 1;
}
}
return 0;
}
```

4. 频率限制

```c
// 频率限制检查
int waf_check_rate_limit(waf_engine_t *waf, const char *ip, const char *uri,
int limit, int window_seconds) {
char key[128];
snprintf(key, sizeof(key), "%s:%s", ip, uri);

time_t now = time(NULL);

pthread_mutex_lock(&waf->mutex);

rate_limit_entry_t *entry = waf->rate_limits;
while (entry) {
if (strcmp(entry->key, key) == 0) {
// 检查是否被阻塞
if (entry->blocked && now < entry->block_until) {
pthread_mutex_unlock(&waf->mutex);
return 1; // 被阻塞
}
if (entry->blocked && now >= entry->block_until) {
entry->blocked = 0;
entry->count = 0;
}

// 滑动窗口
if (now - entry->first_request > window_seconds) {
entry->first_request = now;
entry->count = 1;
pthread_mutex_unlock(&waf->mutex);
return 0;
}

entry->count++;
entry->last_request = now;

if (entry->count > limit) {
entry->blocked = 1;
entry->block_until = now + 300; // 阻塞5分钟
pthread_mutex_unlock(&waf->mutex);
return 1;
}

pthread_mutex_unlock(&waf->mutex);
return 0;
}
entry = entry->next;
}

// 创建新条目
entry = malloc(sizeof(rate_limit_entry_t));
strcpy(entry->key, key);
entry->count = 1;
entry->first_request = now;
entry->last_request = now;
entry->blocked = 0;
entry->block_until = 0;
entry->next = waf->rate_limits;
waf->rate_limits = entry;

pthread_mutex_unlock(&waf->mutex);
return 0;
}
```

5. 主检测流程

```c
// WAF检测
waf_action_t waf_inspect(waf_engine_t *waf, http_request_t *req) {
// 1. IP检查
// 2. URI检查
// 3. Body检查
// 4. Headers检查
// 5. 频率限制

waf_rule_t *rule = waf->rules;
while (rule) {
if (!rule->enabled) {
rule = rule->next;
continue;
}

const char *text = NULL;

if (strcmp(rule->target, "uri") == 0) {
text = req->uri;
} else if (strcmp(rule->target, "body") == 0) {
text = req->body;
} else if (strcmp(rule->target, "ip") == 0) {
text = req->client_ip;
} else if (strcmp(rule->target, "user-agent") == 0) {
// 从headers提取User-Agent
for (int i = 0; i < req->header_count; i++) {
if (strncasecmp(req->headers[i], "User-Agent:", 11) == 0) {
text = req->headers[i] + 12;
while (*text == ' ') text++;
break;
}
}
}

if (text) {
int matched = 0;

switch (rule->type) {
case RULE_SQL_INJECTION:
matched = detect_sql_injection(text);
break;
case RULE_XSS:
matched = detect_xss(text);
break;
case RULE_PATH_TRAVERSAL:
matched = detect_path_traversal(text);
break;
case RULE_CMD_INJECTION:
matched = detect_cmd_injection(text);
break;
case RULE_SSRF:
matched = detect_ssrf(text);
break;
default:
matched = pattern_match(rule->pattern, text, 0);
break;
}

if (matched) {
printf("[WAF] 拦截: %s (规则 #%d) %s -> %s\n",
rule->severity, rule->id, req->client_ip, rule->message);
return rule->action;
}
}

rule = rule->next;
}

return WAF_ALLOW;
}
```

6. 测试代码

```c
void test_waf() {
printf("=== Web应用防火墙测试 ===\n\n");

waf_engine_t *waf = waf_create("./logs");

// 添加规则
waf_add_rule(waf, 1, RULE_SQL_INJECTION, "", "uri",
WAF_BLOCK, "CRITICAL", "SQL注入检测");
waf_add_rule(waf, 2, RULE_XSS, "", "body",
WAF_BLOCK, "HIGH", "XSS攻击检测");
waf_add_rule(waf, 3, RULE_PATH_TRAVERSAL, "", "uri",
WAF_BLOCK, "HIGH", "路径遍历检测");
waf_add_rule(waf, 4, RULE_CMD_INJECTION, "", "uri",
WAF_BLOCK, "CRITICAL", "命令注入检测");

// 测试正常请求
http_request_t req1;
parse_http_request(
"GET /index.html HTTP/1.1\r\n"
"Host: example.com\r\n"
"User-Agent: Mozilla/5.0\r\n"
"\r\n",
&req1
);
strcpy(req1.client_ip, "192.168.1.100");

printf("正常请求:\n");
waf_action_t result = waf_inspect(waf, &req1);
printf(" 结果: %s\n", result == WAF_ALLOW ? "允许" : "拦截");

// 测试SQL注入
http_request_t req2;
parse_http_request(
"GET /search?q=' OR '1'='1 HTTP/1.1\r\n"
"Host: example.com\r\n"
"\r\n",
&req2
);
strcpy(req2.client_ip, "192.168.1.101");

printf("\nSQL注入请求:\n");
result = waf_inspect(waf, &req2);
printf(" 结果: %s\n", result == WAF_ALLOW ? "允许" : "拦截");

// 测试XSS
http_request_t req3;
parse_http_request(
"POST /comment HTTP/1.1\r\n"
"Host: example.com\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"\r\n"
"text=<script>alert(1)</script>",
&req3
);
strcpy(req3.client_ip, "192.168.1.102");

printf("\nXSS请求:\n");
result = waf_inspect(waf, &req3);
printf(" 结果: %s\n", result == WAF_ALLOW ? "允许" : "拦截");

// 测试路径遍历
http_request_t req4;
parse_http_request(
"GET /download?file=../../etc/passwd HTTP/1.1\r\n"
"Host: example.com\r\n"
"\r\n",
&req4
);
strcpy(req4.client_ip, "192.168.1.103");

printf("\n路径遍历请求:\n");
result = waf_inspect(waf, &req4);
printf(" 结果: %s\n", result == WAF_ALLOW ? "允许" : "拦截");

// 频率限制测试
printf("\n频率限制测试 (10次请求):\n");
int blocked_count = 0;
for (int i = 0; i < 10; i++) {
int blocked = waf_check_rate_limit(waf, "192.168.1.100", "/api/test", 5, 60);
if (blocked) blocked_count++;
printf(" 请求%d: %s\n", i+1, blocked ? "拦截" : "允许");
}
printf(" 总拦截: %d 次\n", blocked_count);

printf("\n规则统计: %d 条\n", waf->rule_count);

free(waf);
}

int main() {
test_waf();
return 0;
}
```

---

三、编译和运行

```bash
gcc -o waf waf.c -lpthread
./waf
```

---

四、WAF vs 其他安全方案

特性 WAF 防火墙 IDS 杀毒软件
检测层 应用层 网络层 网络层 文件层
检测方式 规则+签名 规则 规则+异常 签名+行为
实时拦截 ✅ ✅ ❌ ✅
Web攻击检测 ✅ ❌ ❌ ❌
部署位置 Web服务器前 网络边界 旁路 主机

---

五、总结

通过这篇文章,你学会了:

· WAF的核心功能(规则检测、频率限制、黑白名单)
· HTTP请求解析
· 攻击检测规则(SQL注入、XSS、路径遍历、命令注入、SSRF)
· 频率限制实现
· 检测引擎设计

Web应用防火墙是应用安全的关键组件。掌握它,你就理解了AWS WAF、Cloudflare WAF的底层设计。

下一篇预告:《从零实现一个容器运行时:Docker的核心设计》

---

评论区分享一下你遇到过最复杂的WAF绕过方式~

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询