news 2026/9/16 4:29:48

程序员必知:十大网络安全漏洞与工程化防御实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
程序员必知:十大网络安全漏洞与工程化防御实践

上周做代码评审,一个同事拍着胸脯说这个接口没有安全问题,我顺着他提交的改动往下翻了两行,就看到前端传过来的参数被直接拼进了 SQL 字符串,旁边还配了一句注释“这里走的是动态排序字段,预编译参数化处理不了,先拼一下”。类似的情况,过去几年我在代码评审里见到过太多次。程序员必知的网络安全知识点,说来说去其实就那么十来个反复出现的漏洞类型,不是什么高深莫测的黑客技术,更多是工程习惯问题。这篇我不讲攻击炫技,只讲开发中真正能用上的十个方向:威胁建模、SQL注入、密码存储、会话管理、越权控制、XSS、CSRF、文件上传、SSRF、供应链安全。适合刚接触安全的初中级后端、前端和全栈工程师,也适合想在代码评审里主动发现风险的人。

1. 先建立威胁模型:程序员最该理解“攻击者”是谁

1.1 攻击者的真实画像:不是电影里的高手,是扫描器和脚本

很多程序员对网络安全的第一个误解,是把攻击者想象成一个戴兜帽的高手,坐在昏暗房间里盯着你的系统死磕。真实的攻击者不是这样的,至少绝大多数攻击不是这样的。你只要把一个普通服务暴露到公网,几小时内就会收到来自全球的自动化扫描流量,这些流量在探测开放端口、尝试弱口令、试探已知漏洞,像流水线一样批量运作。安全圈有句话叫“不是有人针对你,而是大家都在被批量扫描”,所以“我们系统没价值,不会被攻击”这个想法非常危险。

一台机器被攻破之后,不一定是为了偷你的业务数据,也可能只是被当成跳板去攻击别人,或者被拉进挖矿程序里当免费算力。从工程视角看,你不需要假设一个无所不能的敌人,只需要假设有人会对你的系统做自动化批量尝试,并且会利用所有你暴露出来的入口。这就是“攻击面”这个概念重要性的来源:任何一个接收外部输入、响应外部请求的位置都是攻击面。表单提交是,文件上传是,回调 URL 是,Webhook 是,甚至一个用来生成 PDF 的接口也是。

所以第一步不是急着学各种漏洞利用姿势,而是先建立一个习惯:每次写接口时,把这个接口当成暴露在公网的东西来看,然后问自己“如果这个接口被恶意调用,会发生什么”。建立这种威胁模型视角,比背诵十个漏洞定义更有用。

1.2 STRIDE威胁建模:一张表把风险聊清楚

威胁建模不是安全专家的专利,微软提出的 STRIDE 模型,我到现在还在用,因为它简单到可以画在一张表里。STRIDE 是六类威胁的首字母缩写:伪造(Spoofing)、篡改(Tampering)、抵赖(Repudiation)、信息泄露(Information Disclosure)、拒绝服务(Denial of Service)、权限提升(Elevation of Privilege)。

威胁类型含义典型场景
伪造冒充他人身份登录接口被撞库、Token被伪造
篡改修改数据或请求内容订单金额被改、请求参数被替换
抵赖否认做过的事没有操作日志,用户不承认下过单
信息泄露不该看到的数据被看到越权访问他人订单
拒绝服务让服务不可用接口被刷、压垮数据库
权限提升普通用户获得管理员权限普通账号调用管理接口

用法很简单,拿一个核心业务流程,比如登录模块,逐条过一遍:登录请求会不会被重放,这是伪造;用户资料能不能被改,这是篡改;操作有没有日志,这是抵赖;密码存的是不是明文,这是信息泄露;验证码接口能不能被刷爆,这是拒绝服务;普通用户能不能走管理员登录逻辑,这是权限提升。不需要一次性把所有流程都分析完,挑核心功能过一遍,后面写代码时对“哪里容易出事”就会有一个本能的判断。我见过很多团队第一次做这个练习时会吓一跳,原来自己系统里随手一数就有七八个没兜底的地方。

2. 注入攻击:最经典的漏洞家族,至今仍排第一

2.1 SQL注入:参数化查询是底线,不是可选项

SQL 注入排在第一位,不是因为它最高级,而是因为它最常见,而且一旦发生就是灾难级别的数据泄露。原理其实特别好理解:你的程序把用户输入当成 SQL 语句的一部分去执行了。我用 Python 写个最典型的错误示范:

