☰
安全基线落不了地?一套三层分级清单 + 可直接跑的核查脚本(附 20 条补漏项)
2026/10/28 1:32:16 网站建设 项目流程

安全基线这件事,甲方安全岗几乎都做过,但大部分都做成了"一张没人看的 Excel"。

典型的样子:安全部门花两周整理出一份几百行的配置清单,发给运维,要求自查整改;运维看一眼,回一句"太多了,哪些必须改";安全部门说"都是必须的";然后就没有然后了。半年后审计来查,发现清单躺在共享盘里,没人动过。

我在几个项目里踩过这个坑,后来把基线从"文档"做成了"流水线":一份分级清单 + 一套自动核查脚本 + 一条例外审批通道 + 一个接进整改台账的出口。核查覆盖率从靠人肉抽查的 20% 左右,做到全量自动跑,配置类问题从"季度清理"变成"每周出报告"。

这篇把整条流水线拆开写,包括可以直接跑的核查脚本。

一、基线为什么总是落不了地

先想清楚失败的原因,不然做多少版清单都一样。

我总结下来是四个错位:

错位表现结果
粒度错位一条基线写成一本书("操作系统应安全配置")运维无法执行
刚性错位所有条目都标"必须",没有例外机制遇到业务冲突就整份放弃
验证错位只发清单不验证,靠自查自报报上来的全是"已整改"
归属错位安全部门写、运维改、没人签字出问题互相推

其中刚性错位是最致命的。

安全基线本质上是"默认拒绝"的配置集合,而业务系统几乎总有一些"合理的不合规"——老系统必须开 telnet、某中间件必须用弱加密算法对接旧设备、某服务必须以 root 跑。如果你不给这些情况留正规出口,运维就会整体不执行,因为按清单来一定会影响业务。

所以第一件事不是写清单,是给清单分级,并给例外留通道。

二、基线分级:三层结构,只有第一层不可协商

我把所有基线条目分成三层,这个结构直接决定了后面所有流程怎么跑。

层级定义数量占比例外可能违反后果
L1 红线违反即构成可直接利用的高危风险约 15%不允许例外立即整改,24 小时内
L2 标准业界通行的安全配置要求约 65%需审批,有替代措施30 天内,或走例外
L3 建议加固项,收益明确但非必需约 20%可自主决定纳入季度计划

L1 的判断标准很简单:这一条如果没做,攻击者能不能直接进来或者直接拿走数据。比如:

  • 数据库对公网开放
  • 存在空口令或默认口令账号
  • SSH 允许 root 直接登录 + 密码认证
  • 未授权的远程管理端口暴露(3389/22/5900)
  • Web 目录可列目录且含备份文件
  • 应用存在已知的未修复高危组件(在野利用)

这些不给例外。如果业务确实冲突,方案是改架构,不是签例外。

L2 才是主体,也是容易扯皮的部分。L3 用来给运维"送分",做完显得有进展,实际风险不大——这个设计对推动整体配合度很有效。

三、清单本身怎么长:按"可验证"来写

一条基线写得对不对,只有一个标准:能不能用一条命令或者一次检查判断出"符合/不符合"。

反例:

操作系统应进行安全加固,关闭不必要的服务和端口。

正例(可直接验证):

编号检查项判定方法合格标准层级
LNX-001SSH 禁止 root 登录`sshd -T \grep permitrootlogin`输出permitrootlogin noL1
LNX-002SSH 禁止密码认证`sshd -T \grep passwordauthentication`输出noL2
LNX-003口令最小长度grep -E '^minlen' /etc/security/pwquality.conf≥ 10L2
LNX-004口令复杂度`grep -E '^(minclassdcreditucredit)' /etc/security/pwquality.conf`四类字符中至少三类L2
LNX-005登录失败锁定grep deny /etc/security/faillock.confdeny ≤ 5L2
LNX-006审计服务运行systemctl is-active auditdactiveL1
LNX-007关键目录权限stat -c '%a' /etc/shadow000 或 640 以下L1
LNX-008空口令账号awk -F: '($2==""){print $1}' /etc/shadow无输出L1
LNX-009内核参数-禁止 IP 转发sysctl net.ipv4.ip_forward0(非路由设备)L3
LNX-010时间同步`timedatectl status \grep synchronized`yesL2

