网络入侵检测系统(NIDS)中的人工智能安全问题

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_classification

# 1. 构建网络入侵检测模型
class NIDSModel(nn.Module):
    def __init__(self, input_dim):
        super(NIDSModel, self).__init__()
        self.fc1 = nn.Linear(input_dim, 64)
        self.relu1 = nn.ReLU()
        self.fc2 = nn.Linear(64, 32)
        self.relu2 = nn.ReLU()
        self.fc3 = nn.Linear(32, 2)
        self.softmax = nn.Softmax(dim=-1)

    def forward(self, x):
        if x.dim() == 1:
            x = x.unsqueeze(0)
        x = self.fc1(x)
        x = self.relu1(x)
        x = self.fc2(x)
        x = self.relu2(x)
        x = self.fc3(x)
        return self.softmax(x)

# 2. 生成模拟数据
def generate_data():
    X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, 
                               n_redundant=5, n_classes=2, random_state=42)
    scaler = StandardScaler()
    X = scaler.fit_transform(X)
    return torch.FloatTensor(X), torch.LongTensor(y)

# 3. 训练模型
def train_model(model, dataset, epochs=10):
    loader = DataLoader(dataset, batch_size=32, shuffle=True)
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.01)
    
    model.train()
    for epoch in range(epochs):
        total_loss = 0
        for batch_x, batch_y in loader:
            optimizer.zero_grad()
            outputs = model(batch_x)
            loss = criterion(outputs, batch_y)
            loss.backward()
            optimizer.step()
            total_loss += loss.item()
        print(f"Epoch {epoch+1}/{epochs}, Loss: {total_loss/len(loader):.4f}")

# 4. FGSM 对抗攻击函数 (核心修复)
def fgsm_attack(data, target, model, epsilon=0.1):
    # 创建一个新的张量,基于原始数据,但独立于之前的计算图,并开启梯度
    adv_data = data.clone().detach().requires_grad_(True)
    
    # 前向传播
    output = model(adv_data)
    
    # 计算损失
    loss = nn.functional.cross_entropy(output, target)
    
    # 清零模型参数的梯度 (防止干扰,虽然我们要的是输入的梯度)
    model.zero_grad()
    
    # 反向传播:计算损失相对于输入 (adv_data) 的梯度
    # 关键点:retian_graph=False (默认),因为只需要一次梯度
    loss.backward()
    
    # 检查梯度是否成功计算
    if adv_data.grad is None:
        raise RuntimeError("梯度计算失败:adv_data.grad 为 None。请检查模型是否正确接收了带梯度的输入。")

    data_grad = adv_data.grad.data.sign()
    
    # 生成对抗样本
    adv_data = adv_data + epsilon * data_grad
    
    # 裁剪范围
    adv_data = torch.clamp(adv_data, min=-2.0, max=2.0)
    
    # 返回时不需要梯度了,detach 掉,否则后续绘图或保存会报错
    return adv_data.detach()

# 5. 评估模型准确率
def evaluate(model, loader):
    model.eval()
    correct = 0
    total = 0
    with torch.no_grad():
        for data, target in loader:
            outputs = model(data)
            _, predicted = torch.max(outputs.data, 1)
            total += target.size(0)
            correct += (predicted == target).sum().item()
    return correct / total

# --- 主流程 ---
if __name__ == "__main__":
    print("正在初始化环境...")
    torch.manual_seed(42)
    np.random.seed(42)

    X, y = generate_data()
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
    
    train_dataset = TensorDataset(X_train, y_train)
    test_dataset = TensorDataset(X_test, y_test)
    test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)

    input_dim = X_train.shape[1]
    model = NIDSModel(input_dim)
    
    print("\n=== 开始训练模型 ===")
    train_model(model, train_dataset, epochs=20)
    
    original_acc = evaluate(model, test_loader)
    print(f"\n=== 原始模型准确率: {original_acc:.4f} ===")

    print("\n=== 开始 FGSM 对抗攻击 ===")
    epsilon = 0.5 
    
    model.eval() # 设置为评估模式
    adv_correct = 0
    total = 0
    
    for data, target in test_loader:
        # 1. 生成对抗样本 (这里需要梯度,所以不能包在 no_grad 里)
        adv_data = fgsm_attack(data, target, model, epsilon)
        
        # 2. 使用对抗样本进行预测
        # 此时我们可以用 no_grad 来加速并节省内存,因为预测不需要梯度
        with torch.no_grad():
            output = model(adv_data)
            _, predicted = torch.max(output.data, 1)
            
            total += target.size(0)
            adv_correct += (predicted == target).sum().item()
            
    adv_acc = adv_correct / total
    print(f"=== 对抗样本下的准确率 (Epsilon={epsilon}): {adv_acc:.4f} ===")
    print(f"=== 性能下降幅度: {(original_acc - adv_acc) * 100:.2f}% ===")

    if adv_acc < original_acc:
        print("\n[警告] 模型受到对抗性攻击影响,鲁棒性不足!")
    else:
        print("\n[信息] 模型对该强度的攻击表现出一定的鲁棒性。")