# 错误示范:不要这样写 user_input = request.form["username"] sql = "SELECT * FROM users WHERE username = '" + user_input + "'" cursor.execute(sql)

如果用户输入的 username 是admin' OR '1'='1,拼接出来的 SQL 就变成了:

SELECT * FROM users WHERE username = 'admin' OR '1'='1'

这个查询会返回全部用户,如果后面还跟着更新语句,后果完全可控不住。修复方式也很明确:用参数化查询,把 SQL 语句模板和参数分开传,让数据库自己处理参数的转义和类型转换。改成参数化之后是这个效果:

# 正确做法:参数化查询 sql = "SELECT * FROM users WHERE username = %s" cursor.execute(sql, (user_input,))

参数化为什么有效?因为数据库在执行参数化语句时,已经预先解析好了 SQL 结构,用户输入只会被当成数据,不会被当成 SQL 指令。一句话概括:你给数据库的是一份已经定好空位的菜单,用户往里填的是食材,食材永远不会变成菜单上的操作指令。需要补充的是,ORM 也不能完全免死,很多 ORM 提供了 raw query 或拼接查询的能力,一旦用了,就等于绕过了框架的安全保护。另外数据库账号的权限也要收紧,应用层的账号只给增删改查的必要权限,不要给 DROP 这类高危权限,这算是纵深防御。

2.2 命令注入与模板注入:框架引入的新风险

很多程序员以为不直接拼 SQL 就安全了,其实注入攻击远不止 SQL 一种。命令注入发生在你把用户输入拼到系统命令里的时候,比如有些后台功能需要 ping 一个 IP 或调用某个外部脚本:

# 错误示范:命令拼接 import subprocess ip = request.form["ip"] result = subprocess.run(f"ping -c 1 {ip}", shell=True, capture_output=True)

用户如果在 ip 里传入127.0.0.1; cat /etc/passwd,就相当于让服务器执行了两条命令。修复的核心是两条:能不调用系统命令就不调用,必须调用时用参数列表方式而不是字符串拼接,同时关闭 shell 中间层,并用白名单校验输入。

还有一类容易踩的坑是模板注入,服务端模板渲染引擎把用户输入当成了模板代码。很多 Python 的 Server-Side Template Injection 漏洞就是用户在输入框里填入模板表达式,结果被引擎执行了。防止这类问题,原则是:用户输入只允许作为数据出现,永远不允许作为模板代码的一部分被渲染。模板引擎的沙箱也靠不住,不能完全依赖。

2.3 一个真实排查链路:日志里的SQL语法错误

有一回一个订单查询接口时不时返回 500,报错日志里有一条 SQL syntax error。我们第一反应是数据问题,结果把 SQL 打出来一看,发现问题出在新增的排序功能上:产品经理要求支持按多个字段排序,开发图省事,直接把前端传的 sort_field 拼进了 SQL 尾部 append 的排序子句。看起来人畜无害的“赋值写死”和“几个选项里选一个排序字段”,最后因为接口要支持任意字段,变成了一个隐藏的注入点。

排查过程不复杂,但很有代表性:查日志定位到报错语句,对比代码发现普通查询走了 ORM 的参数化,唯独排序字段因为“参数化不了”用了字符串拼接。更麻烦的是,我们在线下安全扫描里没有发现这个点,因为扫描器很难自动猜出这个参数可以拼接。修复方案是典型的白名单映射:排序字段先从一个允许列表里取值,取不到就返回默认排序,而不是把用户输入当成字段名直接用。

# 修复思路:排序字段白名单映射 ALLOWED_SORT_FIELDS = { "created_at": "created_at", "updated_at": "updated_at", "status": "status", } sort_field = ALLOWED_SORT_FIELDS.get(request.form.get("sort_field", "created_at"), "created_at") # 排序方向也只允许 asc/desc direction = request.form.get("direction", "desc") direction = direction if direction in ("asc", "desc") else "desc"

这个案例给我的经验是:安全修复和稳定性修复往往是同一件事。参数校验缺失导致的问题,不只是安全风险,还会让系统在异常输入下直接 500,日志都被刷屏。写代码时多写一层白名单校验,既防了注入,也防了脏数据。

3. 认证与会话安全:登录功能是最值钱的攻击面

3.1 密码存储:哈希加盐,不是加密

