写出安全的YII WEB应用(一) [ 技术分享 ]
前言 虽然本文是基于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)处理,这就存在安全隐患。
共 8 条回复
-
RichardTian 回复于 2014-09-16 16:22 举报
学习了,的确是有很多方面需要去考虑的
-
yedong0839 回复于 2014-09-17 10:29 举报
谢谢老大指点,已经调整了
yedong0839 成都
最后登录:2019-07-16
在线时长:27小时57分
- 粉丝14
- 金钱1980
- 威望0
- 积分2250