在第一章結尾的時候我就已經說了,這一章將會更詳細的介紹前端防火墻的報警機制及代碼。在一章出來后,有人會問為什么不直接防御,而是不防御報警呢。很簡單,因為防御的話,攻擊者會定位到那一段的JavaScript代碼,從而下次攻擊的時候繞過代碼。如果不防御而報警的話,攻擊者會降低警覺,不會在看JavaScript代碼(至少我是這樣)。回到正題,下面說的代碼,是基于thinkphp框架和bootstrap3.3.5框架。如果你的網站沒有使用thinkphp3.2.3框架的話,可以參照我的思路重新寫一個。這里我強調一下“前端防御XSS是建立在后端忘記做過濾,沒有做過濾,疏忽做過濾的基礎上的...
其實標題應該改成“XSS報警機制”的,因為在這一章里使用了大量的后端代碼。但是第一章的標題都出來了,也沒法改了。
前端要做的事情在第一章的時候就已經說了,代碼如下:
現在我們就是針對第38行進行修改,改成我們后臺接受的API URL。就像這樣:
對,就這一行。沒有其他代碼。在實際的線上環境中,也只需要上面5行。可以直接copy到您的線上環境中,記得把倒數第二行的url改成自己的地址就行了。難道就那么簡單?不,0x05節還有一部分前端代碼。0x01~0x04主要是針對于平臺。
一共兩個表。fecm_user和fecm_bugdata。
fecm_user的字段信息如下:
為了安全起見(其實就是懶)沒有寫添加管理員的,自行在數據庫里添加
fecm_bugdata的字段信息如下:
因為后端代碼太多,所以我就說一些核心的后端處理代碼。
在0x01節里,有個核心的代碼是new Image().src = 'http://fecm.cn/Api/addVul/';
接下來我們來說說這個Api的處理方式(ThinkPHP代碼)
#!php
public function addVul(){
if(I('get.category','','int') == ""){
$this->ajaxReturn(array(
"typeMsg" => "error",
"msgText" => "漏洞類型錯誤",
));
}
switch (I('get.category','','int')) {
case '1':
$vul['category'] = "觸發alret函數";
break;
case '2':
$vul['category'] = "發現不在白名單里的第三方JavaScript資源";
break;
default:
$this->ajaxReturn(array(
"typeMsg" => "error",
"msgText" => "漏洞類型錯誤",
));
break;
}
if($_SERVER['HTTP_X_FORWARDED_FOR'] === null){
$vul['hxff_ip'] = "攻擊者沒有通過代理服務器訪問";
}else{
$vul['hxff_ip'] = I('server.HTTP_X_FORWARDED_FOR'); //獲取攻擊者的HTTP_X_FORWARDED_FOR
}
if($_SERVER['HTTP_CLIENT_IP'] === null){
$vul['hci_ip'] = "攻擊者數據包頭部沒有HTTP_CLIENT_IP";
}else{
$vul['hci_ip'] = I('server.HTTP_CLIENT_IP');//獲取攻擊者的HTTP_CLIENT_IP
}
$vul['ra_ip'] = I('server.REMOTE_ADDR'); //獲取攻擊者的REMOTE_ADDR
$vulcookie = I('cookie.'); //獲取攻擊者的cookies
for($i = 0;$i<count($vulcookie);$i++){
$vul['cookies'] .= array_keys($vulcookie)[$i].'='.$vulcookie[array_keys($vulcookie)[$i]].'; '; //拼接成方便查看的cookies格式
}
$vul['url'] = I('server.HTTP_REFERER'); //獲取攻擊者攻擊成功的url
$vul['ua'] = I('server.HTTP_USER_AGENT'); //獲取攻擊者的User-Agent
$vul['time'] = date("Y-m-d"); //獲取攻擊者攻擊的時間
$vul['fixes'] = 0; //默認為漏洞未修復
$bugData = M('bugdata'); //連接fecm_bugdata數據庫
$bugData->data($vul)->add(); //添加到數據庫中
}
因為這里是接受攻擊信息,不能有管理員驗證。
后臺有一個數據庫可視化的表格,這里我使用的Chart.js,下面是后端代碼:
#!php
public function index(){
$reportForm = M('bugdata'); //連接fecm_bugdata數據庫
$dateTimeLabels = [];
$dateTimeTotal = [];
for($i = 0;$i < 7;$i++){ //獲取近7天的數據
$time = date("Y-m-d",strtotime(-$i." day"));
array_unshift($dateTimeLabels,$time);
$data['time'] = array('like','%'.$time.'%');
array_unshift($dateTimeTotal,$reportForm->where($data)->count());
}
$reportForm = json_encode(["Labels" => $dateTimeLabels,"Total" => $dateTimeTotal]); //轉化成json格式
$this->assign('reportForm',$reportForm)->assign('total',total()); //交給前端模塊
$this->display(); //前端頁面生成
}
前端代碼:
#!js
var lineChartData = {
labels :eval({$reportForm})['Labels'],
datasets : [
{
fillColor : "rgba(151,187,205,0.5)",
strokeColor : "rgba(151,187,205,1)",
pointColor : "rgba(151,187,205,1)",
pointStrokeColor : "#fff",
data : eval({$reportForm})['Total']
}
]
}
var myLine = new Chart(document.getElementById("Statistics").getContext("2d")).Line(lineChartData);
實際的效果圖:
代碼就用0x01節的代碼。我們輸入<script>alert(1)</script>
。看一下:
我們再去平臺看一下:
成功顯示了。
這一節需要用到之前長短短分享的代碼:
#!js
for(var i=0,tags=document.querySelectorAll('iframe[src],frame[src],script[src],link[rel=stylesheet],object[data],embed[src]'),tag;tag=tags[i];i++){
var a = document.createElement('a');
a.href = tag.src||tag.href||tag.data;
if(a.hostname!=location.hostname){
console.warn(location.hostname+' 發現第三方資源['+tag.localName+']:'+a.href);
}
}
但是他這里只是在console里顯示,沒有進一步的操作,而且他這里同時檢測了iframe、frame、script、link、object、embed標簽,對我們來說只需要script標簽就行了,于是我重寫了這段代碼,首先我們需要一個白名單列表,用于放置網站允許第三方加載的url地址:
#!js
var scriptList = [
location.hostname,
]
這里只是默認的只允許當前域名加載,打擊愛可以根據自己的需要添加。
然后就是獲取當前網頁的所有script標簽:
#!js
var webScript = document.querySelectorAll('script[src]');
在把當前的地址賦值var webHost = location.hostname;
至于為什么不放在for循環里,因為根據js優化規則,for循環里避免多次一樣的賦值。
接下來就是for循環里的代碼了:
#!js
for(var i = 0;i < webScript.length;i++){
var a = document.createElement('a'); //建立一個新的a標簽,方便取值
a.href = webScript[i].src; //把script里的src賦值給a標簽里的href屬性
if(a.hostname != webHost){ //對比,是否為第三方資源
for(var j = 0;j < scriptList.length;j++){
if(a.hostname != scriptList[i]){ //判斷當前的第三方資源是否在白名單里
new Image().src = 'http://fecm.cn/Api/addVul/category/2'; //發送給FECM
}
}
}
}
這里我做了一個測試,加載hi.baidu.com的資源:
刷新后,打開FECM平臺,看一下:
因為窮,沒有服務器和域名,也沒法添加郵件自動提醒功能了。感興趣的可以自己添加,如果后來我有錢了,我買個服務器,會添加郵件自動提醒的,第一時間會在烏云社區里發布。本來打算采用ED的on事件攔截代碼的,但是發現on事件在程序里也會大量使用,索性就沒有添加,如果你有思路
下載地址:http://pan.baidu.com/s/1jGVP7Ps
使用時記得在Application\Home\Conf\config.php
改下配置(我已經全部加了注釋,即使不會thinkphp的也可以搭建)
個人代碼寫的沒有多好,思路可能也比較爛。如果您有什么意見歡迎提出來,我會進一步修改的。