网络入侵检测系统(NIDS)中的人工智能安全问题,具体展示了如何使用快速梯度符号法(FGSM)对深度学习模型进行对抗性攻击

代码分为五个主要部分:模型构建数据生成模型训练攻击算法实现以及攻击效果评估


1. 导入库与基础设置

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_classification
  • PyTorch (torch): 用于构建神经网络、自动求导(核心攻击机制依赖于此)和张量运算。
  • Scikit-learn: 用于生成模拟的网络流量数据(make_classification)、数据标准化(StandardScaler)和划分训练/测试集。
  • NumPy: 用于数值计算和随机种子设置,确保结果可复现。

2. 构建网络入侵检测模型 (NIDSModel)

这是一个简单的前馈神经网络(MLP),用于二分类任务(正常流量 vs 攻击流量)。

class NIDSModel(nn.Module):
    def __init__(self, input_dim):
        super(NIDSModel, self).__init__()
        # 输入层 -> 隐藏层1 (64神经元)
        self.fc1 = nn.Linear(input_dim, 64)
        self.relu1 = nn.ReLU()
        # 隐藏层1 -> 隐藏层2 (32神经元)
        self.fc2 = nn.Linear(64, 32)
        self.relu2 = nn.ReLU()
        # 隐藏层2 -> 输出层 (2类:正常/攻击)
        self.fc3 = nn.Linear(32, 2)
        # Softmax用于输出概率分布,dim=-1表示对最后一个维度(类别维度)归一化
        self.softmax = nn.Softmax(dim=-1)

    def forward(self, x):
        # 【鲁棒性处理】如果输入是一维向量(单样本),强制增加批次维度 (1, features)
        if x.dim() == 1:
            x = x.unsqueeze(0)
        
        x = self.fc1(x)
        x = self.relu1(x)
        x = self.fc2(x)
        x = self.relu2(x)
        x = self.fc3(x)
        return self.softmax(x)
  • 关键点: forward 函数中的 if x.dim() == 1 是为了防止在单样本预测或攻击时出现维度不匹配的错误。
  • 输出: 返回一个形状为 (Batch_Size, 2) 的张量,表示属于每一类的概率。

3. 数据生成与预处理 (generate_data)

由于没有使用真实的网络数据集(如NSL-KDD),这里使用 sklearn 生成模拟数据。

def generate_data():
    # 生成1000个样本,20个特征,2个类别
    X, y = make_classification(n_samples=1000, n_features=20, n_informative=15, 
                               n_redundant=5, n_classes=2, random_state=42)
    # 【重要】数据标准化:将特征缩放到均值为0,方差为1。
    # 神经网络对特征尺度敏感,标准化能加速收敛并提高稳定性。
    scaler = StandardScaler()
    X = scaler.fit_transform(X)
    
    # 转换为 PyTorch 张量
    return torch.FloatTensor(X), torch.LongTensor(y)
  • 现实意义: 在真实场景中,这里的 X 会是网络流的统计特征(如包长度、持续时间、标志位计数等)。

4. 模型训练 (train_model)

标准的监督学习训练流程。

def train_model(model, dataset, epochs=10):
    loader = DataLoader(dataset, batch_size=32, shuffle=True)
    criterion = nn.CrossEntropyLoss() # 分类任务常用损失函数
    optimizer = optim.Adam(model.parameters(), lr=0.01)
    
    model.train() # 切换到训练模式(启用Dropout等,虽然本模型没用到)
    for epoch in range(epochs):
        total_loss = 0
        for batch_x, batch_y in loader:
            optimizer.zero_grad()       # 清空旧梯度
            outputs = model(batch_x)    # 前向传播
            loss = criterion(outputs, batch_y) # 计算损失
            loss.backward()             # 反向传播,计算参数梯度
            optimizer.step()            # 更新参数
            total_loss += loss.item()
        print(f"Epoch {epoch+1}/{epochs}, Loss: {total_loss/len(loader):.4f}")

5. FGSM 对抗攻击核心 (fgsm_attack)

这是代码中最关键的部分,展示了AI安全漏洞的原理。

原理:

image

