Amit-Cohen10/Credential-Stuffing-Attack-Detection
GitHub: Amit-Cohen10/Credential-Stuffing-Attack-Detection
基于53万条真实登录数据的凭据填充攻击检测分析项目,综合运用启发式规则与统计异常检测方法(z-分数、IQR、Shannon熵),通过分层置信度评分识别恶意流量并导出泄露凭据列表。
Stars: 0 | Forks: 0
# HW1 — 凭据填充攻击检测
**课程:** 3917 — 恶意软件检测中的 AI 技术
**学期:** 2 / 2026
**学生:** Amit Cohen (318556016) · Sagi Levhar (206590457)
## 本次作业的内容
我们获得了一个包含约 53 万次登录尝试的真实数据集,这些数据是在活跃的
凭据填充攻击期间捕获的。目标是:
1. 通过探索性分析理解数据 (A 部分)
2. 为 IP、电子邮件、工具和网络构建特征表 (B 部分)
3. 设计我们自己的启发式检测规则,并将每一行标记为恶意/合法 (C 部分)
4. 应用统计异常检测——z-分数、IQR 围栏、Shannon 熵——并
将它们组合成分层置信度分数 (D 部分)
5. 撰写一份安全团队可以实际阅读的简短攻击分析报告 (E 部分)
6. 三项附加分析:时间突发检测、国家×工具热力图、创新特征 (附加题)
## 本次提交的文件
| 文件 | 说明 |
|---|---|
| `HW1_Credential_Stuffing.ipynb` | 主 notebook——包含所有代码、图表和书面回答 |
| `helpers.py` | 被 notebook 导入的共享辅助函数(熵、z-分数、IQR、绘图工具) |
| `assignment_labeled.csv` | 添加了两个新列的原始数据集:`is_malicious` (C 部分) 和 `is_malicious_refined` (D 部分) |
| `compromised_credentials.csv` | 至少有一次成功登录(状态 200)且被标记为恶意的电子邮件——“泄露凭据”列表 |
| `figures/` | 保存为 PNG 格式的全部 23 张图表(运行 notebook 时自动创建) |
## 如何运行
```
pip install pandas numpy matplotlib seaborn scipy upsetplot
```
然后在 Jupyter 中打开 `HW1_Credential_Stuffing.ipynb` 并执行:
**Kernel → Restart & Run All**
在运行之前,请确保 `assignment.csv` 与 notebook 位于同一文件夹中。
在普通笔记本电脑上的总运行时间约为 30 秒。
一切都是可复现的——`RANDOM_STATE = 42` 在 notebook
和 `helpers.py` 的顶部均已设置,并传递给每个接受它的操作。
## 关键结果(简要总结)
| 指标 | 值 |
|---|---|
| 总登录尝试次数 | 530,816 |
| 标记为恶意的(C 部分启发式) | 323,614 (61.0 %) |
| 精炼后的恶意标签(D 部分分层) | 308,487 (58.1 %) |
| 独立攻击 IP 数 | 见 notebook 中的 E.1 |
| 发现的泄露凭据 | 22,813 个电子邮件账户 |
`risk_indication` 供应商标签将大约 59% 的流量标记为有风险,这个比例太高了,
无法作为真实的基准数据使用。这正是我们在 C 部分构建自己的检测器,
并在 D 部分通过统计学方法对其进行交叉验证的原因。
## 问题索引 —— PDF 中每个问题的解答位置
使用此表可直接跳转到任何答案。每个答案在 notebook 中都有一个类似
`**Answer to X.Y QZ —` 的标题,因此也可以使用 Ctrl+F 进行查找。
| PDF 问题 | 问题内容 | Notebook 章节 | 要查找的单元格/标题 |
|---|---|---|---|
| **A.1 Q3** | 每个 HTTP 状态码可能代表什么 | Part A → A.1 | `A1_status_dist` + 其下方的 markdown |
| **A.2 Q1** | 数据集的准确时间范围 | Part A → A.2 | `A2_time_range` + markdown |
| **A.2 Q2** | 是否存在流量突发?(>2× 中位速率) | Part A → A.2 | `A2_per_minute` + markdown |
| **A.2 Q3** | `risk_indication` 的划分说明了什么? | Part A → A.2 | `A2_per_minute_split` + markdown |
| **A.3 Q1** | 哪些国家看起来可疑以及原因 | Part A → A.3 | `A3_countries` + markdown |
| **A.3 Q2** | 哪些工具具有接近 100% 的风险率 | Part A → A.3 | `A3_tools` + markdown |
| **A.3 Q3** | 每个 IP 邮件数阈值的合理性依据 | Part A → A.3 | `A3_emails_per_ip` + markdown |
| **B.1 Q** | 为什么需要 IP 级别特征?何时会失效? | Part B → B.1 | `B1_ip_features` 之后的 markdown 单元格 |
| **B.4 Q2** | 我们如何处理缺失的 `network_type`(77% 缺失) | Part B → B.4 | `B4_merge` 之后的 markdown |
| **B.4 Q3** | 为什么对 network_type 使用独热编码,对其他使用频率编码 | Part B → B.4 | 同一个 markdown 单元格 |
| **C.1 Q1–Q3** | 标签质量:标记率、按状态的交叉表、按工具的细分 | Part C → C.1 | `C1_label_quality`, `C1_tool_risk_bar` |
| **C.1 Q4** | 即使供应商标签是完美的,为什么还要构建自己的检测器 | Part C → C.1 | Markdown "Answer to C.1 Q4" |
| **C.2 Q1** | 4 条带有阈值及其合理性依据的检测规则 | Part C → C.2 | Markdown "Answer to C.2 Q1" |
| **C.2 Q2** | 应用规则 → 生成 `is_malicious` 列 | Part C → C.2 | `C2_apply` |
| **C.2 Q3** | 将我们的标签与 `risk_indication` 进行比较,差异分析 | Part C → C.2 | `C2_compare` + `C2_disagree` + markdown |
| **C.2 Q4** | 规则重叠可视化(UpSet 图) | Part C → C.2 | `C2_upset` |
| **D.1 Q1–Q4** | Z-分数计算,z>2 和 z>3 时的计数 | Part D → D.1 | `D1_zscores`, `D1_zscore_histograms` |
| **D.1 Q5** | 这些分布真的是正态分布吗? | Part D → D.1 | Markdown "Answer to D.1 Q5" |
| **D.2 Q1–Q3** | IQR 上限围栏,异常值计数,3 选 2 规则 | Part D → D.2 | `D2_iqr` |
| **D.2 Q4** | 为什么在偏态数据上 IQR 比 z-分数更鲁棒 | Part D → D.2 | Markdown "Answer to D.2 Q4" |
| **D.3 Q1–Q4** | 每个 IP 的 Shannon 熵,散点图,阈值选择 | Part D → D.3 | `D3_entropy`, `D3_scatter` |
| **D.3 Q2** | 高邮件熵/状态熵意味着什么 | Part D → D.3 | Markdown "Answer to D.3 Q2" |
| **D.3 Q5** | 重试 3 次的用户会被误认为攻击者吗? | Part D → D.3 | 同一个 markdown 单元格 |
| **D.4 Q1–Q5** | 分层置信度分数 + 精炼标签 | Part D → D.4 | `D4_layered`, `D4_plots` |
| **D.4 Q4** | 通过精炼去除了多少 FP / 引入了多少 FN | Part D → D.4 | Markdown "Answer to D.4 Q4" |
| **D.4 Q6** | 为什么分层检测比任何单一方法更可靠 | Part D → D.4 | Markdown "Answer to D.4 Q6" |
| **E.1** | 攻击汇总表 + 时间线 + 攻击量最大的 IP | Part E → E.1 | `E1_summary`, `E1_timeline`, `E1_top_attackers` |
| **E.2** | 按国家、网络类型和工具划分的来源分析 | Part E → E.2 | `E2_country`, `E2_network_type`, `E2_tool_attack_rate` |
| **E.3** | 泄露凭据:前 20 名表 + CSV 导出 | Part E → E.3 | `E3_compromised` |
| **Bonus 1** | 使用到达时间间隔 + CV 的时间突发检测 | 附加题部分 | `BONUS_1_burst`, `BONUS_1_scatter` |
| **Bonus 2** | 国家 × 工具攻击率热力图 | 附加题部分 | `BONUS_2_heatmap` |
| **Bonus 3** | 三个创新特征(时间、邮件熵 KDE、工具×国家交互作用) | 附加题部分 | `BONUS_3_creative` |
## 代码结构
该 notebook 被划分为与作业评分标准相同的部分(A → E + 附加题)。
所有可复用的逻辑都存放在 `helpers.py` 中,因此没有在多个单元格间进行复制粘贴:
```
helpers.py
├── shannon_entropy(series) # Shannon entropy of a categorical Series (bits)
├── zscore(series) # Population z-score, NaN-safe
├── iqr_upper_fence(series, k=1.5) # Tukey upper fence Q3 + k*IQR
├── coefficient_of_variation(arr) # std/mean, safe for empty/zero-mean arrays
├── styled_hist(series, ax, ...) # Consistent histogram style across all plots
├── annotate_thresholds(ax, ...) # Draws labelled vertical threshold lines
├── savefig(name) # Saves to ./figures/{name}.png at 150 dpi
├── pretty_print_section(name) # Section banner in notebook output
└── apply_heuristic_rules(df, ...) # Applies R1–R4 detection rules, returns df + is_malicious
```
该 notebook 在顶部的设置单元格中使用 `from helpers import *` 导入所有内容。
## AI 披露
**使用的工具:** Claude (Anthropic),通过 Claude Code,2026 年 4 月;OpenAI Codex,
2026 年 4 月 25 日,用于提交前的验证和小修复。
### 我们使用的提示词(按顺序)
**提示词 1 —— 在编写任何代码之前了解数据集:**
**提示词 2 —— 特征工程设计:**
**提示词 3 —— 启发式检测规则:**
**提示词 4 —— 统计异常检测:**
**提示词 5 —— 分层检测与精炼标签:**
**提示词 6 —— 附加题:时间突发检测:**
**提示词 7 —— 使用 OpenAI Codex 进行提交前验证:**
### 工作过程
我们阅读了作业 PDF,并将其分解为与评分标准相同的部分(A → E + 附加题)。
在最终的 Codex 验证阶段,我们检查了 ZIP 文件的完整性、notebook 的有效性、
从全新解压开始的复现性、CSV 的形状/标签,以及与 PDF 可交付成果的对齐情况。
我们根据 EDA 直方图(30 封电子邮件/IP 的拐点、1 次请求/秒的速率阈值、
大于 4 比特的熵截断值)自行设计了阈值,并使用 Claude 来搭建
辅助函数的框架、编写 matplotlib 样板代码,以及仔细检查 pandas
聚合语法的正确性。
每个阈值都针对实际的数据分布进行了验证——我们没有在没有首先运行图表
并确认经验断点的情况下接受任何“魔法数字”。
对每个 PDF 问题的 markdown 回答是由我们在运行单元格
并阅读输出后撰写的。
### 我们学到的东西
早期让我们感到惊讶的一件事是,z-分数在这类数据上的表现有多么糟糕。
我们原以为它们能将攻击者与普通用户干净利落地分开,但由于极少数
非常活跃的机器人抬高了均值和标准差,导致阈值随之上升,许多中等活跃的攻击者
成为了漏网之鱼。切换到 IQR(Q1 和 Q3)立刻解决了这个问题——
这些百分位数不受尾部极端值的影响,而这正是你在寻找“异常值”时
所期望的特性。
对熵的洞察则花了更长的时间才被领悟。起初我们尝试纯粹通过邮件熵来标记 IP,
但随后我们注意到一些合法的共享 IP(比如:大学 Wi-Fi 网关)同样具有很高的
邮件熵,仅仅是因为许多真实用户通过同一个出口节点登录。解决方法是将熵
与请求量结合起来:真正的攻击者同时具有高熵和高请求量,
而共享网关虽然具有高熵,但每个独立用户只贡献少量请求。一旦我们加入了
请求量门槛,误报率就显著下降了。
关于检测技术,我们学到的最重要的一点是,没有任何单一方法是独立可靠的。
我们的启发式规则(C 部分)抓住了明显的攻击者,却遗漏了隐蔽的攻击者。
z-分数标记了统计异常,但在偏态数据上失效了。
IQR 更加鲁棒,但可能会错过微小的系统性偏移。熵抓住了高扇出特征,
但对低频攻击者视而不见。分层方法——要求 4 种方法中至少有 2 种达成一致——
结果比任何单一方法都要强大得多,因为误报率是相乘而不是相加的。
如果每种方法独立地将合法 IP 误标的几率为 5%,那么两种方法同时误标的几率仅为
0.05 × 0.05 = 0.25%。这就是现实安全系统中集成和分层检测背后的核心直觉。
最后,关于供应商标签(`risk_indication`)的令人大开眼界。它将
约 59% 的流量标记为“有风险”,这显然不能作为可用的真实基准——这
本质上是噪音。构建我们自己的检测器,然后将其与供应商标签进行对比,
向我们确切地展示了它们在哪里存在分歧以及为什么。这是我们没有预料到
能在这个作业中练习的技能,但它可能是我们学到的最实用的一课。
标签:Apex, BSD, IQR, Matplotlib, NoSQL, NumPy, Python, SciPy, Seaborn, Z-Score, 人工智能, 代码示例, 凭证填充, 启发式规则, 威胁情报, 开发者工具, 异常检测, 撞库检测, 数据分析, 数据挖掘, 无后门, 机器学习, 用户模式Hook绕过, 统计特征, 网络安全, 逆向工具, 隐私保护, 香农熵