先搞懂OWASP Top 10到底是什么
很多开发者听说过OWASP Top 10,但可能没认真研究过——它是OWASP(开放式Web应用安全项目)每3年更新一次的“Web应用最危险漏洞榜单”,集合了全球数千起真实攻击案例的分析,是Web安全领域的“风向标”。2021年版的Top 10包括:SQL注入、XSS、失效身份认证、敏感数据暴露、XXE、失效访问控制、CSRF、使用已知漏洞组件、不足的日志记录与监控、服务器端请求伪造(SSRF,2021年新增)。搞懂这10个漏洞,就能覆盖Web应用80%以上的安全风险。

SQL注入:最常见也最致命的漏洞怎么防
SQL注入应该是开发者最熟悉的漏洞——攻击者通过在输入框、URL参数里插入恶意SQL语句,比如把“username=admin’ –”代入登录接口,就能绕过密码验证直接登录。2023年OWASP统计,SQL注入导致的攻击占比高达30%,而且一旦成功,攻击者能直接拖走整个数据库。
防范方法就3条,必须严格执行:
1. 用预处理语句(PreparedStatement)代替拼接SQL:不管是Java还是Python,预处理语句会自动转义特殊字符,从根源上杜绝注入。比如Java代码:
// 不安全:直接拼接SQL
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
// 安全:使用PreparedStatement
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username); // 自动转义单引号、分号等字符
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
2. 限制数据库用户权限:别给Web应用的数据库账号“root”权限,只给“SELECT/INSERT/UPDATE”所需表的权限——就算被注入,攻击者也不能删库或访问其他表。
3. 用工具定期扫描:推荐用SQLMap(开源)或Burp Suite(商业)扫描接口,比如用SQLMap检测登录接口:sqlmap -u "http://xxx.com/login?username=admin&password=123" --risk 3 --level 5
,能快速发现注入点。
XSS攻击:别让恶意脚本钻了空子
XSS(跨站脚本攻击)的本质是“攻击者把恶意JavaScript代码注入到Web页面,让其他用户访问时执行”。比如在评论区输入“”,如果网站没过滤,所有看这篇评论的用户都会弹出警告框——更危险的是,攻击者能偷cookie、冒充用户操作。
XSS分3种类型,防范方法各有侧重:
– 存储型XSS(最危险):恶意代码存在数据库里(比如评论、用户资料),每次加载页面都会执行。防范:输入验证+输出编码。比如用Java的org.springframework.web.util.HtmlUtils.htmlEscape
对输入的内容转义:
// 对用户输入的评论进行转义
String safeComment = HtmlUtils.htmlEscape(comment);
// 存入数据库的是转义后的内容:<script>alert('XSS')</script>
– 反射型XSS:恶意代码在URL参数里(比如http://xxx.com/search?q=<script>alert(1)</script>
),用户点链接才会执行。防范:禁止把URL参数直接输出到页面,或者对参数做URL解码+HTML转义。
– DOM型XSS:通过修改页面DOM结构执行恶意代码(比如document.write(location.hash.substr(1))
)。防范:别用不可信数据修改DOM,比如用textContent
代替innerHTML
:
// 不安全:用innerHTML插入不可信数据
document.getElementById('result').innerHTML = location.hash.substr(1);
// 安全:用textContent,自动转义HTML标签
document.getElementById('result').textContent = location.hash.substr(1);
失效的身份认证:账号安全的核心防线
“失效的身份认证”指的是“攻击者能冒充合法用户登录”,比如破解弱密码、窃取JWT令牌、session固定攻击。2022年某电商平台的用户数据泄露事件,就是因为JWT令牌没设置过期时间,攻击者偷到令牌后能永久登录。
防范的核心是“让身份认证不可伪造”:
1. 用多因素认证(MFA):除了密码,再加短信验证码、Google Authenticator动态码——就算密码泄露,攻击者也登不进去。推荐用Auth0或阿里云MFA服务,集成成本很低。
2. 正确使用JWT令牌:JWT(JSON Web Token)是当前主流的身份认证方式,但很多开发者用错了:
– 别把敏感信息(比如密码)存进JWT;
– 设置短有效期(比如15分钟),用“刷新令牌”续期;
– 用HTTPS传输JWT,别存在localStorage(容易被XSS偷),存HttpOnly Cookie更安全。
3. 禁止弱密码:强制用户设置“8位以上+字母+数字+符号”的密码,用bcrypt哈希存储(别用MD5,早被破解了)。比如Python代码:
import bcrypt
# 哈希密码(自动加盐)
password = "MyStrongPass123!".encode('utf-8')
hashed = bcrypt.hashpw(password, bcrypt.gensalt())
# 验证密码
if bcrypt.checkpw(password, hashed):
print("密码正确")
敏感数据暴露:加密和脱敏一个都不能少
“敏感数据暴露”指的是“用户的密码、身份证号、银行卡号等数据未加密,直接存在数据库或传输”。比如某医疗APP把用户病历存成明文,被黑客拖库后,30万用户的隐私数据全泄露——这类事件每年有上百起,罚款能到千万级(比如GDPR的罚款是全球营收的4%)。
防范要做“全链路加密”:
1. 传输加密:用HTTPS代替HTTP,证书用Let’s Encrypt(免费)或DigiCert(商业),确保数据在传输过程中不被窃听。
2. 存储加密:
– 密码:用bcrypt或Argon2哈希(不可逆);
– 敏感信息(比如银行卡号):用AES-256加密(可逆),密钥存在配置中心(别硬编码在代码里);
– 脱敏展示:比如银行卡号显示“6228 * 1234”,身份证号显示“4101 1234”。
3. *别用弱加密算法:比如DES(已被破解)、RSA-1024(不够安全),推荐用AES-256(对称加密)或RSA-2048(非对称加密)。
失效的访问控制:权限管理要“严丝合缝”
“失效的访问控制”指的是“用户能访问超出权限的资源”,比如普通用户能查看管理员的后台,或者删除其他用户的订单。2021年OWASP统计,这类漏洞导致的攻击占比15%,而且很难被发现——因为攻击者是“合法用户”,行为不像注入那样明显。
防范的关键是“每一步操作都检查权限”:
1. 用“最小权限原则”:给用户分配权限时,只给“刚好够用”的权限——比如普通用户只有“查看自己的订单”权限,管理员有“查看所有订单”权限。
2. 用注解或中间件统一拦截:比如Java用Spring Security的@PreAuthorize
注解,在接口层检查权限:
@RestController
public class OrderController {
// 只有ROLE_ADMIN角色能访问
@GetMapping("/admin/orders")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public List<Order> getAdminOrders() {
return orderService.getAllOrders();
}
// 只有订单所属用户能访问
@GetMapping("/orders/{orderId}")
@PreAuthorize("@orderService.isOwner(#orderId, principal.username)")
public Order getOrder(@PathVariable Long orderId) {
return orderService.getOrderById(orderId);
}
}
3. 禁止直接通过URL访问资源:比如别把“/admin”目录暴露给普通用户,用Nginx或Spring Security拦截未授权的请求。
跨站请求伪造(CSRF):别让攻击者“冒充”用户操作
CSRF的原理很“鸡贼”:攻击者诱导用户点击一个链接(比如“点击领红包”),这个链接会向用户已登录的网站发送一个请求(比如“删除订单”)——因为用户的cookie还在,网站会误以为是用户自己操作。比如某电商平台的“删除订单”接口是POST /order/delete?id=123
,攻击者做一个钓鱼页面,里面藏一个form
提交这个请求,用户点一下就会删订单。
防范方法就2个,简单有效:
1. 加CSRF令牌:每个表单或AJAX请求都加一个随机的CSRF令牌,服务器验证令牌是否正确。比如Spring Security会自动生成_csrf
令牌,前端在表单里加:
<form method="post" action="/order/delete">
<input type="hidden" name="_csrf" value="${_csrf.token}">
<button type="submit">删除订单</button>
</form>
2. 用SameSite Cookie:给Cookie设置SameSite=Strict
或SameSite=Lax
,禁止第三方网站发送带Cookie的请求——比如Chrome默认会拦截SameSite=None的Cookie,从根源上阻止CSRF。
使用已知漏洞的组件:别让旧组件拖垮安全
“使用已知漏洞的组件”指的是“Web应用用了有安全漏洞的第三方库或框架”,比如用了Struts 2的旧版本(存在S2-057漏洞,能远程执行代码),或者jQuery 1.6(存在XSS漏洞)。2023年OWASP统计,这类漏洞导致的攻击占比20%,而且修复成本很高——因为要升级组件,还要测试兼容性。
防范的核心是“持续监控组件漏洞”:
1. 用工具扫描组件漏洞:推荐用OWASP Dependency-Check(开源)或Snyk(商业),比如用Dependency-Check扫描Maven项目:
mvn org.owasp:dependency-check-maven:check
它会生成报告,列出项目中用的组件有没有已知漏洞(比如“log4j-core 2.14.1存在CVE-2021-44228漏洞,需要升级到2.17.0”)。
2. 定期升级组件:别因为“怕兼容问题”就不升级——比如log4j的“核弹级漏洞”(CVE-2021-44228),升级到2.17.0就能解决,而很多公司因为没升级被攻击。
3. 删除没用的组件:比如项目里用了Spring Boot,但没用到Spring Security,就把Spring Security的依赖删掉——少一个组件,就少一个攻击面。
不足的日志记录和监控:出了问题得“查得到”
“不足的日志记录和监控”是很多开发者容易忽略的点——就算防护做的好,也可能被攻击,但如果没有日志,根本不知道攻击者干了什么。比如某支付平台被攻击后,因为没记录“删除订单”的操作日志,花了3天多才定位到攻击者。
日志和监控要做到“可追溯、可告警”:
1. 记录关键操作日志:比如用户登录、修改密码、删除订单、支付等操作,要记录“谁(username)、什么时候(timestamp)、做了什么(action)、从哪来(IP地址)”。比如用Logback记录日志:
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>app.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
2. 用ELK Stack做日志分析:把日志收集到Elasticsearch,用Kibana做可视化(比如展示“最近24小时的异常登录次数”),用Logstash做过滤(比如过滤掉机器人的请求)。
3. 设置告警规则:比如“5分钟内同一IP登录失败10次”“管理员账号在非工作时间登录”,用Prometheus或Grafana设置告警,及时通知运维人员。
最后:防护不是一次性活儿,要持续迭代
Web安全的本质是“攻防对抗”——攻击者在不断找新漏洞,防护也得持续更新。比如OWASP Top 10每3年更新一次,2021年新增了SSRF(服务器端请求伪造),2024年可能会新增AI生成的漏洞(比如用GPT生成恶意代码)。
所以,开发者要养成3个习惯:
– 每季度看一次OWASP的更新;
– 每月用工具扫描一次应用(比如Burp Suite、Nmap);
– 每次发版前做一次安全测试(比如 penetration test)。
其实Web安全没那么“高深”——就是把每个漏洞的防护方法做到位,再加上持续的监控和迭代。记住:安全不是“做了什么”,而是“没被攻击成功什么”。
原创文章,作者:,如若转载,请注明出处:https://zube.cn/archives/246