Yii2 的安全最佳实践(Best Practices Of Security)

下面,咱们将会回顾常见的安全原则,并介绍在使用 Yii 开发应用程序时,如何避免潜在安全威胁。 大多数这些原则并不是您独有,而是适用于网站或软件开发, 所以,您还能够找到有关这些背后的通常概念的进一步阅读的连接。php

基本准则

不管是开发何种应用程序,咱们都有两条基本的安全准则:html

  • 过滤输入
  • 转义输出

过滤输入

过滤输入的意思是,用户输入不该该认为是安全的,你须要老是验证你得到的输入值是在容许范围内。 好比,咱们假设能够经过三个字段完成排序 title,created_at 和 status,而后,这个值是由用户输入提供的, 那么,最好在咱们接收参数的时候,检查一下这个值是不是指定的范围。 对于基本的 PHP 而言,上述作法相似以下:nginx

$sortBy = $_GET['sort'];
if (!in_array($sortBy, ['title', 'created_at', 'status'])) {
    throw new Exception('Invalid sort value.');
}

在 Yii 中,很大可能性,你会使用 表单校验器 来执行相似的检查。web

进一步阅读该主题:sql

https://www.owasp.org/index.p...
https://www.owasp.org/index.p...数据库

转义输出

转义输出的意思是,根据咱们使用数据的上下文环境,数据须要被转义。好比:在 HTML 上下文, 你须要转义 <,> 之类的特殊字符。在 JavaScript 或者 SQL 中,也有其余的特殊含义的字符串须要被转义。 因为手动的给所用的输出转义容易出错, Yii 提供了大量的工具来在不一样的上下文执行转义。apache

进一步阅读该话题:浏览器

https://www.owasp.org/index.p...
https://www.owasp.org/index.p...
https://www.owasp.org/index.p...缓存

避免 SQL 注入

SQL 注入发生在查询语句是由链接未转义的字符串生成的场景,好比:安全

$username = $_GET['username'];
$sql = "SELECT * FROM user WHERE username = '$username'";

除了提供正确的用户名外,攻击者能够给你的应用程序输入相似 '; DROP TABLE user; -- 的语句。 这将会致使生成以下的 SQL:

SELECT * FROM user WHERE username = ''; DROP TABLE user; --'

这是一个合法的查询语句,并将会执行以空的用户名搜索用户操做,而后,删除 user 表。 这极有可能致使网站出错,数据丢失。(你是否进行了规律的数据备份?)

在 Yii 中,大部分的数据查询是经过 Active Record 进行的, 而其是彻底使用 PDO 预处理语句执行 SQL 查询的。在预处理语句中,上述示例中,构造 SQL 查询的场景是不可能发生的。

有时,你仍须要使用 raw queries 或者 query builder。 在这种状况下,你应该使用安全的方式传递参数。若是数据是提供给表列的值,最好使用预处理语句:

// query builder
$userIDs = (new Query())
    ->select('id')
    ->from('user')
    ->where('status=:status', [':status' => $status])
    ->all();

// DAO
$userIDs = $connection
    ->createCommand('SELECT id FROM user where status=:status')
    ->bindValues([':status' => $status])
    ->queryColumn();

若是数据是用于指定列的名字,或者表的名字,最好的方式是只容许预约义的枚举值。

function actionList($orderBy = null)
{
    if (!in_array($orderBy, ['name', 'status'])) {
        throw new BadRequestHttpException('Only name and status are allowed to order by.')
    }

    // ...
}

若是上述方法不行,表名或者列名应该被转义。Yii 针对这种转义提供了一个特殊的语法, 这样能够在全部支持的数据库都使用一套方案。

$sql = "SELECT COUNT([[$column]]) FROM {{table}}";
$rowCount = $connection->createCommand($sql)->queryScalar();

你能够在 Quoting Table and Column Names 中获取更多的语法细节。

进一步阅读该话题:

https://www.owasp.org/index.p...

防止 XSS 攻击

XSS 或者跨站脚本发生在输出 HTML 到浏览器时,输出内容没有正确的转义。 例如,若是用户能够输入其名称,那么他输入 <script>alert('Hello!');</script> 而非其名字 Alexander, 全部输出没有转义直接输出用户名的页面都会执行 JavaScript 代码 alert('Hello!');, 这会致使浏览器页面上出现一个警告弹出框。就具体的站点而言,除了这种无心义的警告输出外, 这样的脚本能够以你的名义发送一些消息到后台,甚至执行一些银行交易行为。

