ThinkPHP3.1 安全要点

在开发过程当中,除了确保业务逻辑没有安全隐患外,应该充分了解和利用框架内建的安全机制或者工具来确保应用以及服务器的安全性,下面咱们总结下ThinkPHP中涉及到的安全机制。

php

系统安全

系统安全指ThinkPHP能够配合的服务器的安所有署策略。html

应用部署建议

首先,咱们建议在条件允 许的状况下,把框架目录和项目目录都部署在非WEB访问目录下面,ThinkPHP的访问机制彻底支持框架和项目的非WEB目录访问,你只须要把入口文件 和资源(主要是指JS、样式和图片文件)目录放置于WEB目录下面便可。所以,建议的部署目录以下:thinkphp

  1. index.php 项目入口文件数组

  2. Public/ 项目资源文件目录缓存

  3.     Js/ JS目录安全

  4.     Css/ 样式文件目录服务器

  5.     Images/  图像文件目录框架

  6. protected/ (受保护的目录,能够部署到非WEB目录或者设置安全访问)xss

  7.     ThinkPHP/ 框架系统目录函数

  8.     App/ 项目目录

  9.     Uploads/ 项目上传目录

复制代码

系统安全设置

若是你已经经过部署策略设置了系统目录安全访问的话,则能够跳过这段。
若是你没法彻底作到上述的服务器目录安全保护,也无需担忧,ThinkPHP仍然能够经过设置确保你的目录安全。框架的核心文件自己已经作了访问判断,全部核心文件均不能直接在URL中被访问,所以也不用担忧直接访问某些文件致使的错误暴露你的服务器路径之类的信息。
对于应用目录,系统则提供了目录安全访问机制,你能够在第一次生成项目目录结构以前,在入口文件中添加:

  1. define('BUILD_DIR_SECURE', true);

复制代码

运行项目后会自动给项目的相关目录生成目录安全文件(在相关的目录下面生成空白的htm文件),而且能够自定义安全文件的文件名 DIR_SECURE_FILENAME ,默认是index.html,若是你想给大家的安全文件定义为default.html可使用

  1. define('DIR_SECURE_FILENAME', 'default.html');

复制代码

还能够支持多个安全文件写入,例如你想同时写入index.html和index.htm 两个文件,以知足不一样的服务器部署环境,能够这样定义:
  1. define('DIR_SECURE_FILENAME', 'index.html,index.htm');

复制代码

默认的安全文件只是写入一个空白字符串,若是须要写入其余内容,能够经过DIR_SECURE_CONTENT参数来指定,例如:
  1. define('DIR_SECURE_CONTENT', 'deney Access!');

复制代码

下面是一个完整的使用目录安全写入的例子
  1. define('BUILD_DIR_SECURE',true);

  2. define('DIR_SECURE_FILENAME', 'index.html');

  3. define('DIR_SECURE_CONTENT', 'deney Access!');

复制代码

除 了目录安全以外,还须要保护模板文件不被直接访问,由于有可能会在模板文件中暴露数据表的字段信息。解决办法是配置.htaccess文件(针对 Apache服务器,其余服务器参考修改),把如下代码保存在项目的模板目录目录(默认是Tpl)下保存存为.htaccess。
  1. <Files *.html>

  2.   Order Allow,Deny 

  3.   Deny from all

  4. </Files>

复制代码

若是你的模板文件后缀不是html能够将*.html改为你的模板文件的后缀。

表单安全

自动验证和自动完成

ThinkPHP内置的自动验证自动完成功能能够有效地对表单提交的数据安全加以控制。这两部分在快速入门系列中已经有过详细的介绍,就再也不描述了。

表单令牌

ThinkPHP内置了表单令牌验证功能,能够有效防止表单的重复提交等安全防御。
表单令牌验证相关的配置参数有:

  1. 'TOKEN_ON'=>true,  // 是否开启令牌验证 默认关闭

  2. 'TOKEN_NAME'=>'__hash__',    // 令牌验证的表单隐藏字段名称

  3. 'TOKEN_TYPE'=>'md5',  //令牌哈希验证规则 默认为MD5

  4. 'TOKEN_RESET'=>true,  //令牌验证出错后是否重置令牌 默认为true

复制代码

若是开启表单令牌验证功能,系统会自动在带有表单的模板文件里面自动生成以TOKEN_NAME为名称的隐藏域,其值则是TOKEN_TYPE方式生成的哈希字符串,用于实现表单的自动令牌验证。
自动生成的隐藏域位于表单Form结束标志以前,若是但愿本身控制隐藏域的位置,能够手动在表单页面添加{__TOKEN__} 标识,系统会在输出模板的时候自动替换。
若是页面中存在多个表单,建议添加{__TOKEN__}标识,并确保只有一个表单须要令牌验证。
若是个别页面输出不但愿进行表单令牌验证,能够在控制器中的输出方法以前动态关闭表单令牌验证,例如:

  1. C('TOKEN_ON',false);

  2. $this->display();

复制代码

模型类在建立数据对象的同时会自动进行表单令牌验证操做,若是你没有使用create方法建立数据对象的话,则须要手动调用模型的autoCheckToken方法进行表单令牌验证。若是返回false,则表示表单令牌验证错误。例如:
  1. $User = M("User"); // 实例化User对象

  2. // 手动进行令牌验证

  3. if (!$User->autoCheckToken($_POST)){

  4. // 令牌验证错误

  5. }

复制代码

表单合法性检测

