一、什么是跨站请求伪造(CSRF)
跨站请求伪造(Cross-Site Request Forgery,简称CSRF)是一种针对网站的恶意利用方式,也被称为“One Click Attack”或“Session Riding”。
CSRF攻击通过伪装来自受信任用户的请求,利用受信任的网站执行未授权的操作。尽管CSRF听起来与跨站脚本(XSS)相似,但两者的攻击方式和原理大相径庭。XSS利用站点内的信任用户,而CSRF则通过用户浏览器冒充用户身份向服务器发送伪造请求。
二、CSRF攻击原理
CSRF攻击的基本原理是攻击者诱使已经登录的用户(例如在银行网站上)在不知情的情况下执行攻击者预设的操作。CSRF攻击通常涉及三个角色:受害者(合法用户)、攻击者(恶意网站)和目标网站(存在CSRF漏洞的网站)。以下是CSRF攻击的典型过程:
1、用户登录:受害者打开浏览器,访问受信任的网站A,输入用户名和密码登录。
2、生成Cookie:登录成功后,网站A生成Cookie信息并返回给浏览器,用于记录用户的登录状态。
3、访问恶意网站:受害者在未退出网站A之前,在同一浏览器中打开一个新的标签页,访问攻击者构建的恶意网站B。
4、发送伪造请求:网站B包含攻击性代码,发出一个请求要求访问第三方站点A。浏览器在用户不知情的情况下携带Cookie信息,向网站A发出请求。
5、请求处理:网站A根据用户的Cookie信息以受害者的权限处理该请求,导致来自网站B的恶意代码被执行。
三、CSRF攻击的危害
1、利用已通过认证的用户权限更新设定信息:如更改密码、修改个人信息等。
2、利用已通过认证的用户权限购买商品:在用户不知情的情况下进行消费。
3、利用已通过认证的用户权限在留言板发表言论:发布不良信息等。
四、模拟一次CSRF攻击
虽然在实际环境中模拟CSRF攻击可能涉及法律和道德问题,但我们可以通过理论描述来模拟这一过程。
攻击场景
假设有两个网站:
网站A:一个受信任的银行网站,用户已经登录并持有有效的Cookie。
网站B:一个恶意网站,由攻击者控制。
步骤
1、用户登录网站A:用户输入用户名和密码,成功登录后,网站A在用户浏览器中设置了一个Cookie,用于维持会话。
2、用户访问网站B:用户在同一浏览器的另一个标签页中访问了网站B。
3、网站B发起请求:网站B包含一段恶意代码,该代码在用户不知情的情况下向网站A发起一个请求(如转账请求)。
4、浏览器发送请求:由于用户的浏览器仍然保持与网站A的会话,该请求会带上用户的Cookie,使得请求看起来是合法的。
5、网站A处理请求:网站A接收到请求后,根据Cookie中的用户信息执行相应的操作(如转账),而用户对此一无所知。
五、如何防止CSRF攻击
为了防止CSRF攻击,可以采取以下措施:
1、使用CSRF Token
这是最常见和有效的防御CSRF攻击的方法。服务器生成一个唯一的令牌(Token),并将其嵌入到每个表单或请求中。服务器在处理请求时验证令牌的有效性。在Spring Boot中,可以通过配置CSRF保护来实现这一点。这种方式需要服务器存储和管理Token。
示例(Spring Boot)
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().and().authorizeRequests().anyRequest().authenticated();
}
}
在表单中包含CSRF令牌:
<form action="/submit" method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<!-- 其他表单字段 -->
<button type="submit">Submit</button>
</form>
2、验证HTTP请求的来源
服务器可以检查HTTP请求的来源地址(如Referer头),以确保请求来自预期的来源。然而,这种方法并不总是可靠的,因为Referer头可以被伪造或删除。现在较少使用。
示例代码(Java Servlet)
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
publicclass CsrfFilter implements Filter {
privatestaticfinal String TRUSTED_REFERER = "https://trusted.com";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String referer = httpRequest.getHeader("Referer");
if (referer != null && referer.startsWith(TRUSTED_REFERER)) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_FORBIDDEN, "CSRF protection");
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
3、设置SameSite Cookie属性
SameSite Cookie属性可以防止浏览器在跨站点请求中发送Cookie。当设置SameSite属性为Strict或Lax时,浏览器将不会在跨站点请求中发送Cookie。这是一种有效的防御CSRF攻击的方法,但需要浏览器支持。
示例代码(Java Servlet)
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
publicclass SameSiteCookieFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
Cookie[] cookies = ((HttpServletRequest) request).getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
cookie.setHttpOnly(true);
cookie.setSecure(true);
cookie.setPath("/");
cookie.setMaxAge(3600);
cookie.setSameSite("Strict"); // 或者 "Lax"
httpResponse.addCookie(cookie);
}
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
4、其他措施
双重提交Cookie:服务器在响应中设置一个CSRF令牌Cookie,并在每个请求中要求客户端将该令牌作为请求参数发送回来。服务器验证请求中的令牌和Cookie中的令牌是否匹配。这种方式不需要服务器存储Token,但需要通过客户端代码实现。
使用HTTPS:确保所有通信都通过安全的HTTP(HTTPS)进行,以避免中间人攻击。
实施内容安全策略(CSP):CSP可以帮助减少XSS(跨站脚本)的风险,从而间接减少CSRF攻击的风险。
六、结论
CSRF攻击是一种严重的网络安全威胁,但通过采取适当的防范措施,如使用CSRF Token、验证HTTP请求的来源、设置SameSite Cookie属性等,可以有效地减少CSRF攻击的风险。在Java开发中,可以利用Spring Security等框架来方便地实现这些防范措施,从而保护Web应用的安全。