这个表的价值在于:每一条都能被脚本直接判定,不需要人去理解、判断、解释。

写清单时容易犯的一个错是写"应该"、"建议"、"适当"这类模糊词。一旦出现这类词,这条基线就等于没写——因为没人能验证,也没人会执行。

四、自动化核查:把清单变成脚本

清单定了,下一步是让它自动跑。架构很简单:

整条流水线分四层,每一层的输入输出都必须固定下来,否则脚本就跑不起来:

  1. 采集层:各主机执行核查脚本,输出统一的 JSON(每条带 check_id、结果、证据)
  2. 比对层:与基线定义逐条比对,生成"符合 / 不符合 / 不适用"三种结论
  3. 汇总层:按系统、按责任人聚合,出 HTML 或 Excel 报告
  4. 闭环层:不符合项自动写入整改台账,进入整改流程

关键在于采集层要足够轻:不装 agent、不依赖特定平台、能批量下发。这一点很重要——很多企业的基线核查做不下去,就是因为要求装一套重型工具,而运维不愿意在几百台机器上装东西。

我用的方式是最原始的:一段 bash 脚本,通过已有的运维通道(ansible / 堡垒机 / 批量执行)推下去跑,输出 JSON 回来。

下面这段可以直接用,覆盖上面表里的主要检查项:

#!/bin/bash # baseline_check.sh - Linux 基线核查,输出 JSON # 用法: ./baseline_check.sh > /tmp/baseline_$(hostname).json out() { printf '%s\n' "$1"; } j() { printf ' "%s": {"value": "%s", "level": "%s"},\n' "$1" "$2" "$3"; } echo "{" echo " \"host\": \"$(hostname)\"," echo " \"time\": \"$(date -Iseconds)\"," echo " \"items\": {" # LNX-001 SSH root 登录 v=$(sshd -T 2>/dev/null | awk '/^permitrootlogin/{print $2}') j "LNX-001" "${v:-unknown}" "L1" # LNX-002 SSH 密码认证 v=$(sshd -T 2>/dev/null | awk '/^passwordauthentication/{print $2}') j "LNX-002" "${v:-unknown}" "L2" # LNX-003 口令最小长度 v=$(grep -E '^\s*minlen' /etc/security/pwquality.conf 2>/dev/null | awk -F= '{print $2}' | tr -d ' ') j "LNX-003" "${v:-unset}" "L2" # LNX-005 登录失败锁定 v=$(grep -E '^\s*deny' /etc/security/faillock.conf 2>/dev/null | awk -F= '{print $2}' | tr -d ' ') j "LNX-005" "${v:-unset}" "L2" # LNX-006 审计服务 v=$(systemctl is-active auditd 2>/dev/null) j "LNX-006" "${v:-inactive}" "L1" # LNX-007 /etc/shadow 权限 v=$(stat -c '%a' /etc/shadow 2>/dev/null) j "LNX-007" "${v:-unknown}" "L1" # LNX-008 空口令账号 v=$(awk -F: '($2==""){print $1}' /etc/shadow 2>/dev/null | tr '\n' ',' ) j "LNX-008" "${v:-none}" "L1" # LNX-009 IP 转发 v=$(sysctl -n net.ipv4.ip_forward 2>/dev/null) j "LNX-009" "${v:-unknown}" "L3" # LNX-010 时间同步 v=$(timedatectl status 2>/dev/null | awk -F: '/synchronized/{gsub(/ /,"",$2);print $2}') j "LNX-010" "${v:-unknown}" "L2" # 附加:监听在 0.0.0.0 的高危端口 v=$(ss -lntp 2>/dev/null | awk 'NR>1 && $4 ~ /0\.0\.0\.0:(3306|6379|9200|27017)/{print $4}' | tr '\n' ',') j "LNX-011" "${v:-none}" "L1" echo " }" echo "}"

输出长得像这样,正好能被下游解析:

{ "host": "app-node-01", "time": "2026-03-25T09:12:33+08:00", "items": { "LNX-001": {"value": "no", "level": "L1"}, "LNX-002": {"value": "yes", "level": "L2"}, "LNX-008": {"value": "none", "level": "L1"} } }

汇总端用一个短脚本把结果转成"不符合项清单",直接当整改台账用:

import json, glob RULES = { # 编号 -> (合格判定函数, 说明) "LNX-001": (lambda v: v == "no", "SSH 允许 root 直接登录"), "LNX-002": (lambda v: v == "no", "SSH 允许密码认证"), "LNX-003": (lambda v: v.isdigit() and int(v) >= 10, "口令最小长度不足"), "LNX-006": (lambda v: v == "active", "审计服务未运行"), "LNX-007": (lambda v: v in ("0", "000", "640"), "/etc/shadow 权限过宽"), "LNX-008": (lambda v: v == "none", "存在空口令账号"), "LNX-010": (lambda v: v == "yes", "未启用时间同步"), } rows = [] for f in glob.glob("/tmp/baseline_*.json"): d = json.load(open(f, encoding="utf-8")) for code, item in d["items"].items(): check, desc = RULES.get(code, (None, None)) if not check: continue if not check(item["value"]): rows.append({ "host": d["host"], "code": code, "level": item["level"], "actual": item["value"], "issue": desc, }) rows.sort(key=lambda x: (x["level"], x["host"])) for r in rows: print(f'[{r["level"]}] {r["host"]:20s} {r["code"]} {r["issue"]} (当前值: {r["actual"]})') print(f"\n共 {len(rows)} 项不符合")

跑完直接得到一份可以贴进整改通知的清单,按等级和主机排好序。

Windows 侧不用写脚本,系统自带两个工具就够:

:: 导出当前安全策略,与基线 .inf 比对 secedit /export /cfg C:\tmp\current.inf :: 查看本地密码策略实际生效值 net accounts :: 导出组策略结果集,看最终生效配置 gpresult /h C:\tmp\gpresult.html /f

把基线做成一个.inf模板,用secedit /configure /db secedit.sdb /cfg baseline.inf就能批量套用。这套东西在域环境里走组策略更省事,非域机器才用 secedit。

五、例外管理:给"改不了"一条正规路

这是整套机制能不能长期跑的关键。

没有例外通道,运维只有两个选择:要么硬改(可能引发故障),要么不理(风险悬空)。有例外通道,就变成第三个选择:走流程、留痕、加补偿措施。

例外单我设计成一张表,五个必填项:

字段要求示例
基线编号精确到条目LNX-002
系统/主机明确责任范围旧版 ERP 应用节点 ×2
无法整改原因必须是客观约束,不能是"没时间"对接的旧设备仅支持密码认证
补偿措施必须可验证仅允许内网 10.0.0.0/8 访问,已加 ACL
有效期 + 复核时间最长 6 个月,到期自动回到待整改至 2026-09-30,9 月复核

规则有三条:

  1. L1 不允许例外,只允许"临时缓解 + 限期整改",且必须业务负责人签字
  2. 例外必须有补偿措施,没有补偿措施的不批
  3. 例外到期自动复活,不允许"永久例外"

第 3 条最容易被绕过——很多单位的例外单签完就再没人看。我的做法是把例外单和台账打通,到期日到点自动回到"待整改"状态,跟着周报一起推。

六、接进整改闭环,别做两套流程

基线核查出来的不符合项,本质上就是漏洞。所以不要单独建一套"基线整改流程",直接并入漏洞整改的同一张台账。

字段设计上只要多两列:

字段说明
来源漏扫/基线核查/人工发现/外部通报
基线编号基线类问题填写,其他为空

这样带来三个好处:

  • 领导看一张表就知道整体风险,不用听你解释两套体系
  • 整改推动的话术、分级时限、复测流程全部复用
  • 同一个系统的漏洞和基线问题能一起排期,减少反复打扰业务方

关于整改推动的完整方法(责任矩阵、话术库、七步 SOP),我另外写过一篇,这里不重复展开,两篇配合看就是一条完整链路。

七、怎么衡量基线做得好不好

不要用"核查了多少台"这种过程指标,用下面四个:

指标定义参考目标
红线清零率L1 不符合项已闭环 / 总数100%,任何时点都不该有存量
核查覆盖率已纳入自动核查的主机 / 资产总数核心系统 100%,整体 ≥ 90%
平均闭环时长从核查出问题到复测通过L1 ≤ 3 天,L2 ≤ 30 天
例外占比有效期内的例外条目 / 总条目< 8%,超过说明基线定得不合理

第四个指标最有诊断价值。例外占比长期高于 10%,通常不是运维不配合,而是基线本身脱离实际——要么定得太严(照抄等保条款但没考虑业务),要么没分层(该是 L3 的写成 L1)。

这时候该改的是基线,不是加压。

八、二十条最常被忽略的加固点

最后给一份"补漏清单",都是实际核查里高频出现、但容易被写的清单漏掉的:

  1. 数据库监听地址是0.0.0.0而不是127.0.0.1或内网地址
  2. Redis 无密码且对公网开放(这个组合出了太多事故)
  3. Elasticsearch 9200 端口无认证暴露
  4. Tomcat 管理后台/manager/html用默认口令
  5. Jenkins 未授权访问(/script可直接执行命令)
  6. Nacos / Eureka / Consul 等注册中心面板裸奔
  7. Spring Boot Actuator 的/env、/heapdump对外可访问
  8. 应用日志目录全局可读,含数据库连接串明文口令
  9. 备份文件(.bak、.sql、.zip)留在 Web 目录
  10. 版本管理目录.git/.svn可被下载
  11. 中间件示例应用未删除(/examples/、/docs/)
  12. 云主机安全组放通0.0.0.0/0的 22/3389
  13. 对象存储桶允许匿名列举
  14. 内部 API 未做鉴权,仅靠网关路由隔离
  15. 运维账号共用,无个人实名账号
  16. 数据库账号权限过大(业务账号带 DBA 权限)
  17. 定时任务以 root 跑业务脚本
  18. 日志未集中采集,本地日志保留不足 6 个月
  19. 证书使用自签名或已过期,未做有效期监控
  20. 双因素认证未覆盖 VPN、堡垒机、云控制台

这 20 条不需要一次性全推。我的建议是先把第 1、2、3、5 条做成自动扫描,因为它们的高危率极高、修复成本极低(改个配置、加个密码),投入产出比最好。

九、落地顺序建议

如果你现在从零开始,别一上来就写完整清单。按这个顺序走:

  1. 第一周:只定 L1 红线,不超过 20 条,全部选"能被一条命令验证"的
  2. 第二周:写核查脚本,先在 3~5 台非核心机器上跑通,修掉脚本误报
  3. 第三周:全量跑一次,出一份《当前基线现状报告》给领导——这一份报告是后面所有推动的凭据
  4. 第四周:把不符合项按系统分组,走整改流程,先清 L1
  5. 第二个月:补 L2 清单,接进例会和周报,形成固定节奏
  6. 第三个月:上例外审批,把"改不了"的正规化

不要跳过第 3 步。没有一份"现状报告",你就没有推动的抓手,所有要求都会被当成"安全部门又来找事"。

十、写在最后

安全基线是甲方安全里最不性感、但收益最确定的一块。

它不像应急响应那样有故事性,也不像攻防演练那样有戏剧性,但它决定了你的安全水位下限——真实世界里绝大多数被拿下的系统,不是被高级 0day 打穿的,而是因为一个没改的弱口令、一个没关的端口、一个没权限控制的接口。

把基线做成流水线,等于把"靠人盯"变成"靠机制跑"。这件事做完,你才有余力去做那些更需要判断力的活。

基线跑起来之后,往下接的两步:一是把不符合项变成整改任务,方法在漏洞整改推动实操;二是把基线条目对应到测评条款,写法在等保2.0定级备案与差距分析。主机被打穿了的情况,处置顺序看应急响应复盘。

有具体场景想聊的(比如你们环境里某类设备怎么核查),评论区留,我看到会回。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询