PHP与MYSQL动态网站开发笔记-3.网站攻击与防御演示及源码

一 基础功能

如图所示,为页面的根界面,未登录时自动进入此页面

图1.1.1

1.注册

点击注册,会进入注册页面,如下图所示. 注册成功之后会自动跳转到登录界面

  • 用户名不能少于3位
  • 密码不能少于5位
  • 密码用MD5哈希后存放到数据库中
  • 邮箱可以不填

图1.1.2

  • 注册的数据库中数据如图所示:

图1.1.4

2.登录和验证

注册成功之后,点击登录按钮,可以进入登录界面,如图所示

  • 每次登录都需要输入验证码
  • 验证码存在session中,每次刷新都会更换
  • 当密码或者验证码错误时都无法登录
  • 登录使用预编译防止SQL注入

图1.2.1

  • 登录功能中的验证码功能如下:
    • 验证码的内容是随机变化的,无论是字符的位置,还是内容
    • 验证码中添加了200个干扰点,防止机器识别
    • 验证码中添加了3个干扰线

登录成功之后,进入如图所示的主界面

右上角有文件上传,SQL注入,反射型XSS,存储型XSS,CSRF,LOGOUT选项,不同选项具有不同功能

图1.3.1

3.文件上传功能

点击Fileload选项,进入文件上传功能.如图所示

  • 文件上传只能上传图片类型,除此之外不允许上传
  • 文件大小不能超过2M
  • 文件上传成功后会使用md5(uniqid(microtime(true),true))重命名,来确保文件名不会重复
  • 文件上传到根目录uploads文件夹,若该文件夹不存在会先创建此文件夹,再移动到此文件夹内
  • 只允许通过HTTP POST类型上传过来的文件

图1.3.1.1

  • 上传文件的存储目录如图所示,由文件可看到文件均已重命名

图1.3.1.2

4.数据查询功能

点击SQL INJECTION选项,可以进入数据查询页面,如图所示

  • 输入id,查询用户名
  • 如果id不存在,则不输出值

图2.2.4

5.评论功能

点击XSS REFLECT或者XSS SRORE 选项可以进入评论页面,如图所示

  • 输入评论会输出在下方

图1.3.3.1

6.更改密码功能

点击CSRF选项,进入如图所示界面,该界面可以更改密码

图2.1.3

7.登出功能

当点击LOGOUT选项,会清空网站的所有session

  • 当用户成功登陆后,创建session用来存储登录成功的信息
  • 登出后再访问上述功能页面,本地的session自然与服务端不相等,会弹窗警告未登录,然后跳转登录界面,如下图所示

image-20210705194744700

  • 登出功能的代码如下所示:
    • 登出就删除所有session
//1.开启 Session
session_start();

//2.判断是否未登录就访问此页面
if($_SESSION['loginsucess']!='wzx'){
    echo"<script type='text/javascript'>alert('please log in ');location='login.html';</script>";}

//3.销毁所有 Session,并跳转会最初的根页面
session_unset();
echo"<script>location='index.html';</script>"

二 攻击与防御

在自己编写还未采用安全措施的网站上进行攻击

2.1 跨站点请求伪造攻击

CSRF攻击

  1. 假设被攻击者已经登录了系统,如图所示,该界面没有任何防护措施,功能是更改密码

图2.1.1

  1. 攻击者构造请求连接http://www.test.com/unsafeweb/csrf.php?newpass=qwert&confirmpass=qwert

    然后通过一些手段欺骗用户点击

  2. 假设用户在登录状态时点击了此连接,就会导致攻击成功,用户的密码已经被更改,攻击如图所示

图2.1.2

防御演示

  1. 如图所示为防御CSRF的网页,该网页具有如下特征
  • 输入验证码才能更改密码
  • 验证码是随机变化的,防止攻击者伪造
  • 表单提交的时候,会提交本表单的Token值,如果与服务器的不一致,则更改失败
  • 攻击者即使构造了伪造请求,但是由于token是基于微秒生成的,所以难以攻击成功

