今天呀,我想当一名黑客,去黑别人的网站!我有两三技能,独乐不如众乐乐,今天我也把这个几个攻击手段教给你,咱们一起搞事情去。
首先我们来了解一下攻击手段,也是比较常见的两种攻击手段了:
CSRF
全称:Cross-site request forgery,跨站请求伪造。原理是:通过伪装成受信任用户的请求来攻击受信任的网站。
如何伪装?如何才算攻击?
生活中其实我们不缺这种例子,比如说我们经常接收到一些来历不明的垃圾短信,短信内容里面有个url链接,有些人手贱点开了链接,然后就发现钱不见了!!
我们从技术角度来复原一下这个过程,首先设定一些基础:
然后用户动作是:点开了垃圾网的链接,但是存钱网里账户的钱不见了。既然是自己账户的钱不见了,所以这里其实有个前提:用户已经登录了存钱网!所以准确来说用户的动作是这样的:点开了垃圾网的链接,但是之前登录过的存钱网里账户的钱不见了!
两个网站毫无关联,为啥会造成这个让人意想不到的后果呢?
其实呀,垃圾网的人为了达到攻击的目的,偷偷在网页上嵌入了存钱网的链接,所以打开垃圾网时候顺便也触发了存钱网的转账的链接,整体逻辑如下:
说到这里,你发现漏洞在哪里没有?大家都知道cookie代表用户身份,每次发起请求,请求头里都会附上用户的cookie信息,既然cookie是存在浏览器的,我偷不到你的cookie,那么我就让你在不知道到的情况下让你自己去操作。
举个例子:假如一家银行转账操作的URL地址如下:
http://www.cunqianwang.com/zhuanzhang?account=A&for=B&amount=500
那么,一个垃圾网中可以放置如下代码
<img src="http://www.cunqianwang.com/zhuanzhang?account=A&for=B&amount=500">
好了,原理和攻击手段我们都懂了,那么我们来说说几种常见的预防手段:
HTTP头中有一个Referer字段,这个字段是用来标明请求来源于哪一个网址。当网站A去访问网站B的资源时候,链接上的请求头上就会有Referer字段。注意是在不同域名下才有。
我随意打开hao123.com的首页,一些图片不是放在hao123.com域名下的,所以会在header中带上Referer字段表示请求源是hao123.com。
那么服务器可以通过判断Referer字段来判断请求的来源。所以在垃圾网站里访问存钱网,Referer的值就是垃圾网的域名,就能判断是不是合法的操作啦。
java代码里获取Referer字段值代码是:
String referer = request.getHeader("Referer");
这种方法简单易行,但也有其局限性。http协议无法保证来访的浏览器的具体实现,可以通过篡改Referer字段的方式来进行攻击,所以就要看你用的浏览器高级不高级了,如果你用的浏览刚好是骗子开发的浏览器,嘿嘿~~
既然我们要判定用户行为的合法性,那么我就给用户颁发一个合法token,除了带上cookie,还得带上token才行,token在前一个步骤中获取。
逻辑如下:
重要步骤添加验证码认证后才能操作。脑补,略略略略~
好了,作为一名出色的黑客,必须要知道自己攻击手段的漏洞在哪,怎么防御,绝不做无用功!既然预防手段我知道了,那么接下来就是我展现真正技术的时候了。
嘿嘿,很多公司在一开始的时候为了节约成本,选择用开源项目作为基础,然后再二次开发。虽说开发快,但其实未必安全,一些开源项目如果没有做csrf的预防,那么漏洞就一直存在。
经过我多天的研究,终于发现了某个商城用的是开源项目二次开发的,没有csrf预防。商城的积分可以直接赠送给别人,我立马搞了个网页,嵌入网站赠送积分的链接。
于是有了我和我朋友的对白。
当黑客感觉真好,小明,你是个好人~
XSS
全程:Cross Site Scripting,中文:跨域脚本攻击。原理:不需要你做任何的登录认证,通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等),类似于SQL注入。
通俗点讲就是:恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。
讲再细点其实就是:利用输入内容来闭合对应的html标签,从而执行输入内容的脚本。
xss有两种形态(网友总结):
发出请求时,XSS代码出现在url中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,所以叫反射型XSS。
存储型XSS和反射型XSS的差别在于,提交的代码会存储在服务器端(数据库、内存、文件系统等),下次请求时目标页面时不用再提交XSS代码。
不管是什么类型,你get到关键点没有?关键点以及技术难点其实在于如何往页面中嵌入恶意的代码。
下面我们来写个例子模拟一下:首先我页面写了个form表单:
<form action="/submit" method="post">
名称:<input name="name" value="${name}">
<input type="submit">
</form>
controller中有个基本跳转,还有form表单的提交:
@GetMapping("")
public String index(HttpServletRequest request) {
request.setAttribute("name", "公众号:java思维导图");
return "index";
}
@PostMapping("/submit")
public String submit(HttpServletRequest request) {
String name = request.getParameter("name");
System.out.println("name---------->" + name);
// 假装只有名字为“求关注”才能通过
if(!name.equals("求关注")) {
request.setAttribute("name", name);
}
return "index";
}
初始效果如下:
ok,基本逻辑也写好了,一个简单的表单提交,提交之后如果数据不对,或格式不对就会返回表单页面,同时回显表单数据。
加入我想嵌入脚本如下:
<script>alert(1);</script>
那么我该怎么样才能往这个页面上嵌入代码呢?我打开F12,研究一下
要是这个这个脚本能提到input的外面,value能提前结束就好了。嘿嘿,突然想到,既然我改不了原来的,那么我就创造一个。
于是我改了一下输入的值成:
"><script>alert(1);</script>
这">不就跑到前面了嘛,哈哈哈,天才,我赶紧试试。谷歌浏览器测试结果如下:
脚本的确跑到外面了,但是alert(1)怎么不见了呀,我赶紧调试一下:
不是后端在搞事情,那么真相就只有一个,谷歌浏览器在搞事情,谷歌果然强大,还能辨别我的脚本并和谐掉。
我换个Edge浏览器再试试:
哇,果然Edge你最帅,我想要的你都给我~ F12看下:
没毛病,原声原味的alert(1);
好了上面我们已经弄懂了xss的嵌入脚本的方式,我们输入是合法的,只是内容有点取巧,这就是xss的攻击手段。
除了这个input标签,其实还有很多标签比较常用,比如title、a、img、script等。
上面这个一般都是反射性的xss攻击,我们再来看看一个存储类型的title的例子。
在很多博客中,我们都可以发布文章,我们需要写文章标题,文章内容等,文章标题一般我们还会放在我们的head的title中,用于标签展示当前浏览文章标题。
加入说,我们的页面是这样展示的:
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
</head>
<body>
这是内容 - ${content}
</body>
</html>
而controller中传过来的内容如下:
@GetMapping("/title")
public String title(HttpServletRequest request) {
request.setAttribute("title", "</title><script>alert('公众号java思维导图');</script>");
request.setAttribute("content", "内容是关注公众号:java思维导图");
return "title";
}
最后我们的得到的页面展示这样子:加载时候先执行弹窗:alert("公众号java思维导图");然后再加载内容。
因为一般我们文章标题内容都是保存到数据库的,所以每次渲染都会执行脚本,所以是个存储型xss攻击。
好了,看了我们的例子项目,我们已经意识到了xss攻击的可怕性,一单发布文章都可以写脚本,那么所有的用户打开这篇文章都会被执行脚本,影响可就大了。那么有什么好的解决方法吗?
这里给大家介绍几个解决方法。我们先来看renren-fast项目是怎么解决这个问题的:
#识别攻击脚本、并删掉对应可执行脚本的标签
HTMLFilter
#全局过滤器,包装request
XssFilter
#包装request,重写request的几个重要方法,比如getParameter等
XssHttpServletRequestWrapper
所以renren-fast项目的设计逻辑是加入一个全局过滤器,然后通过包装请求的request,重写request的getParameter、getHeader、getInputStream等方法,在这些方法里面都进行一遍过滤,从而去掉所有的攻击脚本。看看重要代码:
public class XssFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
...
}
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
@Override
public String getParameter(String name) {
String value = super.getParameter(xssEncode(name));
if (StringUtils.isNotBlank(value)) {
value = xssEncode(value);
}
return value;
}
...
}
可以看到上面的xssEncode就是进行过滤脚本的方法;xssEncode方法代码如下:
private String xssEncode(String input) {
return htmlFilter.filter(input);
}
ok,相信你已经弄明白了。
我们来看看另一个博客项目mblog的解决方法:
#通用控制器
BaseController
#自定义编辑器
StringEscapeEditor
mblog项目其实是通过注册所有controller的自定义编辑器,在提交表单时候对所有字段都进行一层get和set,在set的过程中对输入内容进行一番检查,如果有脚本就进行替换等操作。
详细代码如下:
@InitBinder
public void initBinder(ServletRequestDataBinder binder) {
/**
* 防止XSS攻击
*/
binder.registerCustomEditor(String.class, new StringEscapeEditor(true, false));
...
}
@InitBinder用于表单到方法的数据绑定的,这里绑定了一个自定义编辑器StringEscapeEditor。
可以看到setAsText中就是对脚本进行过滤等操作的。
这两种方法都学会了吗?其实逻辑都是对脚本进行过滤替换删除等操作。
好了,又到了黑客show time,某个知名博客平台没防御xss攻击,这时候我发布了一篇文章,title中包含了脚本
<script>alert("刘亦菲,我爱你");</script>
于是,全世界都知道我的表白,我们最后开心得在一起了。
真是浪漫有爱,又爱做白日梦的黑客,鼓掌,鲜花~
结束语
文中涉及到的项目:
好啦,今天的文章先到这里了。