yedong0839 2014-09-16 14:05:36 7644次浏览 8条回复 10 0 0

前言 虽然本文是基于YII1.1,但其中提到的安全措施适用于多数web项目安全场景,所以翻译此文,跟大家交流。原文地址

目录

前言
正文
安全基本措施
验证与过滤用户的输入信息
原理
客户端验证
YII如何防范

跨站脚本攻击XSS
原理
YII如何防范

SQL注入
原理
YII如何防范

跨站请求伪造CSRF
配置WEB服务器
PHP项目一些有用的指令
授权
验证

常用工具

正文 提示:虽然本文内容丰富,但并未囊括所有的安全方面的知识;如果您程序对安全要求相当高,请多多参考相关技术。

安全基本措施

  • 对用户所有输入的信息都要验证与过滤,再进行处理。
  • 对所有输出到浏览器的信息都要过滤
  • 程序要经过debug模式的测试(开发环境下)
    如下操作:设置YII_DEBUG为true,并设置error_reporting(E_ALL);设置后,YII会打印出所有错误和警告的信息,出错的代码与原因; 注意,不要忽略任务一个提示,即使一个警告(E_NOTICE)都可能引发安全问题,比如未定义的数组的键。
  • 在生产环境 下一定要关闭debug 要保证产品中的提示信息不包含调试敏感信息。
  • 尽量对用户输入的信息都用白名单过滤,而不要用黑名单过滤;
  • 在产品里部署日志系统,定期检查警告与错误提示;
    一般两种日志:YII记录程序中运行的日志,web服务器和PHP记录服务端的日志。

验证与过滤用户的输入信息

原理 比如,当用户修改自己档案中的生日时,后台应该要确保他输入的是有效的日期;这不仅仅是为了防止用户的误操作,也是为了确保安全。如,要确保用户输入的是正确的时间格式“1951-01-25”,以防止sql注入与跨域攻击。验证用户输入虽然不是最有效的防范手段,但这是防范措施的第一步。

客户端验证 客户端验证并不能防范安全隐患,但能使程序与用户的交互更友好。为什么说客户端验证也不能保证安全呢?比如下面的这段HTML代码:

<input type="hidden" name="id" value="1" />    
<input type="text" name="date" size="10" />    
<select name="list"><option>1</option><option>2</option></select>    

虽然,网页前端输出的数据与各种html的input与select控件,但用户可以将这控件全部修改成textarea,然后再发送到后台。

YII如何防范 YII提供了下面两种方式处理这种情况。(在不用YII的情况下,使用PHP的类型转换与过滤扩展。)

基于model的验证 多数时候,用户输入的信息会由model来处理,而model是继承自CFormModel 或者 CActiveRecord 。这两个类的父类CMode的rules()方法用来定义字段验证规则。
验证用户输入的信息也可以写在CComponent 的行为和model 的beforeValidate()方法里。

<?php  
// controller里Action
$model = new Comment;
$model->attributes = $_POST['Comment'];
if ($model->save()) { // 验证通过后,才会被保存
    $this->redirect(array('view', 'id' => $model->id));
} else {
    // 未通过验证,或保存未成功
}  
<?php
// model
class Comment extends CActiveRecord
{
    public function rules()
    {
        return array(
        array('parent', 'numerical', 'integerOnly' => true),
        array('strangedata', 'customValidateForStrangedata'),//自定义的验证器
        array('description', 'length', 'max' => 255),
        );
    }
    // 继承父类的beforeValidate(),在验证之前执行
    protected function beforeValidate()
    if (!empty($this->description) && substr_count($this->description, '"') % 2 !== 0) {
        $this->addError("description", "引号没有配对");
        // return false; // stop validation
    }
    return parent::beforeValidate();
}
	 
/*自定义的验证方法
* @return boolean Continue the validation process? */
protected function customValidateForStrangedata($attribute, $params)
{
    $this->addError($attribute, "validation failed");
    return false;
}

在编写程序的时候,应该重视对用户输入信息的验证,这不仅仅是为了安全,也能保持后台收集到正确的数据。YII已经为我们定义多种的字段的验证方式,而且自己也还可以新增验证器。同时,在不同的场景下也可以使用不同的验证。比如,某个字段仅需要在修改的时候验证,而在新增的时候,则不需要验证。

基于controller验证 有的用户输入信息需要直接在controller里验证。当这种情况的时候,应该使用的PHP的类型转换。一般都这么处理数字型的ID。

<?php
// 不安全
$model = Post::model()->findByPk($_GET['id']);
// 安全
$model = Post::model()->findByPk((int) $_GET['id']);

如果传入字段的类型不是数字型,推荐使用model进行验证。

对上面示例的补充 当遇到上面例子中第一种写法的时候,YII中model的findByPk()方法会自动转换ID为数字型 (下面SQL注入章节会重点介绍)。然而多数情况下,依靠YII的这种自动处理是不保险的。比如,当恶意用户输入这样一个url:comment/delete?id[]=2\&id[]=1。后台$_GET[‘ID’]接收到的就是一个数组,如果此ID没有被验证就用于其它函数(不仅仅 是findByPk)处理,这就存在安全隐患。

您需要登录后才可以回复。登录 | 立即注册