WAF日志之自动化AI分析"

admin 2026-10-15 15:27:37 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

WAF日志之自动化AI分析

原创

pippybear
pippybear

安全无界

2026年10月13日 10:00
上海

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

之前简单聊过一波WAF自动化封禁(基于AWS),刚好前面有时间,就抽空把内部的流程再进一步梳理完善了一下,简单聊聊思路。

最初的一版比较糙,似乎还是去年年初干的,主要是为了解决AWS自动化封禁问题,当时思路也比较简单,使用Eventbridge定时触发Lambda提取WAF Log进行分析,然后针对Rate异常的IP进行简单行为分析,然后Block。

AWS实现自动IP封禁

设计思路

在这一年多里也是不断的修修补补变成了现在的版本,简单描述一下现在的版本,前面的逻辑差不多还是Eventbridge定时触发Lambda提取WAF Log,不过分析这个稍微优化了一下,主要分成3大块。

IP提取

1、规则匹配--触发直接进入block序列2、AI分析行为--未触发规则匹配的rate事件使用AI分析行为

IP威胁情报分提取

这里推荐一个apiabuseipdb.com,这个还挺不错的,DB挺全面,主要是每天的免费额度还挺多的:1000个IP。基本上中小型企业筛选攻击IP后查询基本够用。

威胁情报分这一块主要用于block判断和释放判断。

添加释放逻辑

V1版本只有封禁逻辑,但其实如果IP非持续攻击,其实也没有必要一直封禁着,所以现在的版本添加了自动释放逻辑。

代码实现

IP封禁

这一块逻辑其实很简单,不管是从Athena还是Cloudwatch抑或是S3提取都行,我这里采用的是从Athena提取(另外提一个小点,Athena的DB记得采用分区进行,如果不分区每一次读取的都是整个log,费钱!!!)

