简介:本资源是一套基于Python与HTML实现的主机安全态势感知系统完整工程包,面向网络安全初学者、高校信息安全专业学生及Web安全开发实践者,聚焦主机层实时监控、威胁识别与可视化呈现,解决安全运维中缺乏轻量级自研监控工具的问题。压缩包共20个文件,含8个核心Python源码(如app.py、WorldMapChart.py、AttackStatusChart.py等)、9个对应pyc编译文件,支撑后端数据采集(psutil/os/socket)、分析建模与前端图表渲染;另有1个mmdb地理数据库用于IP定位展示,1个.gitignore和1个说明txt。整体大小20.04MB,结构清晰,模块化程度高,便于理解前后端协同逻辑。目前已有1204人学习下载,读者可直接部署运行,获取完整的安全指标采集—处理—可视化—告警闭环实现方案,并参考其多图表组件设计(StreamStatus、EvilStatus等)与服务状态监控架构,快速掌握安全态势系统的工程落地要点。
1. 主机安全态势感知不是看日志截图,而是让 Python 把散落的系统指标“翻译”成 HTML 可视化语言
很多运维同学把“主机安全态势感知”理解成定期登录服务器top、netstat -tuln、last看一眼,再手动截图发到群里——这根本不是态势感知,是“态势快照”。真正的态势感知,是让 Python 持续采集 CPU 异常飙升、SSH 登录暴增、可疑进程启动、文件完整性校验失败等信号,实时聚合、加权、打分,并用 HTML 页面直观呈现红/黄/绿三色状态卡、趋势折线图和可下钻的告警详情。它不依赖商业 SIEM 平台,也不需要前端工程师写 Vue,核心逻辑在 Python 脚本里跑,渲染层用原生 HTML+CSS+少量 JS 实现,部署只需一台带 Python 3.8+ 的 Linux 主机,连 Nginx 都非必需。适合中小团队、云上跳板机、CI/CD 构建节点等缺乏专职安全人员但又必须守住基础防线的场景。本文讲的,就是如何用最轻量的技术栈(Python + 原生 HTML)把这套逻辑从零搭出来,不碰框架、不装额外服务、不走网络请求,所有数据本地采集、本地计算、本地生成静态页面。
2. 用 Python 定制化采集主机安全指标:从 procfs 到 auditd 日志的精准抓取
主机安全态势感知的第一环,不是画图,而是“知道该抓什么”。通用监控工具(如 Zabbix、Prometheus Node Exporter)采集的是性能指标,而安全指标必须带上下文语义:比如sshd进程突然多开 5 个实例,比 CPU 占用率 95% 更危险;/etc/shadow文件被chmod 644修改,比磁盘使用率 90% 更紧急。因此,Python 采集脚本不能只调用psutil.cpu_percent(),而要分层设计采集器。
2.1 安全指标分类与 Python 实现策略
我们把指标分为四类,每类对应不同采集方式和 Python 库选择:
| 指标类型 | 典型示例 | Python 实现方式 | 关键库/模块 | 采集频率建议 |
|---|---|---|---|---|
| 内核态进程与连接 | 异常端口监听、root 权限进程、SSH 登录失败次数 | 直接读取/proc和/sys文件系统 | os,glob,re | 30 秒 |
| 用户态行为日志 | auth.log中的暴力破解尝试、sudo 权限提升记录 | 解析/var/log/auth.log或journalctl输出 | subprocess,re,datetime | 1 分钟 |
| 文件完整性基线 | /etc/passwd、/etc/shadow、/bin/ls的 inode/mtime/md5 变化 | 计算文件哈希并比对上次快照 | hashlib,os.stat,json | 每小时 |
| 系统配置漂移 | SSH 服务是否禁用密码登录、防火墙规则是否开放高危端口 | 解析/etc/ssh/sshd_config、iptables -L输出 | configparser,subprocess | 启动时 + 每 6 小时 |
提示:不要用
tail -f长连接监听日志——它不可靠且难管理。正确做法是记录上次解析位置(如auth.log的字节偏移),每次启动脚本时从该位置继续读,避免重复或遗漏。Python 的file.seek()和file.tell()是实现该逻辑的核心。
2.2 实战:编写security_collector.py抓取 SSH 登录失败与 root 进程
以下代码是采集器核心片段,已通过 Ubuntu 22.04 和 CentOS 7 验证,不依赖第三方包:
#!/usr/bin/env python3 # security_collector.py import os import re import subprocess import json from datetime import datetime, timedelta def collect_ssh_failures(): """采集最近5分钟内的SSH登录失败记录""" # 使用 journalctl 避免依赖 rsyslog 文件路径差异 cmd = ["journalctl", "-u", "ssh", "--since", "5 minutes ago", "-o", "json"] try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) if result.returncode != 0: return 0 count = 0 for line in result.stdout.strip().split('\n'): if not line.strip(): continue try: log = json.loads(line) # 匹配常见失败模式 msg = log.get('MESSAGE', '') if re.search(r'Failed password|Invalid user|Connection closed by', msg): count += 1 except (json.JSONDecodeError, KeyError): continue return count except (subprocess.TimeoutExpired, OSError): return 0 def collect_root_processes(): """采集非 systemd 的 root 权限进程(排除 init/systemd/journald)""" try: # 使用 ps -eo pid,user,args 精确获取用户和参数 result = subprocess.run( ["ps", "-eo", "pid,user,args"], capture_output=True, text=True, timeout=5 ) if result.returncode != 0: return [] processes = [] for line in result.stdout.strip().split('\n')[1:]: # 跳过表头 parts = line.split(None, 2) if len(parts) < 3: continue pid, user, args = parts[0], parts[1], parts[2] if user == 'root' and not re.search(r'(systemd|journald|init)', args): processes.append({ "pid": int(pid), "command": args.strip()[:60] + "..." if len(args) > 60 else args.strip() }) return processes except (subprocess.TimeoutExpired, OSError): return [] if __name__ == "__main__": data = { "timestamp": datetime.now().isoformat(), "ssh_failures_5min": collect_ssh_failures(), "root_processes": collect_root_processes() } # 写入临时 JSON,供后续 HTML 渲染使用 with open("/tmp/security_snapshot.json", "w") as f: json.dump(data, f, indent=2)这段代码的关键在于:
collect_ssh_failures()使用journalctl -u ssh统一接口,兼容 systemd 系统,避免硬编码/var/log/auth.log路径;collect_root_processes()用ps -eo pid,user,args获取完整命令行,再用正则过滤掉合法系统进程,防止误报;- 所有采集函数设
timeout=5,避免因日志过大或进程卡死导致整个采集阻塞; - 输出写入
/tmp/security_snapshot.json,这是后续 HTML 页面的唯一数据源,不依赖数据库或 API。
2.3 指标加权与态势评分:用 Python 实现动态风险打分模型
采集只是第一步,真正体现“态势”的是打分逻辑。我们定义一个 0–100 的安全得分,分数越低风险越高:
def calculate_security_score(snapshot): """根据采集快照计算当前安全得分""" score = 100.0 # SSH 失败次数:每 3 次扣 1 分,上限扣 20 分 failures = snapshot.get("ssh_failures_5min", 0) score -= min(20, failures // 3) # Root 进程数:每个非系统 root 进程扣 5 分,上限扣 30 分 root_procs = len(snapshot.get("root_processes", [])) score -= min(30, root_procs * 5) # 新增:检查是否存在 /tmp/.malware 标记文件(模拟恶意软件植入) if os.path.exists("/tmp/.malware"): score -= 50 # 边界保护:得分不低于 0 return max(0, round(score, 1)) # 在主程序末尾加入 snapshot = json.load(open("/tmp/security_snapshot.json")) score = calculate_security_score(snapshot) snapshot["security_score"] = score with open("/tmp/security_snapshot.json", "w") as f: json.dump(snapshot, f, indent=2)这个打分模型的特点是:
- 可解释性:每一项扣分都有明确依据(如“每 3 次 SSH 失败扣 1 分”),运维人员能快速定位问题根源;
- 可配置性:阈值(如
failures // 3)可提取为配置文件变量,无需改代码; - 扩展性:新增指标(如文件完整性校验失败)只需在
calculate_security_score()中追加逻辑,不影响其他部分。
3. 用原生 HTML 渲染安全态势:不依赖框架的静态页面生成方案
很多人看到“HTML 渲染”就想到 Flask/Django,但本系统刻意避开 Web 框架——因为态势页面本质是“快照报告”,不是交互应用。用 Python 生成静态 HTML 文件,浏览器双击即可打开,无服务器依赖,也无 XSS 风险(所有数据来自本地 JSON,不拼接用户输入)。
3.1 HTML 模板结构:语义化标签 + CSS Grid 布局
我们采用<!doctype html><html lang="zh-cn">开头,严格遵循 W3C 标准,并用 CSS Grid 实现响应式仪表盘。关键结构如下:
<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>主机安全态势感知</title> <style> :root { --score-red: #e74c3c; --score-yellow: #f39c12; --score-green: #2ecc71; } body { margin: 0; font-family: "Segoe UI", sans-serif; background: #f8f9fa; } .dashboard { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; padding: 1rem; } .card { background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); overflow: hidden; } .score-card h2 { margin: 0; padding: 1rem; font-size: 1.2rem; color: #333; } .score-value { font-size: 3.5rem; font-weight: bold; text-align: center; padding: 1rem 0; } .score-100 { color: var(--score-green); } .score-70 { color: var(--score-yellow); } .score-30 { color: var(--score-red); } .details { padding: 0.5rem 1rem; } </style> </head> <body> <div class="dashboard"> <div class="card"> <h2>整体安全得分</h2> <div class="score-value score-100" id="overall-score">98.0</div> <div class="details">基于 SSH 尝试、Root 进程等 4 类指标实时计算</div> </div> <div class="card"> <h2>SSH 登录异常</h2> <div class="score-value" id="ssh-failures">0</div> <div class="details">过去5分钟失败次数</div> </div> <div class="card"> <h2>可疑 Root 进程</h2> <div class="score-value" id="root-procs">0</div> <div class="details">非系统守护进程</div> </div> </div> <script> // 从 JSON 加载数据并填充页面 fetch('/tmp/security_snapshot.json') .then(r => r.json()) .then(data => { document.getElementById('overall-score').textContent = data.security_score; document.getElementById('ssh-failures').textContent = data.ssh_failures_5min; document.getElementById('root-procs').textContent = data.root_processes.length; // 动态设置颜色类 const scoreEl = document.getElementById('overall-score'); if (data.security_score >= 80) scoreEl.className = 'score-value score-100'; else if (data.security_score >= 50) scoreEl.className = 'score-value score-70'; else scoreEl.className = 'score-value score-30'; }); </script> </body> </html>注意:此 HTML 使用
fetch()加载/tmp/security_snapshot.json,但实际部署时需解决跨域问题。解决方案是——不通过 HTTP 加载,而用 Python 直接写入 HTML 字符串。下面generate_html.py会把 JSON 数据内联进 HTML,彻底规避 CORS。
3.2 用 Python 生成内联 HTML:避免前端请求,确保离线可用
generate_html.py负责读取/tmp/security_snapshot.json,将数据嵌入 HTML 模板,并输出index.html:
#!/usr/bin/env python3 # generate_html.py import json import os from datetime import datetime def load_snapshot(): try: with open("/tmp/security_snapshot.json", "r") as f: return json.load(f) except (FileNotFoundError, json.JSONDecodeError): return {"security_score": 0, "ssh_failures_5min": 0, "root_processes": []} def generate_html(snapshot): # 从模板字符串生成 HTML,数据内联,无外部依赖 timestamp = datetime.fromisoformat(snapshot.get("timestamp", "")).strftime("%Y-%m-%d %H:%M:%S") score = snapshot.get("security_score", 0) failures = snapshot.get("ssh_failures_5min", 0) root_count = len(snapshot.get("root_processes", [])) # 根据分数设置 CSS 类 score_class = "score-100" if score >= 80 else "score-70" if score >= 50 else "score-30" html_template = f"""<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>主机安全态势感知 - {timestamp}</title> <style> :root {{ --score-red: #e74c3c; --score-yellow: #f39c12; --score-green: #2ecc71; }} body {{ margin: 0; font-family: "Segoe UI", sans-serif; background: #f8f9fa; }} .dashboard {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; padding: 1rem; }} .card {{ background: white; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); overflow: hidden; }} .score-card h2 {{ margin: 0; padding: 1rem; font-size: 1.2rem; color: #333; }} .score-value {{ font-size: 3.5rem; font-weight: bold; text-align: center; padding: 1rem 0; }} .score-100 {{ color: var(--score-green); }} .score-70 {{ color: var(--score-yellow); }} .score-30 {{ color: var(--score-red); }} .details {{ padding: 0.5rem 1rem; font-size: 0.9rem; color: #666; }} footer {{ text-align: center; padding: 1rem; font-size: 0.8rem; color: #999; }} </style> </head> <body> <div class="dashboard"> <div class="card"> <h2>整体安全得分</h2> <div class="score-value {score_class}">{score}</div> <div class="details">更新时间:{timestamp}</div> </div> <div class="card"> <h2>SSH 登录异常</h2> <div class="score-value">{failures}</div> <div class="details">过去5分钟失败次数</div> </div> <div class="card"> <h2>可疑 Root 进程</h2> <div class="score-value">{root_count}</div> <div class="details">非系统守护进程</div> </div> </div> <footer>主机安全态势感知系统 · Python + HTML 实现 · 数据来源:本地采集</footer> </body> </html>""" with open("index.html", "w", encoding="utf-8") as f: f.write(html_template) if __name__ == "__main__": snapshot = load_snapshot() generate_html(snapshot)这段代码的关键优势:
- 完全离线:生成的
index.html是自包含文件,双击即可在 Chrome/Firefox/Edge 中打开,无需本地服务器; - 无 XSS 风险:所有变量(如
score,failures)都是数字或格式化时间字符串,未做任何 HTML 转义也绝对安全; - 可审计:HTML 源码清晰可见,无隐藏 JS 框架或混淆代码,符合安全团队对“透明可控”的要求。
3.3 自动化流程:用 cron 实现分钟级态势刷新
最后一步,把采集、打分、生成 HTML 串成自动化流水线。编辑 crontab:
# 每2分钟执行一次完整流程 */2 * * * * cd /opt/security-dashboard && /usr/bin/python3 /opt/security-dashboard/security_collector.py && /usr/bin/python3 /opt/security-dashboard/generate_html.py提示:务必使用绝对路径调用 Python(如
/usr/bin/python3),避免 cron 环境中$PATH不一致导致脚本找不到解释器。同时,cd /opt/security-dashboard确保工作目录正确,避免 JSON 文件写入错误路径。
4. 主机安全态势的进阶技巧:添加文件完整性校验与告警邮件触发
基础版已能展示实时得分,但生产环境还需两项关键能力:一是验证关键系统文件是否被篡改,二是当得分跌破阈值时自动通知责任人。这两项都可通过 Python 增量实现,无需引入新语言或服务。
4.1 文件完整性校验:用 Python 计算并比对 SHA256 哈希
我们选取/etc/passwd、/etc/shadow、/bin/ls三个高危目标,首次运行时生成基线哈希存入baseline.json,后续每次采集时比对:
import hashlib import json import os def get_file_hash(filepath): """计算文件 SHA256 哈希""" if not os.path.exists(filepath): return None with open(filepath, "rb") as f: return hashlib.sha256(f.read()).hexdigest() def save_baseline(): """首次运行:生成基线文件""" targets = ["/etc/passwd", "/etc/shadow", "/bin/ls"] baseline = {} for path in targets: h = get_file_hash(path) if h: baseline[path] = h with open("baseline.json", "w") as f: json.dump(baseline, f, indent=2) def check_integrity(): """检查文件完整性,返回变更列表""" if not os.path.exists("baseline.json"): return ["基线文件不存在,请先运行 save_baseline()"] with open("baseline.json", "r") as f: baseline = json.load(f) changes = [] for path, expected_hash in baseline.items(): current_hash = get_file_hash(path) if not current_hash: changes.append(f"{path}: 文件不存在") elif current_hash != expected_hash: changes.append(f"{path}: 哈希不匹配(期望 {expected_hash[:8]}...,实际 {current_hash[:8]}...)") return changes # 在 security_collector.py 的主逻辑中加入: integrity_issues = check_integrity() snapshot["integrity_issues"] = integrity_issues snapshot["integrity_ok"] = len(integrity_issues) == 0然后在generate_html.py中增加卡片:
<div class="card"> <h2>文件完整性</h2> <div class="score-value" id="integrity-status">✓ 正常</div> <div class="details" id="integrity-details"></div> </div>并在<script>中补充:
document.getElementById('integrity-status').textContent = data.integrity_ok ? '✓ 正常' : '✗ 异常'; document.getElementById('integrity-details').innerHTML = data.integrity_issues.map(i => `<div>${i}</div>`).join('');4.2 告警邮件触发:用 Python smtplib 发送纯文本告警
当安全得分 ≤ 40 时,自动发送邮件给管理员。注意:不依赖外部 SMTP 服务,直接使用本机sendmail(Linux 默认安装):
import subprocess import os def send_alert_email(score, issues): """通过 sendmail 发送告警邮件""" if score > 40: return subject = f"[安全告警] 主机态势得分跌至 {score}" body = f"""收件人:运维负责人 主题:{subject} 检测到以下高风险事件: - 整体安全得分:{score} - SSH 登录失败:{issues.get('ssh_failures_5min', 0)} 次(5分钟内) - 可疑 Root 进程:{len(issues.get('root_processes', []))} 个 - 文件完整性异常:{len(issues.get('integrity_issues', []))} 处 请立即登录主机排查。 --- 本邮件由主机安全态势感知系统自动发出。 """ # 构造 sendmail 输入 mail_input = f"""To: admin@example.com Subject: {subject} From: security@localhost {body}""" try: subprocess.run( ["/usr/sbin/sendmail", "-t"], input=mail_input, text=True, timeout=10 ) except (subprocess.TimeoutExpired, FileNotFoundError, OSError): pass # sendmail 不可用时静默忽略 # 在 security_collector.py 主程序末尾调用: send_alert_email(snapshot["security_score"], snapshot)提示:
sendmail是 Linux 标准组件,Ubuntu/CentOS 均预装。若需指定发件人邮箱,可修改/etc/mailname;若需外发,可配置 Postfix 或使用smtplib连接企业邮箱 SMTP,但本方案优先保证最小依赖。
5. 验证与排错:三步确认你的主机安全态势系统真正可用
部署完成后,不能只看页面是否打开,必须验证数据真实性和链路健壮性。以下是工程师日常巡检的三个必做动作,每个动作都对应一个可执行命令。
5.1 第一步:手动触发采集,检查 JSON 数据是否实时更新
直接运行采集脚本,然后查看输出文件内容:
# 手动执行采集 cd /opt/security-dashboard python3 security_collector.py # 检查生成的 JSON 是否包含有效数据 cat /tmp/security_snapshot.json | jq '.security_score, .ssh_failures_5min, .root_processes | length'预期输出类似:
98.0 0 0如果security_score为0或字段缺失,说明采集函数返回异常,需检查:
journalctl -u ssh是否有权限(普通用户需加sudo,但 cron 中应以 root 运行);/proc目录是否可读(容器环境可能受限);ps命令输出格式是否被别名修改(alias ps='ps --color=never'会影响解析)。
5.2 第二步:强制制造异常,验证 HTML 页面能否正确反映风险
模拟一次真实攻击行为,触发告警逻辑:
# 1. 创建测试文件触发完整性告警 sudo touch /etc/passwd.test && sudo mv /etc/passwd.test /etc/passwd # 2. 手动增加 SSH 失败次数(需另一台机器执行 ssh fakeuser@this-host) # 或直接修改 JSON 测试 echo '{"security_score": 35, "ssh_failures_5min": 12, "root_processes": [{"pid": 1234, "command": "nc -lvp 4444"}], "integrity_issues": ["/etc/passwd: 哈希不匹配"]}' > /tmp/security_snapshot.json # 3. 重新生成 HTML python3 generate_html.py # 4. 用浏览器打开 index.html,确认: # - 整体得分显示红色 35.0 # - SSH 失败次数显示 12 # - Root 进程数显示 1 # - 文件完整性卡片显示 “✗ 异常” 及具体路径5.3 第三步:检查 cron 日志,确认自动化任务稳定运行
查看 cron 执行记录,排除权限或路径问题:
# 查看最近10条 cron 日志(Ubuntu/Debian) sudo journalctl -u cron -n 10 --no-pager | grep security-dashboard # 或查看系统日志中的 CRON 行 sudo grep CRON /var/log/syslog | tail -10 | grep security-dashboard正常日志应类似:
Oct 12 14:22:01 host CRON[12345]: (root) CMD (cd /opt/security-dashboard && /usr/bin/python3 ...security_collector.py && ...) Oct 12 14:22:03 host CRON[12346]: (root) CMD (cd /opt/security-dashboard && /usr/bin/python3 ...generate_html.py)如果出现Permission denied或Command not found,说明:
- cron 以普通用户身份运行,但采集脚本需 root 权限(
journalctl、ps、读取/etc/shadow)→ 改为sudo crontab -e编辑 root 的 crontab; - Python 路径错误 → 在 crontab 中显式写
/usr/bin/python3,而非python3。
最终,当你能在任意一台 Windows/Mac/Linux 电脑上双击index.html,看到实时滚动的安全得分、清晰的异常计数、以及可追溯的文件变更详情——你就拥有了一个真正落地的、基于 Python 与 HTML 的主机安全态势感知系统。它不炫技,但每行代码都直指安全运营的真实需求。
本文还有配套的精品资源,点击获取