登录模块是整个业务系统里攻击者最想拿下的地方,而密码存储方式直接决定了用户数据泄露时的损失上限。很多程序员会把“加密”和“哈希”混为一谈,其实这是两回事。加密是可逆的,有密钥就能还原成明文,所以数据库一旦泄露,加密密码和明文差别不大;哈希是不可逆的,同一份输入每次输出的长度固定、结果固定,但不应该能还原出原文。

正确的密码存储方式是哈希加盐。哈希算法要选专门为密码设计的慢哈希算法,比如 bcrypt、scrypt、Argon2,不要用 MD5、SHA1 或者裸的 SHA256。这些普通哈希算得太快,攻击者可以用 GPU 每秒算几十亿次,再大的密码库都能被穷举。盐的作用是让同一个密码在不同用户那里产生完全不同的哈希结果,从而破坏彩虹表和批量破解。bcrypt 的 cost 参数建议设置在 10 到 12 之间,太低了容易被暴力破解,太高了会拖慢正常登录响应。Python 里用 bcrypt 库写起来很简单:

import bcrypt # 注册时:生成盐并哈希 password = request.form["password"].encode("utf-8") hashed = bcrypt.hashpw(password, bcrypt.gensalt(rounds=12)) # 登录时:校验 is_valid = bcrypt.checkpw(password, hashed)

这里有个小坑:bcrypt 只处理前 72 字节,超过部分会被截断,导致两个超长且前缀相同的密码可能被判定为同一个密码。处理方式可以是在哈希前先做一次 SHA-256 再交给 bcrypt,但这需要统一方案,不能一部分用户用旧逻辑一部分用新逻辑。如果系统里已经存了一批弱哈希甚至明文密码,别等大版本,可以先在登录流程里做无感升级:用户登录成功时检测到旧哈希格式,立刻用 bcrypt 重新哈希并更新存储,下一次登录就用新算法校验了。

3.2 会话与Token管理:过期、刷新和固定会话

登录之后系统怎么识别你是你,这就是会话管理的问题。最基础的场景是 Session,服务端给用户发一个 session_id,存到 Cookie 里,用户后续请求带上这个 ID。这里有几个容易被忽略的点:session_id 的随机性要够强,不能用自增数字或者可预测的短随机串;登录成功之后必须重新生成 session_id,防止会话固定攻击;Cookie 要加上 HttpOnly、Secure、SameSite 属性。HttpOnly 是防止 JavaScript 读取 Cookie,Secure 是保证只在 HTTPS 下传输,SameSite 是限制跨站请求携带 Cookie,这三个属性可以在设置 Cookie 时直接带上。

Token 体系里最流行的是 JWT,但 JWT 的坑也最多。HS256 算法依赖一个服务端密钥,这个密钥如果太弱就会被爆破;过期时间如果设成 7 天甚至 30 天,泄露一个 Token 等于泄露了长期访问权限;很多实现里把 user_id 直接编码进 Token,还不校验用户是否被禁用;最要命的是,很多系统根本没有退出登录的吊销机制,Token 发出去了就收不回来。我建议的落地参数是:短期 Access Token 15 分钟到 1 小时,长期 Refresh Token 7 天,Refresh Token 在刷新时必须轮换,旧的直接作废;Token 尽量放到 HttpOnly Cookie 里而不是 localStorage,因为 localStorage 里的一点 XSS 就可能导致 Token 被偷,而 Cookie 配合 HttpOnly 可以挡住这一条路。当然放到 Cookie 会带来 CSRF 的考量,下一章会讲,安全从来不是单点问题,是层层叠加的问题。

4. 权限与业务逻辑:越权漏洞比注入更难防

4.1 水平越权与垂直越权:两个场景

越权漏洞是我在实际业务系统里看到发生率最高的漏洞类型,远高于注入。它的特点是不需要什么高深技巧,纯粹是逻辑问题。水平越权指用户 A 操作了用户 B 的数据,比如用户 A 登录后想查看订单详情,请求里带上订单 ID,后端直接按这个 ID 查库并返回:

# 错误示范:只校验登录,不校验归属 order_id = request.form["order_id"] order = db.query_one("SELECT * FROM orders WHERE order_id = %s", (order_id,)) return render_order(order)