def fgsm_attack(data, target, model, epsilon=0.1):
    # 1. 准备输入张量
    # clone().detach(): 创建数据的副本,切断与之前计算图的联系,避免副作用
    # requires_grad_(True): 关键!告诉 PyTorch 我们需要计算相对于“输入数据”的梯度,而不仅仅是模型参数
    adv_data = data.clone().detach().requires_grad_(True)
    
    # 2. 前向传播
    output = model(adv_data)
    
    # 3. 计算损失
    # 我们希望通过最大化这个损失来欺骗模型,但在实现上通常计算损失后取梯度的反方向或直接利用梯度符号
    loss = nn.functional.cross_entropy(output, target)
    
    # 4. 反向传播
    model.zero_grad() # 清空模型参数的梯度(虽然我们要的是输入的梯度,但习惯上清空)
    loss.backward()   # 计算梯度。此时 adv_data.grad 包含了损失对输入的敏感度
    
    # 5. 检查梯度
    if adv_data.grad is None:
        raise RuntimeError("梯度计算失败")

    # 6. 获取梯度符号并生成扰动
    # sign() 函数将梯度简化为 -1, 0, 1,只保留扰动的方向,忽略大小
    data_grad = adv_data.grad.data.sign()
    
    # 7. 构造对抗样本
    # epsilon 控制扰动的强度。越大越容易绕过检测,但也越容易被人类或规则发现
    adv_data = adv_data + epsilon * data_grad
    
    # 8. 数据裁剪 (Clamping)
    # 确保生成的对抗样本特征值在合理范围内(例如,包长度不能为负数)
    # 在标准化数据中,[-2.0, 2.0] 是一个常见的合理范围
    adv_data = torch.clamp(adv_data, min=-2.0, max=2.0)
    
    # 9. 返回结果
    # detach(): 再次切断梯度图,因为后续只需要用它做预测,不需要继续反向传播
    return adv_data.detach()

安全启示:
这段代码证明了攻击者不需要知道模型的内部结构细节(如果是黑盒则通过替代模型),只要能得到梯度信息(或通过查询估算),就能构造出让人眼(或传统规则)无法察觉,但能让AI“失明”的恶意流量。


6. 评估与主流程 (__main__)

评估函数

def evaluate(model, loader):
    model.eval() # 切换到评估模式(关闭Dropout等)
    correct = 0
    total = 0
    with torch.no_grad(): # 评估时不需要计算梯度,节省内存并加速
        for data, target in loader:
            outputs = model(data)
            _, predicted = torch.max(outputs.data, 1)
            total += target.size(0)
            correct += (predicted == target).sum().item()
    return correct / total

主执行逻辑

if __name__ == "__main__":
    # ... 数据准备和模型训练 ...
    
    # 1. 测试原始准确率
    original_acc = evaluate(model, test_loader)
    print(f"=== 原始模型准确率: {original_acc:.4f} ===")

    # 2. 执行攻击
    print("\n=== 开始 FGSM 对抗攻击 ===")
    epsilon = 0.5  # 设置较强的扰动强度
    
    model.eval()   # 固定模型参数
    adv_correct = 0
    total = 0
    
    # 【关键细节】主循环不能在 torch.no_grad() 中!
    # 因为 fgsm_attack 内部需要计算梯度 (loss.backward())
    for data, target in test_loader:
        # A. 生成对抗样本 (需要梯度计算)
        adv_data = fgsm_attack(data, target, model, epsilon)
        
        # B. 用对抗样本测试模型 (不需要梯度,所以包裹在 no_grad 中)
        with torch.no_grad():
            output = model(adv_data)
            _, predicted = torch.max(output.data, 1)
            
            total += target.size(0)
            adv_correct += (predicted == target).sum().item()
            
    adv_acc = adv_correct / total
    
    # 3. 输出结果对比
    print(f"=== 对抗样本下的准确率: {adv_acc:.4f} ===")
    print(f"=== 性能下降幅度: {(original_acc - adv_acc) * 100:.2f}% ===")
    
    if adv_acc < original_acc:
        print("\n[警告] 模型受到对抗性攻击影响,鲁棒性不足!")
        print("这意味着攻击者可以通过微调恶意流量的特征,轻松绕过该AI检测系统。")

总结

  1. AI 的脆弱性: 即使模型在正常数据上准确率高达 95%+,在面对精心设计的对抗样本(epsilon=0.5)时,准确率可能会暴跌(例如降至 10%-30%)。
  2. 攻击成本低: 攻击只需要一次前向和一次反向传播(FGSM是单步攻击),计算成本极低,可以实时进行。
  3. 防御的必要性: 这强调了在部署 AI 驱动的 NIDS 时,不能只看测试集准确率,必须进行对抗训练(Adversarial Training)(即在训练集中加入这类对抗样本重新训练)或采用防御性蒸馏等技术来提高模型的鲁棒性。
posted @ 2026-03-25 23:26  左耳听风  阅读(5)  评论(0)    收藏  举报