避免 XSS 攻击在 Yii 中很是简单,有以下两种通常状况:

  • 你但愿数据以纯文本输出。
  • 你但愿数据以 HTML 形式输出。

若是你须要的是纯文本,你能够以下简单的转义:

<?= \yii\helpers\Html::encode($username) ?>

若是是 HTML,咱们能够用 HtmlPurifier 帮助类来执行:

<?= \yii\helpers\HtmlPurifier::process($description) ?>

注意 HtmlPurifier 帮助类的处理过程较为费时,建议增长缓存。

进一步阅读该话题:

https://www.owasp.org/index.p...

防止 CSRF 攻击

CSRF 是跨站请求伪造的缩写。这个攻击思想源自许多应用程序假设来自用户的浏览器请求是由用户本身产生的, 而事实并不是如此。

例如,网站 an.example.com 有一个 /logout 网址,当使用简单的 GET 请求访问时, 记录用户退出。 只要用户的请求一切正常,可是有一天坏人们故意在用户常常访问的论坛上放上 <img src="http://an.example.com/logout">。 浏览器在请求图像或请求页面之间没有任何区别, 因此当用户打开一个带有这样一个被操做过的 <img> 标签的页面时, 浏览器将 GET 请求发送到该 URL,用户将从 an.example.com 注销。

这是 CSRF 攻击如何运做的基本思路。能够说用户退出并非一件严重的事情, 然而这仅仅是一个例子,使用这种方法能够作更多的事情,例如触发付款或者是改变数据。 想象一下若是某个网站有一个这样的 http://an.example.com/purse/transfer?to=anotherUser&amount=2000 网址。 使用 GET 请求访问它会致使从受权用户帐户转帐 $2000 给 anotherUser。 咱们知道,浏览器将始终发送 GET 请求来加载图像, 因此咱们能够修改代码以仅接受该 URL 上的 POST 请求。 不幸的是,这并不会拯救咱们,由于攻击者能够放置一些 JavaScript 代码而不是 <img> 标签,这样就能够向该 URL 发送 POST 请求。

出于这个缘由,Yii 应用其余机制来防止 CSRF 攻击。

为了不 CSRF 攻击,你老是须要:

  1. 遵循 HTTP 准则,好比 GET 不该该改变应用的状态。 有关详细信息,请参阅 RFC2616
  2. 保证 Yii CSRF 保护开启。

有的时候你须要对每一个控制器和/或方法使用禁用 CSRF。能够经过设置其属性来实现:

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
    public $enableCsrfValidation = false;

    public function actionIndex()
    {
        // CSRF validation will not be applied to this and other actions
    }

}

要对每一个自定义方法禁用 CSRF 验证,您可使用:

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
    public function beforeAction($action)
    {
        // ...set `$this->enableCsrfValidation` here based on some conditions...
        // call parent method that will check CSRF if such property is true.
        return parent::beforeAction($action);
    }
}

在 standalone actions 禁用 CSRF 必须在 init() 方法中设置。 不要把这段代码放在 beforeRun() 方法中,由于它不会起任何做用。

<?php

namespace app\components;

use yii\base\Action;

class ContactAction extends Action
{
    public function init()
    {
        parent::init();
        $this->controller->enableCsrfValidation = false;
    }

    public function run()
    {
          $model = new ContactForm();
          $request = Yii::$app->request;
          if ($request->referrer === 'yiipowered.com'
              && $model->load($request->post())
              && $model->validate()
          ) {
              $model->sendEmail();
          }
    }
}
警告: 禁用 CSRF 将容许任何站点向您的站点发送 POST 请求。在这种状况下,实施额外验证很是重要,例如检查 IP 地址或秘密令牌。

进一步阅读该话题:

https://www.owasp.org/index.p...

防止文件暴露

默认的服务器 webroot 目录指向包含有 index.phpweb 目录。在共享托管环境下,这样是不可能的, 这样致使了全部的代码,配置,日志都在webroot目录。

若是是这样,别忘了拒绝除了 web 目录之外的目录的访问权限。 若是无法这样作,考虑将你的应用程序托管在其余地方。