用户 A 只要把 order_id 换成 B 的订单号,就能看到别人的订单。正确做法是多查一层归属条件:查询时强制加上当前登录用户的 ID。垂直越权则是指普通用户调用了管理员的接口,比如管理端接口没有校验角色,只校验了登录状态,普通用户只要能拿到管理接口的 URL 就能执行操作。修复的核心是每一个接口都要做身份与权限校验,不能只做在菜单和路由上。

越权类型错误场景修复要点
水平越权通过遍历 ID 访问他人数据数据查询强制关联当前用户
垂直越权普通用户调用管理员接口每个接口校验角色权限

4.2 后端鉴权原则:永远不要相信前端传参

有些开发会在前端隐藏按钮、禁用操作入口、用路由守卫拦截页面,以为这样后端就安全了。但这些全都可以绕过,攻击者直接发请求就行,根本不经过你的前端页面。后端的每个接口都必须在服务端独立做权限校验,这是铁律。校验逻辑最好封装成统一的装饰器或中间件,不要在每个接口里手写一遍,否则很容易漏掉某一个接口。我见过一个后台系统,列表接口校验了权限,但导出 Excel 的接口忘了校验,普通用户直接调导出接口,把全量数据拖走了。这个教训我记到现在。

权限模型上,中小团队用 RBAC 就够,给用户分配角色,角色关联一组权限;再复杂的场景可以上 ABAC,根据用户、资源、环境等属性动态判断。但不管用什么模型,核心原则是同一个:每个请求到达后端时,先问两个问题,第一这个用户是谁,第二这个用户对当前操作目标有没有权限。我自己的自测习惯是多准备两个普通测试账号,专门用来互查数据,模拟用户 A 访问用户 B 的资源场景,一旦发现能查到对方的数据,立刻定位后端逻辑问题。

5. 浏览器侧攻击:XSS、CSRF和浏览器安全机制

5.1 XSS:存储型、反射型、DOM型,输出编码是关键

XSS(跨站脚本攻击)的原理是攻击者的脚本被当成页面内容执行了。比如一个博客评论功能,用户在评论里写了<script>alert(1)</script>,如果站点没有对评论做转义,所有打开这个页面的用户都会执行这段脚本。危害远不止弹个框这么简单,脚本可以窃取用户 Cookie、篡改页面内容、伪造登录框、在用户不知情的情况下发起请求。

XSS 分存储型、反射型和 DOM 型。存储型是攻击脚本存进数据库,每次访问都执行,危害最大;反射型是攻击脚本通过 URL 参数带入,服务端没处理就原样返回页面;DOM 型是攻击脚本在前端 DOM 操作中被执行,不经过服务端。它们有一个共同点:用户输入只有在进入页面时没有按内容类型做正确的编码转义,才会变成可执行的脚本。

修复思路分三层。第一层是输入侧做校验,但输入校验只是辅助,不能把它当主要防线,因为有些场景就是允许用户输入特殊字符,比如富文本编辑器。第二层是输出侧做编码,这是真正的关键,模板引擎默认会自动转义,问题是很多人用了 innerHTML、v-html、dangerouslySetInnerHTML 这类接口,相当于绕过转义让用户输入直接当 HTML 执行。第三层是兜底方案,配置 CSP,让浏览器只执行白名单里的脚本来源。

5.2 CSRF:同源策略为什么挡不住它

CSRF(跨站请求伪造)是和 XSS 经常被一起提起,但完全不同的漏洞。CSRF 的核心危害在于:用户登录了银行网站 A,然后又在另一个标签页打开了恶意网站 B,B 页面上的脚本或表单向 A 发起请求,浏览器会自动把 A 的 Cookie 带上。A 的服务器看到这个带 Cookie 的请求,就认为是用户本人的操作,于是转账、改密码、发帖,全被攻击者“代劳”了。

很多新手会问,同源策略难道不管用吗?这里有一个关键区别:同源策略限制的是浏览器读取跨域响应,但不限制发送请求。攻击者发一个表单提交,确实读不到服务器的返回结果,但操作已经发生了。要想判断这个 POST 请求是不是用户主动发起的,需要另外的手段。

修复 CSRF 的标准做法是使用 CSRF Token:服务端在渲染表单时生成一个随机 Token 存进 Session,用户提交时把 Token 一起带回来,两边一致才放行。更省事儿的现代方案是设置 Cookie 的 SameSite 属性:SameSite=Lax 可以阻止大多数跨站请求携带 Cookie,至少挡住 GET 以外的跨站提交;SameSite=Strict 更严格,但会影响一些正常的跨站跳转导航,需要权衡。还有校验 Origin 和 Referer 的做法,但这两个头有时会被浏览器限制或为空,容易误伤,所以 Token 仍然是主流。