图2.1.3

  1. Token值使用burpsuite进行抓包可以得到,如下图所示
  • 即使抓到了Token值,但是它是使用$_SESSION['token'] = md5(microtime());来变化的
  • 每微秒Token都会变化,每次刷新验证码都会变化
  • 攻击者很难伪造出相同Token和验证码,确保不会发生CSRF攻击

图2.1.4

  1. 该页面的部分代码如下所示:
//1.将提交过来的token与服务端的比较
if(isset($_REQUEST['token'])){
    if($_REQUEST['token']!=$_SESSION['token']){;
        echo"<script type='text/javascript'>alert('Token error');location='csrf.php';</script>";}}

//2.判断验证码是否正确
if(isset($_REQUEST['authcode1'])){
    if(strtolower($_REQUEST['authcode1'])!=$_SESSION['authcode1']){
        echo"<script type='text/javascript'>
        	alert('verification code error');location='csrf.php';</script>";}}

//3.判断两次输入的密码是否相同
if($newpass==$confirmpass){
    if(($newpass!='')&&($confirmpass!='')){
        $md_pass=md5($newpass);//使用MD5
        
        //4.将md5后的新密码放入数据库
        $sql="update user set password='$md_pass' where id=5";
        $result=mysqli_query($link,$sql);
        if($result){
            echo "<script>alert('Alert sucess');window.location.href='csrf.php'</script>";}}}

防御策略

  1. 验证请求的Referer

    • 如果Referer是以自己的网站开头的域名,则说明该请求来自网站自己,是合法的
    • 如果Referer是其他网站域名或空白,就有可能是CSRF攻击,服务器应拒绝该请求
    • 但是此方法存在被绕过的可能
  2. 在请求中放入攻击者不能伪造的信息

    • 可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务器验证此token
    • 若请求中没有token或token的内容不正确,则认为请求可能是CSRF攻击从而拒绝该请求

2.2 SQL注入攻击

SQL注入(字符型)

  1. 如图所示页面,存在字符型注入漏洞

image-20210705215434763

2.在用户名输入框中构造1' or 1=1#,密码随便输入,验证码输入正确,如图所示

图2.2.2

  1. ,点击Login,会显示登录成功,即注入成功,如图所示

图2.2.3

SQL注入(数字型)

  1. 如下图所示页面,输入id,用来查询是否存在该用户,如果存在就返回用户名,不存在就返回空,该页面存在数字型注入漏洞

    图2.2.4

  2. 在页面中输入4 and 1=1#返回用户名

    图2.2.5

  3. 输入4 and 1=2#不返回结果.由此得出该页面存在数字型注入漏洞

    图2.2.6

  4. 输入4 order by 4#返回值,如下图所示;输入4 order by 5#不返回结果,由此得出字段名由4个

  5. 输入-1 union select 1,2,3,4#,只输出2,表明第二个字段明会输出结果

    image-20210705222752617

  6. 输入-1 union select 1,(select database()),3,4#,返回了数据库的名字

    image-20210705223216997

  7. 输入-1 union select 1,(select table_name from information_schema.tables where table_schema='member' limit 1,1),3,4#查询到表名一个表名为user

    image-20210705223545306

  8. 输入-1 union select 1,(select column_name from information_schema.columns where table_schema='member' and table_name='user' limit 2,1),3,4#得到一个字段名为password,同理可以获得字段名id,username,email

    image-20210705224012154

  9. 输入-1 union select 1,(select password from member.user where id=4),3,4#得到id为4的用户的密码为b0baee9d279d34fa1dfd71aadb908c3f,密码使用了MD5,可以使用彩虹表破解,破解后为11111

    image-20210705224314238

防御演示

  1. 在查询语句中使用预编译,可以有效防止SQL注入
  2. 如下图所示,使用同样的注入语句

image-20210705225030674

  1. 点击登录按钮后,后弹出错误,注入失败
  1. 在数字型注入页面同样使用预编译,输入注入相同的内容:-1 union select 1,(select password from member.user where id=4),3,4#,但是这次没有返回值

image-20210705225735564

5.使用id来查询的预编译代码如下所示:

//根据输入的id来查询用户名(使用预编译)
$id=$_POST['id'];
if($id!=''){
	$sql="select * from user where id =?";
	$stmt = $link->prepare($sql);
	$stmt->bind_param("i",$id);
	$stmt->execute();
	$result=$stmt->get_result();
		if($result){
			$rows = mysqli_fetch_array($result);
   			echo "<p  style='margin: auto;width:320px;color: #bce8f1'>'username:'$rows[1]<br/></p>";
         }
}

防御策略

  1. 过滤危险字符

    • 采用正则表达式匹配union,sleep,load_file等关键字,如果匹配到就退出程序
  2. 使用预编译语句,绑定变量

  3. 使用存储过程

    • 先将SQL语句定义在数据库中
    • 尽量避免在存储过程中使用动态SQL语句
    • 若无法避免,应使用严格的输入过滤或编码函数来处理用户输入数据
  4. 检查数据类型

    • 检查输入数据的数据类型,很大程度上可以对抗SQL注入

2.3 跨站脚本攻击

反射型XSS

1.在下面界面输入<script>alert("xss")</script>

image-20210705231903867

  1. 点击comment,会弹窗显示xss

image-20210705231950703

存储型XSS

1.在下面界面输入<script>alert("xss")</script>

image-20210705232037719

2.点击comment,会显示xss,当再刷新页面,还会显示该弹窗

image-20210705232133371

防御演示

  1. 以存储型XSS为例
    • 输入评论后,后端会进行检查,将<,> , ' , " , & ,#等进行Html编码,让它以Html编码的形式存入数据库中
    • 当输出到页面的时候,html会自动进行解释,然后还原成为原来的字符
    • 不过输出的内容为字符串,不会再触发XSS攻击

图1.3.3.1

  1. 特殊字符经过HTML编码后存到数据库,如下所示:

图1.3.3.2

  1. 特殊字符转换代码如下所示:
//1.对传入的字符串str进行Html encode转换
$str_com='';
if ($comment !=null && $comment.trim()!=""){
for ($i = 0, $len=strlen($comment); $i < $len; $i++) {
	$arr1 = str_split($comment);
    switch($arr1[$i]) {
    	case '&':
        	$str_com.="&amp;";
            break;
        case '<':
            $str_com.="&lt;";
            break;
        case '>':
            $str_com.="&gt;";
            break;
        case '"':
            $str_com.="&quot;";
            break;
        case ' ':
            $str_com.="&nbsp;";
            break;
        case '%':
            $str_com.="";
            break;
        default:
            $str_com.= $arr1[$i];
        }
   }
}

XSS的防御策略

  1. 输入检查
    • 在服务端检查用户输入的数据是否包含一些特殊的字符,如<,>,',",等
  2. 输出检查
    • 除了富文本的输出外,在变量输出到HTML页面时,可以使用编码或转义的方式来防御XSS
  3. 使用安全的编码函数
    • 使用HTMLEncode等编码方式

2.4 文件上传漏洞

文件上传绕过

  1. 如下图所示,为文件上传,该页面只允许上传图片类型

    image-20210706084110732

  2. 但是它的源码中只使用了$_FILE['myFile']['type']来判断类型

$type=$_FILES['myFile']['type'];
$allowExt=array('jpeg','jpg','png','gif');
 if(!in_array($type,$allowExt)){
       echo"<script>alert('非法文件类型');window.location.href='do_upload.html'</script>";
    }
  1. $_FILE['myFile']['type']是根据客户端发送过来的数据中Content-Type来判断类型,因此可以绕过

  2. 如下图上传图片类型的文件,而是扩展名为txt的文本文件

  3. image-20210706084836221

  4. 上传时使用burpsuite抓包,可以看到Content-Type的类型为text

    image-20210706085411950

  5. 将text类型改为image/png

    image-20210706085521423

  6. 然后将数据发送,显示上传成功

    image-20210706085635937

  7. 在上传目录uploads内可以发现此txt文件,文件经过了重命名

    image-20210706085838312

防御演示

  1. 如下图上传一个pptx类型的文件

    image-20210706090327060

  2. 使用burpsuite抓包,并修改Content-Type数据

    image-20210706090813822

  3. 但是上传失败

    image-20210706090934721

  4. 文件上传检测部分代码如下所示:

  • $_FILES['myFile']['type']判断文件类型,只允许图片类型
  • 设置文件最大不能超过2M
  • 移动文件到根目录下的uploads下,没有就先创建再移动
  • $ext变量用来判断文件扩展名是否为$allowExt白名单中的类型,防止$_FILES['myFile']['type']被绕过
  • is_uploaded_file($tmp_name)用来只允许post传过来的文件
  • getimagesize($tmp_name)用来防止其他类型的文件更改后缀名,伪造成图片类型来上传
  • $uniName=md5(uniqid(microtime(true),true)).'.'.$ext用来重命名,并确保文件名不会重复
  • 重命名同时可以防止.php.png等这种多重扩展名的文件
//1.将文件属性赋值给变量
$filename=$_FILES['myFile']['name'];
$type=$_FILES['myFile']['type'];
$tmp_name=$_FILES['myFile']['tmp_name'];
$size=$_FILES['myFile']['size'];
$error=$_FILES['myFile']['error'];
$allowExt=array('jpeg','jpg','png','gif');//允许上传类型白名单

//2.规定文件最大大小
$maxSize=2097152;//(2M)
if($error==0){
    if($size>$maxSize){
        echo"<script>alert('上传文件过大');window.location.href='do_upload.html'</script>";
    }
    
    //3.检查扩展名,如果扩展名不在规定的白名单内就禁止上传
    $ext=pathinfo($filename,PATHINFO_EXTENSION);
    if(!in_array($ext,$allowExt)){
       echo"<script>alert('非法文件类型');window.location.href='do_upload.html'</script>";
    }
    
    //4.检查是否通过http post过来
    if(!is_uploaded_file($tmp_name)){
        echo"<script>alert('文件不是通过HTTP POST方式上传过来的');
        	window.location.href='do_upload.html'</script>";
    }

    //5.检测是否是真的图片类型,不能是.txt改成.jpg等类似此种情况
    //getimagesize可以检测是否为图片,如果不是返回false
    $flag=true;
    if($flag){
        if(!getimagesize($tmp_name)){
            echo"<script>alert('不是真正的图片类型');window.location.href='do_upload.html'</script>";
        }
    }
    
    //6.检查是否存在uploads目录,没有文件就创建一个
    $path='uploads';
    if(!file_exists($path)){
        mkdir($path,0777,true);
        chmod($path,0777);
    }
    
    //7.重命名文件,并确保文件名唯一
    $uniName=md5(uniqid(microtime(true),true)).'.'.$ext;//
    $destination=$path.'/'.$uniName;
    //@ 错误抑制符
    
    //8.移动文件到指定的目录
    if(@move_uploaded_file($tmp_name,$destination)){
        echo"<script>alert('上传成功');window.location.href='do_upload.html'</script>";
    }else{
        echo"<script>alert('上传失败');window.location.href='do_upload.html'</script>";
    }

}

防御策略

  1. 文件上传目录设置为不可执行
    • 只要web容器无法解析该目录下的文件,即使上传了脚本文件,服务器也不会受到影响
  2. 判断文件类型
    • 判断类型时,结合使用MIME Type,后缀检查等方式
    • 文件检查时使用白名单的方式
    • 对于图片的处理可以使用压缩函数或者resize函数,用来破坏图片中可能存在的HTML代码
  3. 使用随机数改写文件名和文件路径
    • 文件上传如果要执行代码,需要用户能访问到这个文件
    • 使用随机数更改文件路径和文件名,极大增加了攻击成本
    • 此外文件重命名可以使.shell.php.rar,crossdomain这种文件所实施的攻击无法成功
  4. 单独设置文件服务器域名
    • 由于浏览器的同源策略,一系列客户端攻击将失效
    • 比如上传crossdomain.xml,上传包含JavaScriptXSS利用等问题将得到解决

三 网站源码

网站源码
解压密码为:Alucardlink

posted @ 2021-07-18 18:45  1ink  阅读(262)  评论(0编辑  收藏  举报