安全

安全在程序开发和产品运营过程中一直都是非常重要不可忽略的,FastAdmin基于ThinkPHP5进行开发,其中还引用了许多开源库,因为我们建议你在开发和运营过程中时刻保持关注FastAdmin官网动态。

开发安全

开发安全是我们在开发过程中很容易的一个环节,由于程序的不严谨很容易数据泄漏、数据丢失、服务器被提权,因此我们在开发过程中就要尽量做到开发规范严谨,接下来主要讲解下在开发过程中要注意避免产生BUG的几种情形

SQL注入

这种问题常常出现在读写操作数据库时产生,很多时候我们需要查询数据库的数据做逻辑操作,由于未对请求的参数做过滤或处理,很容易产生数据丢失和数据泄漏的问题。

例如,我们需要根据用户名查询指定用户的信息,我们常用的SQL语句如下:

$username = $this->request->request("username");
\think\Db::query("SELECT * FROM fa_user WHERE username='{$username}'");

一般情况下我们username参数传普通参数都不会有问题,但是如果遇到别有用心之人传一些特殊参数,例如传递username的参数为

http://www.yoursite.com/test/index?username=' OR '1'='1

此时我们接收到的username' OR '1'='1,最终生成的SQL语句为

SELECT * FROM fa_user WHERE username='' OR '1'='1'

此时将导致筛选所有数据或筛选我们任意想要的数据。

那如果避免这种情况发生呢?我们有以下几种解决方法

1、使用参数预处理

$username = $this->request->request("username", "");
$username = htmlspecialchars($username, ENT_QUOTES);
\think\Db::query("SELECT * FROM fa_user WHERE username=:username", ['username'=>$username]);

2、使用模型的自带的查询方法进行查询

$username = $this->request->request("username", "");
$username = htmlspecialchars($username, ENT_QUOTES);
//方法1
\app\common\model\User::getByUsername($username);
//方法2
\app\common\model\User::where('username', $username)->find();

在ThinkPHP5中提供了许多数据输入过滤方法,例如

//强制转换为Email格式
$this->request->post('email','',FILTER_VALIDATE_EMAIL);
$this->request->post('email','','email');
//强制转换为数字
$this->request->post('id/d','0');

常用的修饰符如下

修饰符作用
s强制转换为字符串类型
d强制转换为整型类型
b强制转换为布尔类型
a强制转换为数组类型
f强制转换为浮点类型

XSS跨站注入

XSS跨站点脚本注入常常出现在浏览器客户端输出时由于未对输出的数据进行过滤和转换,导致浏览器响应执行了Javascript代码,这个代码代具有管理员权限的用户做一些隐藏的操作,比如提权、恶意修改删除数据,甚至将用户当前浏览器端的Cookie等数据传到攻击者的服务器,攻击者通过伪造Cookie请求,很容易造成用户信息漏洞和数据丢失的情况。这种问题常出现在会员昵称、会员头像输出、会员评论数据等情况下。

解决防范XSS跨站注入的首先是按照上方的SQL注入做好请求数据过滤,其次是做好数据输出时的编码,我们在视图模板中编写代码时,可以通过添加htmlentities对HTML代码做实体编码,例如

<input type="text" name="username" value="{$username|htmlentities}" />

千万不要直接使用以下的代码,以下代码都是非常不安全的

<input type="text" name="username" value="{$Think.get.username}" />
或
<input type="text" name="username" value="{$_GET['username']}" />

CSRF跨站请求

CSRF全称为cross site request forgery,中文意思为:跨站点伪装请求

跨站点请求的原理就是用户A站点1发布上传粘贴了一个站点2的URL,用户B不明就里的点击了站点2的URL,而这个URL因为是伪装请求站点1修改密码(其它危险请求)的操作。此时用户A就已经获取了用户B的账户信息。

例如我们常常在编写表单提交的时候都是如下的写法

<form action="">
<input type="password" name="newpassword" />
<input type="submit" />
</form>

以上的写法很容易产生CSRF跨站点伪装请求。

在ThinkPHP5可以很方便的使用token的方式来防范这种威胁,比如我们将上面的代码改写为

<form action="">
{:token()}
<input type="password" name="newpassword" />
<input type="submit" />
</form>

我们添加了一个{:token()},由于这个token是我们服务端动态输出的,伪装者的服务器没法获取该值,此时我们再做好服务端验证这个token是否有效即可,常用的方法如下

 $token = $this->request->post('__token__');

//验证Token
if (!$token || !\think\Validate::is($token, "token", ['__token__' => $token])) {
        $this->error("请勿非常请求");
}

更多Token表单令牌的使用方法可以参考ThinkPHP5官方文档:https://www.kancloud.cn/manual/thinkphp5/193918

服务器安全

服务器安全涉及目录安全、站点配置、安全组等多方面。

目录安全

目录安全在是经常容易生产问题的一个环节,很多时候我们都会忽略或忘记进行站点目录安全的配置。

我们建议在生产环境只开放uploadsruntime目录的读写权限,其次还需要关闭uploads目录执行PHP的权限,因为很多时候用户上传恶意脚本,而服务端uploads目录又未屏蔽PHP,导致用户数据泄漏丢失。

通常情况在生产环境下建议使用

chown www:www /var/www/yoursite -R
chmod 655 /var/www/yoursite -R
chmod u+w /var/www/yoursite/runtime -R
chmod u+w /var/www/yoursite/public/uploads -R

通过以上的配置还不够,我们还需要继续对服务器做WEB配置以限制PHP脚本的运行

WEB配置

Nginx可以通过以下配置禁止PHP脚本执行

location ~ ^/(uploads|assets)/.*\.(php|php5|jsp)$ {                                                                                                   
    deny all;                                                                                                                                         
} 

Apache可以通过在.htaccess中配置来禁用PHP脚本执行

RewriteEngine on RewriteCond % !^$
RewriteRule uploads/(.*).(php)$ – [F]

或使用

<Directory "/www/yoursite/public/uploads">
  <Files ~ ".php">
  Order allow,deny
  Deny from all
  </Files>
</Directory>

其次在新增网站配置时务必绑定public目录为运行目录,同时启用open_basedir限制只允许FastAdmin的根目录,例如:fastcgi_param PHP_VALUE "open_basedir=/var/www/fastadmin/:/tmp/:/proc/";

通常也建议修改php.ini,禁用不安全的函数,配置如disable_functions = passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru

运行配置

首先打开application/config.php,做以下几项配置

app_debug 设置为 false
app_trace 设置为 false
deny_module_list 添加 admin //可以参考 https://ask.fastadmin.net/article/7640.html
cookie.httponly 设置为 true
cookie.secure 设置为 true   //前提是你的网站仅提供https访问

其次前台开启全局过滤,早期FastAdmin版本并未开启全局过滤。你也可以检查下你的代码。请手动修改以下代码:

application/common/controller/Frontend.php
application/common/controller/Api.php

注意修改其中的$this->request->filter过滤代码,可以修改成:

$this->request->filter('trim,strip_tags,htmlspecialchars');

同时也可以参考https://ask.fastadmin.net/article/7534.html 设置全局 HtmlPurifier 安全过滤

最后务必修改后台管理入口,将admin.php修改成自定义字符.php,以后都从我们自定义的后台入口进行登录。