5.3 CSP与安全响应头:给浏览器写一份安全白皮书

CSP(内容安全策略)是浏览器提供的一套白名单机制,通过响应头告诉浏览器:这个页面只能加载哪些来源的脚本、样式、图片。一旦配置生效,即使 XSS 能把脚本标签插进页面,浏览器也会拒绝执行非白名单来源的脚本,这是 XSS 的一道非常硬核的兜底防线。一个最小可用的 CSP 配置长这样:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'

解读一下:default-src 'self' 表示所有资源默认只允许同源加载,script-src 'self' 表示脚本只能从本站加载,object-src 'none' 禁止插件,frame-ancestors 'none' 禁止被 iframe 嵌套,这同时把点击劫持也挡掉了。实际项目里如果用了第三方统计脚本、CDN 资源、内联脚本,CSP 配置会复杂一些,所以上线时可以先加一个Content-Security-Policy-Report-Only头,只收报告不拦截,观察几天再切换成强制模式。

除了 CSP,几个常用的安全响应头也值得固定到网关层。X-Frame-Options 防点击劫持,X-Content-Type-Options: nosniff 防止浏览器对响应类型进行猜测性解析,Referrer-Policy 控制外链请求时携带多少 URL 信息。Nginx 里可以集中加上:

add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always;

浏览器侧的防线是层层叠加的,响应头只是最后一道闸门,前面的输入校验、输出编码、Token 机制该做的都得做。只有一个原则:不要指望单层防线挡住所有攻击。

6. 入口级漏洞:文件上传、SSRF与反序列化

6.1 文件上传:后缀检查远远不够

文件上传功能是很多业务系统的标配,头像、附件、导入模板都走这里,但这也是攻击者特别喜欢试探的入口。最经典的攻击是把可执行脚本传上去并访问到它,脚本在服务器上跑起来,整个服务器就沦陷了。还有更隐蔽的玩法:上传一个 HTML 文件到你的域名下,利用同源关系做钓鱼;上传一个恶意 SVG 文件,里面内嵌脚本,在有 SVG 渲染能力的页面里触发 XSS;上传同名文件覆盖已有文件;甚至狂传文件把存储空间塞满。

后缀名黑名单基本是最弱的方案,攻击者可以双写后缀、大小写绕过、上传无后缀文件再配合解析漏洞。正确的做法是一整套组合拳:后缀名白名单,只允许业务需要的图片或文档类型;校验文件的真实 MIME 类型和文件头,而不是只看 HTTP 请求里的 Content-Type;给文件重新生成随机文件名,彻底放弃用户传入的原始文件名;文件存到独立的对象存储或与 Web 根目录隔离的目录,并保证该目录不支持脚本执行;限制单文件大小和总存储量。图片类上传最稳的是服务器重新解码一次图片,相当于清洗了一遍,但要注意不要解出超大图片把自己内存打爆。

6.2 SSRF:服务端请求被当成跳板

SSRF(服务端请求伪造)是近几年讨论热度特别高的漏洞,名字听着拗口,但原理很直接:服务端帮用户去请求了一个 URL,而这个 URL 能被用户控制。常见的触发场景是 webhook 回调、图片抓取、URL 转 PDF、社交平台链接预览。用户把 URL 填进去,服务端去访问,如果没有任何限制,服务端就成了攻击者的代理,可以拿着你的服务器身份去扫描内网、访问云平台元数据服务。

防御 SSRF 的核心思路是切断用户对目标地址的完全控制。做 URL 白名单是最先想到的方案,但域名白名单会被 DNS 重绑定和重定向绕过,所以还要组合:限制协议只能 http/https;在服务端做 DNS 解析后检查解析出的 IP 是否落在私网段;不允许跟随重定向,或者每次重定向都重新校验一次;访问超时时间设短,避免被用来拖慢服务。代码层面可以用 socket 先解析 IP 再判断:

import socket from urllib.parse import urlparse def check_url_allowed(url): hostname = urlparse(url).hostname ip = socket.gethostbyname(hostname) # 这里可以增加私网 IP 段判断,如 10.x.x.x, 192.168.x.x, 172.16.x.x if is_private_ip(ip): raise ValueError("目标地址不允许访问") return ip

