[Yii2笔记]048系统安全的最佳实践(Security best practices) [ 技术分享 ]
说明
学习Yii Framework 2(易2框架)的过程是漫长的,也是充满乐趣的,以下是我学习Yii2框架时对官网英文资料(请参见原文网址)的翻译和代码实现,提供了较完整的代码,供你参考。不妥之处,请多多指正!
原文网址:
http://www.yiiframework.com/doc-2.0/guide-security-best-practices.html
本文主题:系统安全的最佳实践(Security best practices)
1、系统安全的基本原则(Basic principles)
要保障开发的应用系统没有大的安全问题要遵循两个主要原则: 1)输入过滤(Filter input) 2)规避输出(Escape ouptput)
1)输入过滤(Filter input) 输入过滤的意思是输入永远是不安全的,你需要一直检测它直到这个值是允许的值时才通过。例如,如果我们知道排序参数有三个字段:title、create_at、status,且此字段将由用户来输入,当我们接收到用户输入时,最好的办法就是去检测这些输入值,PHP中的代码是:
$sortBy=$_GET['sort'];
if(!in_array($sortBy,['title','create_at','status'])){
throw new Exception('Invalid sort value.');
}
2)规避输出(Escape ouptput) 规避输出的意思是依赖于不同的上下文,我们的数据将被规避(escape),例如:在HTML中,你需要规避<,>和一些特殊字符。在JavaScript或SQL中则有另外一些字符需要规避。手动规避每一个输出是极易出错的,所以Yii为不同的环境提供了大量的工具去执行规避。
2、避免SQL注入(Avoiding SQL injections)
当查询语句的文本中包含一些未规避的字符串时,就会发生SQL注入,例如:
$username=$_GET['username'];
$sql="SELECT * FROM ueser WHERE username='$username'";
攻击者会使用一些额外字符来替代常规的username输入,如:';DROP TABLE user; --',SQL语句就变成了:
SELECT * FROM user WHERE username='';DROP TABLE user; --' '
这是一个有效的SQL查询,将会使用空的username查询,然后删除user表,这将破坏掉我们的网站并导致数据丢失。(你已做好备份了,确定?) 在Yii中绝大多数数据库操作是通过AR(Active Record)来执行的,AR则是使用内部的PDO预处理机制完成。预处理语句则不会存在上述恶意语句被执行的情况。 有时你可能需要使用原生的查询语句或查询构造器,此时你应对传来的数据使用安全的方法,如果数据是针对列值,最好使用预处理语句:
//查询构造器
$userIDs=(new Query())
->select('id')
->form('user')
->where('status=:status',[':status'=>$status])
->all();
//DAO
$userIDs=$connection
->createCommand('SELECT id FROM user where status=:status')
->bindValues([':status'=>$status])
->queryColumn();
如果数据用于特定列名或表名,最好设置预定的值集合,如:
function actionList($orferBy=null){
if(!in_array($orderBy,['name','status'])){
throw new BadRequestException('Only name and status are allowed to order by.');
}
//...
}
这样,表名和列名就不可能被忽略了,Yii针对所支持的所有数据库都有特定的格式来避免SQL注入。
$sql="SELECT COUNT([[$column]]) FROM {{table}}";
$rowCount=$connection->createCommand($sql)->queryScalar();
你可以从"Quoting Table and Column Names"章节获取更多的格式信息: http://www.yiiframework.com/doc-2.0/guide-db-dao.html#quoting-table-and-column-names
3、Avoiding XSS
XSS或跨站脚本是在输出HTML到浏览器上是没有对输出进行正确规避时发生的,例如:用户输入他的名称时,不是输入的Alexander,而是,每一个输出用户名字的页面都会执行JavaScript"alert('Hello!');",也就是在浏览器中弹出一个提示框。依托网站不只是简单的弹出提示,这样的脚本可以使用您的名字发送消息,甚至执行银行交易。 在Yii中避免XSS非常简单,通常有两种情况: 1、将数据输出为纯文本 2、将数据输出为HTML
如果要输出纯文本:
<?=\yii\helpers\Html::encode($username)?>
如果要输出HTML
<?=\yii\helpers\HtmlPurifier::process($description)?>
注意:HtmlPurifier处理过程繁重,建议添加缓存。
4、避免CSRF(Avoiding CSRF)
CSRF是跨站请求伪造(cross-site request forgery)的缩略语(abbreviation),这个创意来自于应用假设来自于一个用户浏览器的请求是由用户自己创建的,但实际不是这样。 例如,an.example.com网站有一个/logout网址,当用户使用GET方法访问时,用户登出系统。当此网址被用户自己执行时,一切OK,但当某一天,来个坏小子在一个热门论坛中发了个图片连接,浏览器分不清一个请求是来自于图片还是来自于网页,当用户打开了上述图片标签的网页时,浏览器就会发送GET请求到该URL,用户将从an.example.com登出。 这是最基本的创意,登出一个用户或许并不会造成太严重的后果,但是坏小子利用这个创意可以做的事情就多了去了。假如一个网站有一个URL http://an.example.com/purse/transfer?to=anotherUser&amount=2000 使用GET请求方式访问它,将从授权用户账户上转$2000 给用户anotherUser。我们知道,浏览器会发送GET请求去加载一个图片,所以我们可以修改代码仅接收POST请求。不幸的是,这种做法也不能救得了我们,攻击者会放置JavaScript代码,而不是一个简单的标签,这样就会发送一个POST请求到该连接了。
要避免CSRF,你可以: 1、遵循HTTP定义,例如GET,无需修改应用状态。 2、使Yii的CSRF保护生效。
有时你需要针对某个控制器或动作禁用CSRF检测,可以通过设置完成:
namespace app\controller;
use yii\web\Controller;
class SiteController extends Controller{
//禁用此控制器的CSRF检测功能
public $enableCsrfValidation=false;
public function actionIndex(){
}
}
beforeAction
在动作post中禁用CSRF检测:
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller{
public function beforeAction($action){
if($action=="post"){
$this->enableCsrfValidation=false;
}
return parent::beforeAction($action);
}
}
5、避免文件曝露(Avoiding file exposure)
服务器的默认网站根目录是指向web目录的,此目录与index.php在同一个目录下,这样,在共享主机环境下,要获取我们的代码也是不可能的,我们已将代码、配置和日志与网站根目录分离开。 不要忘记将web之外的目录设置为禁止存取,如果无法做到请考虑在其他地方托管您的应用程序。
6、避免在生产环境中使用调试信息和工具(Avoiding debug info and tools at production)
在调试模式Yii将显示非常多的错误信息,这对于开发工作是很有帮助的。问题是,这些调试信息对于攻击者来说也是非常有用的,会泄露数据库结构,配置信息和部分代码,永远不要在生产环境下设置index.php中的YII_DEBUG为true。 永远不要在生产环境下使用Gii,它将被用于获取数据结构信息、代码和重写使用Gii生成的代码。 除非真的需要,否则一定避免在生产环境中使用调试工具,它将泄露应用和配置的细节信息。如果确实需要,请再次检查这样的存取操作严格限定到你的IP地址。
7、使用TLS安全链接(Using secure connection over TLS)
依托于Cookie和PHP的session,Yii提供了一些特性,如果连接是脆弱的,那这些特性可能也是脆弱的。使用TLS安全链接则可以降低这些风险,如何去配置请参考服务器文档,你也可以在H5BP项目中检测例程配置: Ngix Apache IIS Lighttpd
8、安全服务器配置(Secure Server configuration)
本节的目的是突出在创建服务器配置以服务基于Yii的网站时需要考虑的风险。除了此处所提的点外,还有一些其他相关的安全配置需要考虑,所以本节描述是不完备的。
避免主机头攻击(Avoiding Host-header attacks)
象yii\web\UrlManager和yii\helpers\Url类在创建链接时可能会使用到当前请求的主机名,如果Web服务器配置为通过独立主机头服务于相同的站点,这些信息可能是并不可靠的,也可以被用户使用发送HTTP请求的方式进行伪造。这时,你需要修复你的Web服务器配置,使站点仅服务于特定的主机名,或者明确设置,或使用请求应用组件的主机信息(hostinfo)属性过滤。 关于服务器配置的更多信息,请参考服务器文档: Apache2:http://httpd.apache.org/docs/trunk/vhosts/examples.html#defaultallports Nginx:https://www.nginx.com/resources/wiki/start/topics/examples/server_blocks/
如果你没有服务器配置的权限,你可以创建应用级别的yii\filters\HostControl过滤器,以抵御此类攻击: // Web Application configuration file return [
'as hostControl' => [
'class' => 'yii\filters\HostControl',
'allowedHosts' => [
'example.com',
'*.example.com',
],
'fallbackHostInfo' => 'https://example.com',
],
// ...
];
注意:抵御"主机头攻击",你应该尽量使用Web服务器配置,而不是过滤器,yii\filters\HostControl只有当服务器配置不可用时才使用。
(全文完)
共 0 条回复
阿江
最后登录:2024-03-03
在线时长:186小时21分
- 粉丝94
- 金钱16816
- 威望160
- 积分20276