状态检测防火墙原理
讲完了包过滤,咱们得聊聊真正的「智能」防火墙了。状态检测,说白了就是防火墙能记住你刚才干了什么。
我记得刚入行那会儿,老工程师跟我说:「防火墙不是傻傻地看每个包,它得会记事儿。」当时不太理解,直到自己配了一回包过滤规则,被各种回程流量折腾得欲哭无泪……嗯,从那以后我才真正明白状态检测有多重要。
状态表与连接跟踪
状态检测的核心,就是一张表——状态表(也叫连接跟踪表)。
这张表长什么样?我直接给你看个简化版:
| 源IP | 源端口 | 目的IP | 目的端口 | 协议 | 状态 | 超时 |
|---|---|---|---|---|---|---|
| 192.168.1.10 | 34567 | 8.8.8.8 | 53 | UDP | ESTABLISHED | 30s |
| 10.0.0.5 | 44321 | 203.0.113.1 | 80 | TCP | ESTABLISHED | 3600s |
| 172.16.0.2 | 12345 | 198.51.100.7 | 22 | TCP | NEW | 120s |
每次有数据包经过,防火墙先查这张表。如果表里有匹配的条目,直接放行,不用再查规则库。这就是状态检测比包过滤快的原因——查表比查规则快得多。
连接跟踪的过程,我习惯分成三步:
- 首包创建:第一个SYN包到达,防火墙在状态表里新建一条记录,标记为NEW。
- 握手确认:三次握手完成后,状态更新为ESTABLISHED。
- 超时清理:连接空闲超过阈值,条目被删除。
关键点:状态表不是无限大的。我见过有人把超时时间设成24小时,结果防火墙内存爆了。生产环境里,TCP默认超时一般是3600秒,UDP更短,30-60秒。
状态检测的优势
说白了,状态检测解决了包过滤的两个大坑:
- 回程流量不用单独开规则:包过滤你得手动开「允许外部返回流量」,状态检测自动识别。
- 性能更好:查表是O(1)的哈希查找,查规则是O(n)的线性匹配。
- 安全性更高:能识别伪造的返回包——如果状态表里没有对应的出站记录,进来的包直接丢弃。
我在项目里遇到过一件事:客户用包过滤,为了能让用户上网,开了「允许所有来自外部的ESTABLISHED包」。结果呢?攻击者伪造ESTABLISHED标志位,直接穿透了防火墙。换成状态检测后,伪造包因为没有对应的状态表条目,全部被拦下。
个人建议:只要条件允许,优先用状态检测。包过滤只适合极简单的场景,比如只允许某个IP访问某个端口,不需要考虑回程流量。
常见状态:NEW / ESTABLISHED / RELATED / INVALID
这四个状态,是状态检测防火墙的基石。我一个个讲。
NEW
表示这是一个新连接的第一个包。对于TCP,就是SYN包。对于UDP,就是第一个数据包。
规则里一般这样写:
iptables -A FORWARD -m state --state NEW -p tcp --dport 80 -j ACCEPT
意思是:允许内网用户发起新的HTTP连接。
ESTABLISHED
表示这个连接已经完成了握手(TCP)或者已经双向通信过(UDP)。
这是最常用的状态。几乎所有状态检测防火墙都会有一条默认规则:
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
这条规则放行所有已建立的连接和相关的辅助连接。
RELATED
这个状态有点意思。它表示当前包属于一个已存在连接的「子连接」或「辅助连接」。
最常见的例子是FTP的主动模式和被动模式。FTP控制连接用21端口,数据连接用20端口或者随机端口。没有RELATED状态,你得手动开一堆端口。有了它,防火墙能自动识别数据连接属于哪个控制连接。
我记得有一次帮客户排查FTP问题,他们开了21端口,但数据传输就是失败。一看规则,没写RELATED状态。加上之后,问题立刻解决。
INVALID
这个状态表示包无法被识别,或者状态表里找不到匹配的条目,或者包本身有问题(比如TCP标志位组合非法)。
对于INVALID的包,标准做法是直接丢弃:
iptables -A FORWARD -m state --state INVALID -j DROP
警告:千万不要放行INVALID状态的包。我曾经见过有人为了省事,把INVALID也ACCEPT了,结果被扫描工具利用,绕过了防火墙的访问控制。INVALID包就是垃圾,直接扔掉。
状态转换流程
为了让你更直观地理解,我画了一张状态转换图:
从图上你能看到,正常的连接路径是 NEW → ESTABLISHED。如果出现非法包或者超时,就会跳到 INVALID。RELATED 是从 ESTABLISHED 分支出来的,用于处理像 FTP 数据连接这样的场景。
实际配置示例
最后,给你一个完整的 iptables 状态检测配置示例:
# 清空现有规则
iptables -F
iptables -X
# 默认策略:丢弃所有流量
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP
# 允许回环接口
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# 允许已建立和相关的连接
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 允许内网发起新连接(HTTP/HTTPS/DNS)
iptables -A FORWARD -i eth1 -m state --state NEW -p tcp --dport 80 -j ACCEPT
iptables -A FORWARD -i eth1 -m state --state NEW -p tcp --dport 443 -j ACCEPT
iptables -A FORWARD -i eth1 -m state --state NEW -p udp --dport 53 -j ACCEPT
# 丢弃无效包
iptables -A INPUT -m state --state INVALID -j DROP
iptables -A FORWARD -m state --state INVALID -j DROP
iptables -A OUTPUT -m state --state INVALID -j DROP
避坑指南:我曾经在配置时忘了加 ESTABLISHED,RELATED 的放行规则,结果内网能发请求,但回不来任何数据。排查了半天才发现是回程流量被默认策略丢弃了。记住:ESTABLISHED 规则一定要放在最前面。