这段代码只是示意,真正的完整方案还需要考虑 IPv6、DNS 解析多个结果等细节。但核心思路很清楚:不要让用户完全决定服务端该访问哪里。

6.3 反序列化:数据到对象的危险一跃

反序列化漏洞是那种听起来很专业、实际影响也极大的问题。很多语言提供把对象序列化成字节流再恢复成对象的能力,但反序列化过程中,如果数据里包含了可触发的魔法方法、析构函数或构造链,攻击者就可能构造一条调用链,让服务端在恢复数据时执行任意代码。Java 的 ObjectInputStream、Python 的 pickle、PHP 的 unserialize 都属于这类高危接口。

这类漏洞对程序员的启示不是让你去背利用链,而是让你建立一条红线:永远不要对不可信数据做原生反序列化。优先用 JSON、MessagePack 这类纯数据格式,它们只表示数据没有代码执行能力。如果业务确实需要跨语言、跨版本的对象传输,加一层签名校验保证数据来源可信,同时在反序列化端做类的白名单限制,只允许自定义的少数几个类被恢复。这里要特别提醒:依赖库里的老版本反序列化实现也可能出问题,升级依赖本身就是在补安全债。

7. 供应链与工程化安全:把安全从“运维的事”变成开发日常

7.1 依赖漏洞:开源组件里的定时炸弹

很多程序员的眼里只有自己写的代码,感觉自己写的部分没问题就安全了,但实际上现代应用里 80% 以上的代码来自第三方依赖,这些依赖里任意一个存在漏洞,你的系统就跟着遭殃。之前影响面极大的日志组件漏洞,只要项目里引入了受影响版本就可能中招,哪怕你一行相关的业务代码都没写过。依赖管理不是选个包管理器就完事,而是一条持续的安全检查流程。

工具层面,前端可以用 npm audit,Python 可以用 pip-audit,通用型的依赖检查工具推荐 OWASP Dependency-Check,它在 CI 里能扫描 Java、.NET、Python 等多种项目的依赖并生成漏洞报告。GitHub 的 Dependabot 会自动检测已知漏洞并提交升级 PR,Snyk 则是商业化做得比较成熟的依赖安全平台。

工具适用场景特点
npm auditNode.js 项目集成在 npm 中,开箱即用
pip-auditPython 项目命令行即可扫描
OWASP Dependency-Check多语言项目开源免费,适合 CI 集成
DependabotGitHub 项目自动检测并提 PR
Snyk多语言/容器覆盖广,支持漏洞链路分析

落地建议有几个:依赖锁文件一定要入库,保证所有人装到的版本完全一致;升级依赖要纳入排期,不能只在做新功能时顺手升;企业里可以用私有仓库镜像做统一代理,既方便内网安装,也方便在中间层做一次版本控制。最近几年供应链安全的关注度越来越高,国际上开始推行 SBOM 软件物料清单,相当于把项目里每个依赖、每个组件都列成一份清单,出问题的时候能快速定位影响范围。这个意识值得从现在就开始养。

7.2 最小安全检查清单:在CI和代码评审里落地

安全要做进开发流程,而不是等上线前找安全团队扫一遍。我建议每个项目至少在 CI 里加三道工序:静态代码扫描(SAST)、依赖漏洞扫描、基础镜像扫描。静态扫描工具有很多,比如 SonarQube、Semgrep,它们能在代码提交后自动跑一遍,发现明显的注入、硬编码密钥、危险函数调用问题。依赖扫描上面已经说过,镜像扫描可以用 Trivy 或 Grype,扫描出镜像里操作系统和第三方库的漏洞列表。

除了工具,代码评审时也可以有一份最小自查清单,我把自己常用的十条列在这里,可以直接抄:

  1. 登录相关:密码是否用了慢哈希加盐,是否校验旧密码强度。
  2. 会话相关:登录后是否重新生成 session_id,Token 过期策略是否合理。
  3. 权限相关:每个后端接口是否都校验了当前用户身份和资源归属。
  4. 注入相关:所有外部输入是否都走了参数化,排序、表名等特殊位置是否用了白名单。
  5. 前端输出:用户输入在渲染时是否被正确编码,有没有绕过转义的危险 API。
  6. 浏览器安全:CSP、X-Frame-Options、X-Content-Type-Options 等响应头是否配置。
  7. 文件上传:后缀、MIME、文件头、随机文件名、存储隔离是否都做了。
  8. 服务端请求:URL 请求类接口是否限制了协议、目标 IP 和重定向。
  9. 反序列化:是否有对不可信数据的原生反序列化调用。
  10. 依赖安全:依赖锁文件是否入库,CI 是否有依赖漏洞扫描。