async function startAthenaQuery(database, query) {  const startRes = await athena.startQueryExecution({    QueryString: query,    QueryExecutionContext: { Database: database },    ResultConfiguration: { OutputLocation: ATHENA_OUTPUT }  }).promise();
  const queryExecutionId = startRes.QueryExecutionId;
  while (true) {    const statusRes = await athena.getQueryExecution({ QueryExecutionId: queryExecutionId }).promise();    const state = statusRes.QueryExecution.Status.State;    if ([\"SUCCEEDED\", \"FAILED\", \"CANCELLED\"].includes(state)) break;    await new Promise(r => setTimeout(r, 5000));  }
  let allRows = [];  let nextToken = null;
  do {    const res = await athena.getQueryResults({      QueryExecutionId: queryExecutionId,      NextToken: nextToken    }).promise();
    const rows = res.ResultSet.Rows.map(r => r.Data.map(d => d.VarCharValue || \"\"));    allRows = allRows.concat(rows);
    nextToken = res.NextToken;  } while (nextToken);
  if (allRows.length > 0) {    allRows = allRows.slice(1);  }
  return allRows;}
function multiDecode(str) {  let prev = \"\";  let cur = str;  let count = 0;
  while (cur !== prev && count < 5) {    prev = cur;    try {      cur = decodeURIComponent(cur);    } catch (e) {      break;    }    count++;  }
  return cur;}

提取Log后,对Log进行分析(先规则筛选+Rate提取分类)。

function classifyWAFLogs(rows) {  const ip_logs = {};
  for (const [ruleidRaw, clientip, uriRaw, argsRaw, user_agent] of rows) {    if (IP_WHITELIST.includes(clientip)) {      console.log(`🟦 白名单 IP 忽略:${clientip}`);      continue;    }
    const ruleid = (ruleidRaw || \"\").toString();    const uri = (uriRaw || \"\").toString();    const args = (argsRaw || \"\").toString();
    if (!ip_logs[clientip]) {      ip_logs[clientip] = {        ruleids: new Set(),        user_agents: new Set(),        records: [],        category: \"unknown\",        matchedIndex: null,        hitRule: null      };    }
    ip_logs[clientip].records.push({ ruleid, uri, args, user_agent });    ip_logs[clientip].ruleids.add(ruleid);    ip_logs[clientip].user_agents.add(user_agent);  }
  for (const [ip, data] of Object.entries(ip_logs)) {    for (let i = 0; i < data.records.length; i++) {      const { ruleid, uri, args } = data.records[i];      const ruleidLower = ruleid.toLowerCase();      const uriLower = (uri || \"\").toLowerCase();      const argsLower = (args || \"\").toLowerCase();
      if (KNOWN_BAD_RULES.some(badRule => ruleid.toLowerCase().includes(badRule.toLowerCase()))) {        data.category = \"attack\";        data.hitRule = ruleid;        data.matchedIndex = i;        break;      }
      if (        uri_suspicious_keywords.some(k => uriLower.includes(k)) ||        matchSuspicious(argsLower, args_suspicious_keywords) ||        matchSQLi(argsLower) ||        matchSQLi(uriLower)      ) {        data.category = \"attack\";        data.hitRule = \"(keyword-match)\";        data.matchedIndex = i;        break;      }
      if (RATE_RULE_KEYWORDS.some(k => ruleidLower.includes(k))) {        data.category = \"rate\";        data.hitRule = ruleid;        data.matchedIndex = i;        break;      }    }  }
  return ip_logs;}

然后使用AI对Rate类Log进行分析,判断是否为攻击行为。

IP威胁情报分提取

每一次首次出现的攻击IP,程序会自动对IP进行威胁情报分提取并存入DB。

    let intel = null;    if (!existing) {      console.log(\"🔍 首次出现,查询威胁情报:\", ip);      intel = await analyzeIPIntel(ip, secrets);    }

提取逻辑。

async function analyzeIPIntel(ip, secrets) {  try {    const res = await checkIP(ip, secrets);
    const d = res?.data;
    if (!d) return null;
    return {      intelScore: Number(d.abuseConfidenceScore || 0),      intelMarked: d.totalReports > 0,      intelLastCheck: new Date().toISOString()    };  } catch (e) {    console.error(\"❌ 威胁情报查询失败:\", ip, e.message);    return null;  }}
function checkIP(ip, secrets) {  return new Promise((resolve, reject) => {    const options = {      hostname: \"api.abuseipdb.com\",      path: `/api/v2/check?ipAddress=${ip}&maxAgeInDays=90&verbose=true`,      method: \"GET\",      headers: {        Key: secrets.abuseipdb_key,        Accept: \"application/json\",      },    };
    const req = https.request(options, (res) => {      let data = \"\";
      res.on(\"data\", (chunk) => (data += chunk));      res.on(\"end\", () => {        try {          const json = JSON.parse(data);          resolve(json);        } catch (e) {          reject(e);        }      });    });
    req.on(\"error\", reject);    req.end();  });}

释放逻辑

释放逻辑在每天一次的执行逻辑里面,具体释放大家可以根据自己的业务来做调整,我这里是设置了几个门槛:是否持续攻击persistent、是否属于高威胁情报IP。针对不同门槛释放时间不同。

async function runLifecycleJob(secrets) {  console.log(\"🚀 开始IP生命周期维护(Query attack + ai-rate, triggerCount>=3)\");
  const toBlock = [];  const toRelease = [];  const releasedSuccess = [];  const blockedSuccess = [];  const seen = new Set();
  async function collectBySource(source) {    let lastKey = null;
    do {      const res = await queryAttackCandidates(source, lastKey);
      console.log(`📦 [${source}] 本批获取 ${res.Items?.length || 0} 条记录, LastEvaluatedKey:`, res.LastEvaluatedKey);
      for (const item of (res.Items || [])) {        if (!item.ip || String(item.ip).startsWith(\"__\")) continue;
        const ip = item.ip;        if (seen.has(ip)) continue;        seen.add(ip);
        const triggerCount = Number(item.triggerCount || 0);        const lastSource = (item.lastSource || \"\").toLowerCase();
        // lifecycle 封禁逻辑只针对 attack        if (lastSource === \"attack\") {          if ((item.intelScore ?? 0) >= 50 && !item.isBlocked) {            console.log(\"🚫 命中威胁情报IP多次扫描自动封禁条件:\", item.ip);            toBlock.push({              ip: item.ip,              item,              reason: \"威胁情报高危封禁\"            });          }
          if ((item.intelScore ?? 0) < 50 && !item.isBlocked && triggerCount >= 5) {            console.log(\"🚫 命中普通IP多次扫描自动封禁条件:\", item.ip);            toBlock.push({              ip: item.ip,              item,              reason: \"普通多次扫描封禁\"            });          }        }
        const score = Number(item.intelScore ?? 0);        const idleDays = daysSince(item.lastSeen);        const hasTag = item.tag === \"persistent\";
        let attackDaySetRaw = item.attackDaySet;        let attackDaySet = [];
        if (Array.isArray(attackDaySetRaw)) {          attackDaySet = attackDaySetRaw;        } else if (attackDaySetRaw && typeof attackDaySetRaw === \"object\" && Array.isArray(attackDaySetRaw.values)) {          attackDaySet = attackDaySetRaw.values;        } else if (typeof attackDaySetRaw === \"string\") {          attackDaySet = attackDaySetRaw.split(\";\").filter(Boolean);        }
        const last7 = getLastNDays(7);        const activeDays = attackDaySet.filter(d => last7.includes(d));
        /* ---------- persistent ---------- */        if (!hasTag && activeDays.length >= 4) {          console.log(`🔥 标记 persistent: ${ip}`);
          await dynamo.update({            TableName: DYNAMODB_TABLE,            Key: { ip },            UpdateExpression: \"SET tag = :t\",            ExpressionAttributeValues: { \":t\": \"persistent\" }          }).promise();
          continue;        }
        /* ---------- 普通释放 ---------- */        if (!hasTag && !item.tagClearedAt && canReleaseByIntel(score, idleDays)) {          console.log(`🟢 命中普通释放条件: ${ip}`);          toRelease.push({            ip,            item,            type: \"普通释放\"          });          continue;        }
        /* ---------- persistent 降级 ---------- */        if (hasTag && idleDays >= 7 && !item.tagClearedAt) {          console.log(`🟡 清 persistent: ${ip}`);
          await dynamo.update({            TableName: DYNAMODB_TABLE,            Key: { ip },            UpdateExpression: `              REMOVE tag              SET tagClearedAt = :t            `,            ExpressionAttributeValues: {              \":t\": new Date().toISOString()            }          }).promise();
          continue;        }
        /* ---------- 二次释放 ---------- */        if (          !hasTag &&          item.tagClearedAt &&          daysSince(item.tagClearedAt) >= 7 &&          canReleaseByIntel(score, idleDays)        ) {          console.log(`🟢 命中 persistent释放条件: ${ip}`);          toRelease.push({            ip,            item,            type: \"persistent释放\"          });        }      }
      lastKey = res.LastEvaluatedKey;    } while (lastKey);  }
  await collectBySource(\"attack\");  await collectBySource(\"ai-rate\");
  const uniqueBlockMap = new Map();  for (const b of toBlock) {    if (!uniqueBlockMap.has(b.ip)) {      uniqueBlockMap.set(b.ip, b);    }  }
  const uniqueReleaseMap = new Map();  for (const r of toRelease) {    if (!uniqueReleaseMap.has(r.ip)) {      uniqueReleaseMap.set(r.ip, r);    }  }
  const finalBlocks = Array.from(uniqueBlockMap.values());  const finalReleases = Array.from(uniqueReleaseMap.values());
  console.log(`📌 待释放: ${finalReleases.length}`);  console.log(`📌 待封禁: ${finalBlocks.length}`);
  const blockIps = finalBlocks.map(x => x.ip);  const releaseIps = finalReleases.map(x => x.ip);
  try {    if (blockIps.length) {      console.log(`🚫 批量封禁 ${blockIps.length} 个 IP`);      await blockIPs(blockIps);    }
    if (releaseIps.length) {      console.log(`🟢 批量解封 ${releaseIps.length} 个 IP`);      await unblockIPs(releaseIps);    }  } catch (err) {    console.error(\"❌ 批量更新 IPSet 失败,终止后续 DB 更新:\", err);    throw err;  }
  for (const r of finalReleases) {    try {      console.log(`🟢 ${r.type}: ${r.ip}`);
      await dynamo.update({        TableName: DYNAMODB_TABLE,        Key: { ip: r.ip },        UpdateExpression: `          REMOVE attackDaySet, tag, tagClearedAt          SET should_release = :r,              isBlocked = :b,              triggerCount = :z,              lastSeen = :n,              releasedAt = :rt        `,        ExpressionAttributeValues: {          \":r\": true,          \":b\": false,          \":z\": 0,          \":n\": new Date().toISOString(),          \":rt\": new Date().toISOString()        }      }).promise();
      releasedSuccess.push({        ip: r.ip,        type: r.type,        intelScore: r.item?.intelScore,        triggerCount: r.item?.triggerCount      });    } catch (err) {      console.error(`❌ 释放失败 ${r.ip}:`, err);    }  }
  for (const b of finalBlocks) {    try {      console.log(`🚫 ${b.reason}: ${b.ip}`);
      await dynamo.update({        TableName: DYNAMODB_TABLE,        Key: { ip: b.ip },        UpdateExpression: \"SET isBlocked = :blocked, blockedAt = :time\",        ExpressionAttributeValues: {          \":blocked\": true,          \":time\": new Date().toISOString()        }      }).promise();
      blockedSuccess.push({        ip: b.ip,        reason: b.reason,        intelScore: b.item?.intelScore,        triggerCount: b.item?.triggerCount      });    } catch (err) {      console.error(`❌ 封禁失败 ${b.ip}:`, err);    }  }
  await notifyIPReleaseBatch(releasedSuccess, secrets);  await notifyIPBlockBatch(blockedSuccess, secrets, \"🚫 IP 批量封禁通知\");
  console.log(\"✅ 生命周期任务完成\");}

新增的几块核心代码如上,至于中间还涉及到的比如每日封禁/释放统计、如何实现DB、IPset同步操作等等逻辑代码就不展示了,实现起来其实也比较简单。直接看看最终效果。

WAF日志之自动化AI分析" 网络安全文章

WAF日志之自动化AI分析"

本文介绍基于AWS的WAF日志自动化AI分析系统,通过Eventbridge定时触发Lambda提取WAF日志,结合规则匹配与AI行为分析识别攻击IP,利用AbuseIPDB提取威胁情报分,并设计自动释放逻辑避免长期封禁。文章提供Athena查询、IP分类、威胁情报查询等核心代码实现,并建议Athena采用分区以节省成本,适合中小型企业参考落地。
Fortify静态应用安全测试(SAST)26.4" 网络安全文章

Fortify静态应用安全测试(SAST)26.4"

FortifySAST26.4发布,新增Kornshell脚本AI驱动扫描、Vue3预览支持、新应用分析器,扩展后量子密码学覆盖及多个语言框架支持。安全内容更新包括新漏洞类别和误报修复。建议团队更新工具链并注意规则包维护政策变更。
从漏洞分析到网络防护:美国陆军“列克星敦特遣部队”推进AI代理实战化" 网络安全文章

从漏洞分析到网络防护:美国陆军“列克星敦特遣部队”推进AI代理实战化"

美国陆军网络司令部通过列克星敦特遣部队推进AI代理在网络运营中的实战化应用,从漏洞分析员等角色扩展至完整AI代理网络防护小组,强调人类监督与严格框架。陆军即将宣布狮鹫项目授标,旨在构建AI代理生态系统防御网络,应对AI支持的威胁,并与其他军事机构共享工具。
评论:0   参与:  0