表单合法性检测是3.1版本开始增长的表单提交字段检测机制,你再也不须要担忧用户在提交表单的时候注入非法字段数据了。表单字段合法性检测须要使用create方法建立数据对象的时候才能生效,有两种方式:

1、属性定义

能够给模型配置insertFields 和 updateFields属性用于新增和编辑表单设置,使用create方法建立数据对象的时候,不在定义范围内的属性将直接丢弃,避免表单提交非法数据。
insertFields 和 updateFields属性的设置采用字符串(逗号分割多个字段)或者数组的方式,例如:

  1. class UserModel extends Model{

  2.     protected $insertFields = array('account','password','nickname','email');

  3.     protected $updateFields = array('nickname','email');

  4. }

复制代码

设置的字段应该是实际的数据表字段,而不受字段映射的影响。
在使用的时候,咱们调用create方法的时候,会根据提交类型自动识别insertFields和updateFields属性:

  1. D('User')->create();

复制代码

使用create方法建立数据对象的时候,新增用户数据的时候,就会屏蔽'account','password','nickname','email' 以外的字段,编辑的时候就会屏蔽'nickname','email'以外的字段。
下面是采用字符串定义的方式,一样有效:
  1. class UserModel extends Model{

  2.     protected $insertFields = 'account,password,nickname,email';

  3.     protected $updateFields = 'nickname,email';

  4. }

复制代码

2、方法调用

若是不想定义insertFields和updateFields属性,或者但愿能够动态调用,能够在调用create方法以前直接调用field方法,例如,实现和上面的例子一样的做用:
在新增用户数据的时候,使用:

  1. $User = M('User');

  2. $User->field('account,password,nickname,email')->create();

  3. $User->add();

复制代码

而在更新用户数据的时候,使用:

  1. $User = M('User');

  2. $User->field('nickname,email')->create();

  3. $User->where($map)->save();

复制代码

这里的字段也是实际的数据表字段。field方法也可使用数组方式。
若是你的字段比较多,还可使用field方法的排除功能,例如:
  1. $User->field('status,create_time',true)->create();

复制代码


变量安全

变量安全获取

对全局系统变量的获取建议采用系统提供的变量获取方法获取,具体能够参考快速入门系列的变量

全局变量过滤

若是你习惯于直接调用$_GET $_POST变量的话,那么或者能够采用ThinkPHP系统内置的对全局变量的安全过滤方式,只须要设置VAR_FILTERS参数。
例如:

  1. 'VAR_FILTERS'=>'htmlspecialchars'

复制代码

在3.1.2版本开始,安全过滤方法的定义函数须要调整为引用返回的方式,并且可以实现多维数组的递归过滤,例如:

  1. 'VAR_FILTERS'=>'filter_vars'

复制代码

对应的过滤方法定义以下:
  1. function filter_vars(&$value){

  2.     $value = htmlspecialchars($value);

  3. }

复制代码


数据安全

防SQL注入

对于WEB应用来讲,SQL注入攻击无疑是首要防范的安全问题,系统底层对于数据安全方面自己进行了不少的处理和相应的防范机制,例如:

  1. $User = M("User"); // 实例化User对象

  2. $User->find($_GET["id"]);

复制代码


即使用户输入了一些恶意的id参数,系统也会强制转换成整型,避免恶意注入。这是由于,系统会对数据进行强制的数据类型检测,而且对数据来源进行数据格式转换。并且,对于字符串类型的数据,ThinkPHP都会进行escape_string处理。
一般的安全隐患在于你的查询条件使用了字符串参数,而后其中一些变量又依赖由客户端的用户输入,要有效的防止SQL注入问题,咱们建议:
查询条件尽可能使用数组方式,这是更为安全的方式;
若是不得已必须使用字符串查询条件,使用预处理机制;

查询条件预处理

where方法使用字符串条件的时候,支持预处理(安全过滤),并支持两种方式传入预处理参数,例如:

  1. $Model->where("id=%d and username='%s' and xx='%f'",array($id,$username,$xx))->select();

复制代码

或者

  1. $Model->where("id=%d and username='%s' and xx='%f'",$id,$username,$xx)->select();

复制代码

模型的query和execute方法 一样支持预处理机制,例如:
  1. $model->query('select * from user where id=%d and status=%d',$id,$status);

复制代码

或者
  1. $model->query('select * from user where id=%d and status=%d',array($id,$status));

复制代码

execute方法用法同query方法。

防XSS攻击

XSS(跨站脚本攻击)能够用于窃取其余用户的Cookie信息,要避免此类问题,能够采用以下解决方案:
   直接过滤全部的JavaScript脚本;
   转义Html元字符,使用htmlentities、htmlspecialchars等函数;
   系统的扩展函数库提供了XSS安全过滤的remove_xss方法;
   新版对URL访问的一些系统变量已经作了XSS处理。

上传安全

网站的上传功能也是一个很是容易被攻击的入口,因此对上传功能的安全检查是尤为必要的。
系统提供的上传扩展类库(ORG.Net.UploadFile)提供了安全方面的支持,包括对文件后缀、文件类型、文件大小以及上传图片文件的合法性检查,确保你已经在上传操做中启用了这些合法性检查。

其余安全建议

下面的一些安全建议也是很是重要的:对全部公共的操做方法作必要的安全检查,防止用户经过URL直接调用;不要缓存须要用户认证的页面;对于关键操做须要检查用户权限;对用户的上传文件,作必要的安全检查,例如上传路径和非法格式。如非必要,不要开启服务器的目录浏览权限;对于项目进行充分的测试,不要生成业务逻辑的安全隐患(这多是最大的安全问题);

相关文章
相关标签/搜索