在生产环境关闭调试信息和工具
在调试模式下, Yii 展现了大量的错误信息,这样是对开发有用的。 一样,这些调试信息对于攻击者而言也是方便其用于破解数据结构,配置值,以及你的部分代码。 永远不要在生产模式下将你的 index.php 中的 YII_DEBUG 设置为 true

你一样也不该该在生产模式下开启 Gii。它能够被用于获取数据结构信息, 代码,以及简单的用 Gii 生成的代码覆盖你的代码。

调试工具栏一样也应该避免在生产环境出现,除非很是有必要。它将会暴露全部的应用和配置的详情信息。 若是你肯定须要,反复确认其访问权限限定在你本身的 IP。

进一步阅读该话题:

https://www.owasp.org/index.p...
https://www.owasp.org/index.p...

使用 TLS 上的安全链接(HTTPS的启用)

Yii 提供依赖 cookie 和/或 PHP 会话的功能。若是您的链接受到威胁,这些可能会很容易受到攻击。 若是应用程序经过 TLS 使用安全链接,则风险会下降。
有关如何配置它的说明,请参阅您的 Web 服务器文档。

如今各大云平台都提供免费的单域名SSL证书,如阿里云,腾讯云

以阿里云部署Nginx下的SSL证书为例:
修改nginx.conf文件以下:

# 如下属性中以ssl开头的属性表明与证书配置有关,其余属性请根据本身的须要进行配置。
server {
    listen 443 ssl;
    server_name localhost;  # localhost修改成您证书绑定的域名。
    
    root html;
    index index.html index.htm;
    ssl_certificate cert/domain name.pem;   #将domain name.pem替换成您证书的文件名。
    ssl_certificate_key cert/domain name.key;   #将domain name.key替换成您证书的密钥文件名。
    ssl_session_timeout 5m;
    #使用此加密套件。
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; 
    #使用该协议进行配置。 
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;   
    ssl_prefer_server_ciphers on;   
    location / {
        root html;   #站点目录。
        index index.html index.htm;   
    }
}

设置http请求自动跳转https。(通常都这么作)

server {
  listen 80;
  server_name www.example.com;
  return 301 https://$server_name$request_uri;
}
参考官方说明,写的能不能配置成功本身脑补
腾讯云 Nginx 服务器证书安装
阿里云 在Nginx/Tengine服务器上安装证书

安全服务器配置

本节的目的是强调在为基于 Yii 的网站提供服务配置时须要考虑的风险。 除了这里涉及的要点以外, 可能还有其余与安全相关的配置选项, 因此不要认为这部分是完整的。

避免 Host-header 攻击

像 yiiwebUrlManager 和 yiihelpersUrl 这样的类会使用 currently requested host name 来生成连接。 若是 Web 服务器配置为独立于 Host 标头的值提供相同的站点,这个信息并不可靠, 而且 可能由发送HTTP请求的用户伪造。 在这种状况下,您应该修复您的 Web 服务器配置以便仅为指定的主机名提供站点服务 或者经过设置 request 应用程序组件的 hostInfo 属性来显式设置或过滤该值。

注意: 您应该始更倾向于使用 web 服务器配置 'host header attack' 保护而不是使用过滤器。 仅当服务器配置设置不可用时 yiifiltersHostControl 才应该被使用。

以Nginx为例:

方法一:

修改nginx.conf

添加一个默认server,当host头被修改匹配不到server时会跳到该默认server,该默认server直接返回403错误。

例子以下:

server {

       listen 80 default;

       server_name _;
       server_name_in_redirect off;
       location / {

            return 403;

       }

       }

重启nginx便可。

方法二:

修改nginx.conf

在目标server添加检测规则,参考如下标红配置:

server {

       server_name  abc.com;

       listen       80;

        if ($http_Host !~*^abc.com:80$)

        {
            return 403;
        }

       ...

       }

重启nginx便可。

有关于服务器配置的更多信息,请参阅您的 web 服务器的文档:

若是您无权访问服务器配置,您能够在应用程序级别设置 yii\filters\HostControl 过滤器, 以防此类的攻击。

// Web Application configuration file
return [
    'as hostControl' => [
        'class' => 'yii\filters\HostControl',
        'allowedHosts' => [
            'example.com',
            '*.example.com',
        ],
        'fallbackHostInfo' => 'https://example.com',
    ],
    // ...
];

补充阅读

web安全攻防思惟导图

  • web攻击及防护技术 (来源网络)

web攻击及防护技术

相关文章
相关标签/搜索