有一个容易被轻视的点是:安全扫描和主动测试一定要在测试环境做,而且要在授权范围内做。很多安全意识不错的团队想验证自己的系统,结果在不该扫描的环境里跑了一遍扫描器,给自己惹了麻烦。测试环境的验证、生产环境的主动扫描,这两件事的边界要非常清晰,生产环境的安全验证应该交给有授权的专业团队。

最后说一个我的个人体会。真正让我从对安全无感变成下意识担心的,不是看了多少漏洞报告,而是某天半夜收到线上告警,发现一个没人注意的内部工具接口被自动化脚本扫了一遍。那次之后我给自己定了一条规矩:每次新建接口,先把“这个接口被恶意调用会发生什么”写进实现代码的注释里,再把出手权限校验和输入校验的失败路径写清楚。安全本质上不是一个独立岗位的职责,也不是上线前的某个环节,它只是代码评审清单里多出来的一行字。当你把它当成普通工程问题去处理时,会发现并没有想象中那么难。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 4:29:39

Vibe Coding退烧后:用全局MD文档和规格驱动重构AI编程工作流

说句实话&#xff0c;我到现在还记得 Vibe Coding 这个词刚火起来的那阵子。2025年初&#xff0c;AI 编程从"帮你补全函数"直接跳到了"你用大白话描述需求&#xff0c;它当场给你把整个功能写完"。前一阵圈子里铺天盖地都是"我不用手写代码了"&q…

作者头像 李华
网站建设 2026/9/16 4:29:12

MDBT42Q-AT2与R7KA8D2KFLCAC双芯片BLE系统设计指南

1. 为什么选 MDBT42Q-AT2 R7KA8D2KFLCAC 这对组合&#xff1f;不是 STM32ESP32&#xff0c;也不是 Nordic nRF52840我第一次看到这个组合时也愣了一下——MDBT42Q-AT2 是瑞萨&#xff08;Renesas&#xff09;旗下 Dialog Semiconductor 的超小型 BLE 模块&#xff0c;而 R7KA8…

作者头像 李华
网站建设 2026/9/16 4:28:35

x86架构下Docker离线安装与中间件部署实战指南

干过几次内网交付项目之后&#xff0c;我对“docker离线安装”这几个字真的又爱又恨。爱的是&#xff0c;一旦把离线环境打通&#xff0c;后面部署中间件简直行云流水&#xff1b;恨的是&#xff0c;第一次操作时&#xff0c;光是把docker装起来&#xff0c;就可能卡在依赖、架…

作者头像 李华
网站建设 2026/9/16 4:28:16

AMR磁角度传感器KMZ60与R7KA8D2KFLCAC高精度电机定位实战

1. 这不是“又一个角度传感器”——KMZ60与R7KA8D2KFLCAC组合的真实定位价值你手头那块标着“高精度磁角度测量”的开发板&#xff0c;很可能正在用12位ADC读取霍尔电压&#xff0c;再靠查表法拟合角度&#xff0c;误差动辄1.5——这在伺服电机闭环控制里&#xff0c;意味着转子…

作者头像 李华
网站建设 2026/9/16 4:28:04

操作系统第2章进程与线程:核心考点、高频误区与得分点详解

期末复习也好&#xff0c;考研冲刺也好&#xff0c;操作系统第2章“进程与线程”基本是整门课的命脉。这一章如果吃透了&#xff0c;后续的内存管理、文件系统&#xff0c;甚至设备管理学起来都会顺很多&#xff1b;反之&#xff0c;如果连进程和线程的关系、PCB里装了什么、信…

作者头像 李华
网站建设 2026/9/16 4:28:00

基于CSPNet的轻量级火灾检测模型设计与部署

简介&#xff1a;本资源是一套基于卷积神经网络的火灾实时检测系统实现方案&#xff0c;面向深度学习初学者、计算机视觉实践者及安防类项目开发者&#xff0c;解决图像与视频流中火灾目标识别与声光报警联动的实际问题。压缩包共10个文件&#xff0c;含2个核心Python脚本&…

作者头像 李华