文章目录
- OWASP 2023 TOP 10
- CSRF 导图
- CSRF的基本概念
- CSRF的工作原理
- 常见CSRF攻击模式
- CSRF防御策略
- 补充建议
- 应用场景实战
- 防御策略选择
- 1. CSRF Token(首选)
- 2. SameSite Cookie属性
- 3. 验证Referer和Origin
- 4. 多因素认证
- 实现方案
- CSRF Token实现
- SameSite Cookie设置
- Referer和Origin头验证
- 多因素认证 (MFA)
OWASP 2023 TOP 10
OWASP Top 10 概述
OWASP (Open Web Application Security Project) Top 10 是一份最常见和最危险的Web应用安全风险列表,由安全专家定期更新。 旨在提高开发人员、测试人员以及组织的安全意识并帮助他们预防这些漏洞。
2023年OWASP Top 10 列表
主流防范措施
-
Broken Access Control
- 描述:未能正确执行访问控制,允许用户访问他们不应该拥有的权限或资源。这可能导致数据泄露、数据篡改等问题。
- 防御措施:严格实施基于角色的访问控制(RBAC),并确保敏感操作具有足够的授权检查。
-
Cryptographic Failures
- 描述:不当的加密实践或加密算法的使用不当,可能导致敏感数据(如密码、信用卡信息)被暴露或窃取。
- 防御措施:使用最新的加密标准(如AES-256-GCM、RSA-2048),并避免使用弱或过时的加密算法。
-
Injection
- 描述:应用未能对用户输入进行有效的验证或转义,导致恶意代码注入(如SQL注入、命令注入)并执行在服务器上。
- 防御措施:使用参数化查询、输入验证、输出转义技术,避免拼接SQL或动态代码。
-
Insecure Design
- 描述:系统在设计阶段未考虑安全问题,导致应用架构中的基本安全漏洞。
- 防御措施:在开发生命周期中引入威胁建模、攻击面分析等设计阶段的安全审查。
-
Security Misconfiguration
- 描述:错误的配置(如不安全的默认设置、过时的软件或未配置的安全功能),可能使应用程序面临攻击。
- 防御措施:定期审计和测试系统配置,使用自动化工具识别和修复配置问题。
-
Vulnerable and Outdated Components
- 描述:使用了具有已知漏洞或未及时更新的第三方库和组件,可能被攻击者利用。
- 防御措施:确保使用依赖管理工具(如Maven、npm),并定期更新组件,避免使用过时的版本。
-
Identification and Authentication Failures
- 描述:认证和身份验证流程中的缺陷,可能导致用户冒充、会话劫持等问题。
- 防御措施:实施强密码策略、使用多因素认证(MFA)和加固会话管理机制。
-
Software and Data Integrity Failures
- 描述:未能保证软件更新和数据的完整性,可能使攻击者篡改关键数据或上传恶意更新。
- 防御措施:使用签名机制来验证更新包的完整性,确保数据在传输和存储过程中的可靠性。
-
Security Logging and Monitoring Failures
- 描述:缺乏适当的日志记录和监控,无法有效检测、响应或追踪安全事件。
- 防御措施:实施集中化的日志记录、主动的监控和告警系统,确保能够及时发现并响应异常行为。
-
Server-Side Request Forgery (SSRF)
- 描述:攻击者通过伪造服务器端的请求来获取未授权的内部资源或数据,通常利用未受限制的服务器端请求机制。
- 防御措施:限制服务器端可以发起的请求范围,避免允许用户输入直接控制服务器端的请求参数。
重点风险与防御措施建议
-
Broken Access Control:最重要的防御措施是定期审查权限设计,确保每个用户只能访问必要的资源。建议结合应用的访问控制系统与自动化测试工具,确保权限配置不被篡改。
-
Cryptographic Failures:确保敏感数据加密和密钥管理机制符合行业标准,如使用硬件安全模块(HSM)来保护密钥。避免明文传输或存储敏感数据。
-
Injection:对于Web应用来说,防止注入攻击的最佳实践是始终使用参数化查询和预编译的语句。严禁直接拼接用户输入构建SQL或命令。
-
Security Misconfiguration:安全配置管理应作为持续改进的一部分,尤其是在引入新服务或更新系统时,保持自动化的安全配置审计机制至关重要。
-
SSRF:严格限制后端服务器能够访问的网络和资源,禁止对内部资源(如metadata或本地IP)发起请求。
CSRF 导图
CSRF的基本概念
CSRF (Cross-Site Request Forgery,跨站请求伪造) 是一种攻击方式,攻击者诱导用户在不知情的情况下发起非授权的请求。例如,用户在登录某个站点后,攻击者通过社交工程等手段诱使用户点击包含恶意请求的链接,利用用户已登录的状态,发起用户意图之外的操作,如转账、修改账户设置等。
CSRF的工作原理
- 用户登录受信任的站点A,并保持登录状态(通常通过cookie维持会话)。
- 攻击者在站点B或通过电子邮件等媒介诱导用户点击恶意链接,或者自动加载某些恶意内容。
- 浏览器在访问该恶意内容时自动带上站点A的cookie,导致恶意请求被认为是用户发起的合法请求。
- 站点A由于没有进行足够的防御措施,处理了该请求,执行了不符合用户预期的操作。
常见CSRF攻击模式
- GET请求攻击:攻击者构造包含恶意查询参数的URL,并诱导用户点击或自动加载该URL。
- POST请求攻击:攻击者通过隐藏的表单或JavaScript脚本在后台发送恶意POST请求。
- 第三方内容加载:攻击者通过iframe、img等方式嵌入恶意请求,利用用户的认证状态。
CSRF防御策略
-
使用CSRF Token:在每次受保护的请求中,服务器生成一个唯一的CSRF Token,注入到表单或请求头中。服务器接收到请求时,检查Token的有效性。由于攻击者无法获取该Token,恶意请求将被服务器拒绝处理。
- Synchronizer Token Pattern:服务器在用户的每个会话中生成唯一的Token,嵌入到页面的表单或请求中,服务器在处理请求时验证这个Token。
- Double Submit Cookie:在页面中嵌入一个Token,同时将该Token保存在cookie中,服务器在接收到请求时对比两个Token的值。
-
使用SameSite Cookie属性:通过将cookie的SameSite属性设置为"Strict"或"Lax",限制跨站请求时自动发送cookie。此属性允许服务器防止来自外部站点的请求利用用户的认证状态。
- Strict模式:完全禁止跨站点发送cookie,适用于高度安全要求的应用场景。
- Lax模式:在安全性和用户体验之间做权衡,允许某些跨站请求(如GET请求)发送cookie,但限制POST请求。
-
验证Referer和Origin头:服务器可以检查请求的
Referer
或Origin
头,确保请求来源于合法的站点。虽然不总是可靠,但可以作为额外的安全防线。 -
多因素认证(MFA):对于敏感操作,可以要求用户进行额外的身份验证,如验证码或短信验证,防止攻击者即便利用用户的认证状态,也无法完成关键操作。
-
使用CAPTCHA:通过在敏感请求前引入CAPTCHA,阻止自动化脚本提交伪造请求。
补充建议
- 最小化攻击面:限制敏感操作的请求方法,比如将敏感操作从GET转为POST,并确保所有修改数据的操作都经过授权验证。
- 对API接口的保护:对于RESTful API等接口,应确保请求都包含CSRF防护机制,特别是在允许跨域请求的情况下(如CORS)。
应用场景实战
假设场景为一个典型的Web应用,涉及用户登录、账户操作、表单提交等敏感操作。该应用具备以下特点:
- 用户通过登录操作获得会话,并使用Cookie维持会话状态。
- 存在表单提交和数据修改操作。
- 系统允许跨域请求访问部分资源。
主要风险包括:
- 恶意用户通过构造跨站请求,伪造受害者身份执行操作。
- 特别是在敏感操作(如修改密码、交易等)上,CSRF的风险尤为显著。
防御策略选择
基于应用场景,综合考虑以下策略:
1. CSRF Token(首选)
- 对于所有用户提交表单的敏感操作(如账户设置、转账等),服务器生成并验证CSRF Token。这是最可靠的防御机制。
- 适用范围:几乎所有敏感操作场景,尤其是表单提交。
2. SameSite Cookie属性
- 针对GET请求或跨站资源请求较多的场景,推荐设置
SameSite=Lax
或Strict
。这可以有效防止浏览器自动发送Cookie给外部站点。 - 适用范围:较轻量级的场景,防止未经认证的跨站请求。
3. 验证Referer和Origin
- 对于一些简单场景,可以在服务器端检查Referer和Origin头部,以确保请求来自合法的站点。
- 适用范围:非敏感的、依赖浏览器的操作场景(可以作为辅助手段)。
4. 多因素认证
- 针对特别敏感的操作,如账户资金转移或密码修改,建议在执行操作前增加MFA(多因素认证),如短信验证或二次密码确认。
- 适用范围:极为敏感的用户操作,特别是涉及资金或安全的功能。
实现方案
CSRF Token实现
Spring Security框架自带CSRF防护机制,默认保护所有需要修改数据的HTTP方法(POST、PUT、DELETE)。如果是REST API,可按如下方式进行配置:
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 启用CSRF保护
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// 配置保护哪些请求
http
.authorizeRequests()
.antMatchers("/sensitive-endpoints/**").authenticated()
.anyRequest().permitAll();
}
}
-
CookieCsrfTokenRepository.withHttpOnlyFalse()
会将CSRF Token存储在Cookie中,并在客户端的表单中嵌入,防止跨站请求。
SameSite Cookie设置
可以在Spring Boot中设置SameSite
属性来防御跨站请求:
@Bean
public ServletContextInitializer initializer() {
return servletContext -> {
SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
sessionCookieConfig.setHttpOnly(true); // 防止XSS攻击
sessionCookieConfig.setSecure(true); // 只允许HTTPS请求
sessionCookieConfig.setSameSite("Lax"); // 设置SameSite属性
};
}
- 这里配置了
SameSite=Lax
,确保敏感的POST请求不会被跨站发送。
Referer和Origin头验证
对某些关键操作(如密码修改),可以手动添加对请求头的验证:
@RequestMapping(value = "/change-password", method = RequestMethod.POST)
public ResponseEntity<String> changePassword(HttpServletRequest request) {
String referer = request.getHeader("Referer");
String origin = request.getHeader("Origin");
if (referer == null || !referer.startsWith("https://yourdomain.com")) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Invalid referer");
}
if (origin == null || !origin.equals("https://yourdomain.com")) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Invalid origin");
}
// 执行密码修改逻辑
return ResponseEntity.ok("Password changed successfully");
}
- 在此场景中,只有合法的Referer和Origin请求才会被接受。
多因素认证 (MFA)
针对特别敏感的操作,例如转账或修改密码,可以通过引入短信验证或Google Authenticator等方式实现多因素认证:
@RequestMapping(value = "/transfer", method = RequestMethod.POST)
public ResponseEntity<String> transfer(@RequestParam String token, HttpServletRequest request) {
// 验证MFA Token
boolean isValid = mfaService.verifyToken(token);
if (!isValid) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Invalid MFA token");
}
// 执行转账操作
return ResponseEntity.ok("Transfer successful");
}