网站安全评估(云计算和云安全实战)
模块1 网站搭建
在docker环境下,使用现有php-apache和mysql的镜像构建网站环境
网站主界面如下:
点击留言可以查看详细的留言信息,界面如下:
模块2 查找网站漏洞
- 利用漏洞扫描工具(Bursuite和AWVS)对网站进行风险评估和分析漏洞
扫描结果
使用
Bursuite
工具分析发现 14 处安全问题,其中包括常见的跨站脚本攻击、数据库注入等
使用
AWVS
工具分析发现 13 处安全问题,与 Bursuite 扫描到的问题一致(因标准不同少1处错误为正常现象)
分析漏洞
- 跨站脚本攻击:没有对用户输入进行过滤,例如 HTML 标签、JavaScript 脚本,这样会导致网页中被插入恶意代码,从而使用用户的 Cookie、身份信息等敏感内容被盗取,或者在用户不知情的情况下,对用户的浏览器进行恶意操作。
- SQL 注入:没有对数据库查询进行过滤,这样会导致数据库被恶意修改,从而导致数据泄露、篡改等。
- 明文传输:在网站请求中所有内容均为明文传输,并未使用 HTTPS 协议进行基本的加密,这样有很大可能因中间人攻击导致用户的敏感信息被窃取。
- CORS 头未正确配置:CORS 头直接设置为 * 是非常不安全的,应该执行白名单机制,只允许指定的域名访问。
模块3 防范漏洞
防范跨站脚本攻击与 HTML 转义
- 触发过程
- 在留言板
index_low.php
中提交新留言,名字为<script>alert(1)</script>
,内容为<script>alert(1)</script>
,提交后,点击留言 - 在留言查看页面
show_low.php
中,可以看到弹出框,说明存在跨站脚本攻击。
- 在留言板
- 漏洞修复
在
low.php
的第 21 行与第 24 行中换行,使用htmlspecialchars
函数转义用户名与留言内容中包含的 HTML 标签与 JavaScript 脚本,防止新增留言出现跨站脚本攻击。在
show_log.php
的第 32 行被注解的正则替换虽然能防止 JavaScript 脚本攻击,但还是会正常渲染其他 HTML 标签(例如 img 标签,也能用于 xss 攻击,img 标签中的onerror
可以包含 JavaScript 脚本),因此还是需要使用htmlspecialchars
函数转义数据库中曾经包含的 HTML 标签的内容。
- 触发过程
防范 SQL 注入
触发过程
- 留言查看页面
show_low.php
的请求中包含?id=1
之类的请求,由于没对传入参数进行过滤,可尝试注入。- 尝试在
id
中加入1' AND (SELECT+IF(LENGTH(DATABASE())>2,1,0));--
来尝试判断数据库名字长度,在LENGTH(DATABASE()) > 3
时没再返回任何内容,则能得出数据库名字长度为 3。
- 尝试在
- 留言查看页面
漏洞修复
此处漏洞存在于
low.php
与show_low.php
中留言提交接口
low.php
中,向数据库插入信息的 SQL 语句INSERT INTO guestbooklow ( comment_id,comment, name ) VALUES ( '$comment_id','$message','$name' );
中的$comment_id
$message
$name
均用?
代替,然后使用prepare
函数预处理 SQL 语句,将 SQL 语句中的?
替换为实际的参数,详细请看下图与修改的主要代码- 代码部分
// 原本代码 // 在第 17 行至第 34 行 $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $message ); //stripslashes()删除反斜杠
$message = mysqli_real_escape_string($conn,$message);
// Sanitize name input
$name =mysqli_real_escape_string($conn,$name);
//get count
$get_count=“SELECT COUNT(*) FROM guestbooklow;“;
//$result = mysqli_query($conn, $query ) or die( '' . ((is_object($conn)) ? mysqli_error($conn) : (($mysqli_res = mysqli_connect_error()) ? $mysqli_res : false)) . '
' );
$r_count=mysqli_query($conn, $get_count );
$row = mysqli_fetch_row( $r_count );
$comment_id=$row[0]+1;
// Update database
$query = “INSERT INTO guestbooklow ( comment_id,comment, name ) VALUES ( '$comment_id','$message', '$name' );“;
$result = mysqli_query($conn, $query ) or die( '' . ((is_object($conn)) ? mysqli_error($conn) : (($mysqli_res = mysqli_connect_error()) ? $mysqli_res : false)) . '
' );// ====================修改后的代码====================
// 从 17 行开始替换
// Sanitize message input
$message = stripslashes( $message ); //stripslashes()删除反斜杠
// 转义 HTML 标签
$message = htmlspecialchars($message);
$message = mysqli_real_escape_string($conn,$message);// Sanitize name input
// 转义 HTML 标签
$name = htmlspecialchars($name);
$name =mysqli_real_escape_string($conn,$name);
//get count
$get_count=“SELECT COUNT(*) FROM guestbooklow;“;
//$result = mysqli_query($conn, $query ) or die( '' . ((is_object($conn)) ? mysqli_error($conn) : (($mysqli_res = mysqli_connect_error()) ? $mysqli_res : false)) . '
' );
$r_count=mysqli_query($conn, $get_count );
$row = mysqli_fetch_row( $r_count );
$comment_id=$row[0]+1;
// Update database
// 将 $comment_id $message $name 变量都替换为 ? 进行占位
$query = “INSERT INTO guestbooklow ( comment_id,comment, name ) VALUES ( ?,?,? );“;
// 对 SQL 语句进行预处理
if ($pre = $conn->prepare($query)) {// 占位符与变量绑定 // bind_param() 第一个传入参数为数据类型 // $comment_id(评论 ID)为数字则为 int,$message(留言内容)与 $name(评论者昵称)是字符串则为 string,所以传入的第一个参数为三个类型的首字母缩写 iss $pre->bind_param("iss", $comment_id, $message, $name); // 执行数据库查询 $pre->execute(); // 关闭数据库链接 $pre->close();
} else {
die( '<pre>' . ((is_object($conn)) ? mysqli_error($conn) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
}
- 代码部分
留言展示页面
show_low.php
中,使用prepare
函数预处理 SQL 语句,将 SQL 语句SELECT comment FROM guestbooklow WHERE comment_id='$id'
中的'$id'
替换为?
来进行占位,并使用bind_param
来绑定参数。详细请看下图- 代码部分
// 原本代码 // 在第 24 到 32 行 $query = "SELECT comment FROM guestbooklow WHERE comment_id='$id';"; $result = mysqli_query($conn, $query ); if(!$result) { echo "yuju fail connet"; } $row = mysqli_fetch_row( $result ); $result_id=$row[0]; //$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
// ====================修改后的代码====================
// 从 24 行开始替换
// 将 '$id' 替换为 ? 进行占位
$query = “SELECT comment FROM guestbooklow WHERE comment_id=?;“;// 使用 prepare 对 SQL 语句进行预处理,过滤不安全字符
if ($pre = $conn->prepare($query)) {// 使用 bind_param 与 SQL 语句中的占位符进行绑定 // bind_param 的第一个传入参数为占位符相绑定的数据类型 // 例如:$id 的类型为 int 则应该为 bind_param("i", $id) $pre->bind_param("i", $id); // 执行语句查询 $pre->execute(); // 将查询结果绑定到变量 $message 中 $pre->bind_result($message); // fetch() 将会返回数据总条数,在此需要使用 while 循环来为 $result_id 赋值 // 此处因为是查询评论 id 所以结果只会有 1 条,循环并不可能会出现变量被覆盖的现象 while($pre->fetch()) { $result_id = $message; } // 关闭数据库链接 $pre->close();
} else {
echo "yuju fail connet"; exit;
}
//$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t /i', '', $_GET[ 'name' ] );
// 转义 HTML 标签
$result_id = htmlspecialchars($result_id);- 代码部分