不谈具体的代码,php站点安全防护心得
首先,php本身有漏洞不在这篇文章的讨论范围之内,具体问题自行解决,这里要说的,是假如代码就是有漏洞,但是漏洞又找不到的情况下,如何去做。此文章仅针对小站点,大站点请忽略。
常见的漏洞有三个,通过XSS进入了后台,上传木马,sql注入。sql注入百度搜下很多,我的办法比较笨,过滤的严格一点,特殊地方不能过滤的,再特殊对待
实验环境
centos7 php7.1 nginx
防御XSS:
后台一般都有个后台目录,给该目录加上目录保护(浏览器打开目录,会提示输入账号密码),这样即便被拿到了后台的cookie和地址,也进入不了。下面是通过宝塔面板的设置截图。
按照提示填写即可,注意名称虽然可以用中文,但是感觉尽量还是用英文吧
然后就有个小坑,实际应用在网站上不好用,于是去找nginx的相关配置文件,宝塔的nginx配置文件路径为(/www/server/panel/vhost/nginx/dir_auth/)。
location ~* ^/laozhang/* {
#AUTH_START
auth_basic "Authorization";
auth_basic_user_file /www/server/pass/xiaozhu.5mai.net/test.pass;
include enable-php-71.conf;
#AUTH_END
}
如果没有伪静态,这样是没问题的,但是有了伪静态,得这样配置
location ~* ^/laozhang/* {
#AUTH_START
auth_basic "Authorization";
auth_basic_user_file /www/server/pass/ytsn.5mai.net/a.pass;
error_page 404 = @admin;
error_page 405 = @admin;
include enable-php-71.conf;
#AUTH_END
}
location @admin {
rewrite ^(.*)$ /index.php?s=$1 last; break;
}
#如果nginx是通过反向代理的形式对接的(比如swoole),那么就这么写
location @admin {
proxy_pass http://127.0.0.1:20199;
proxy_http_version 1.1;
proxy_read_timeout 360s;
proxy_redirect off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
add_header X-Cache $upstream_cache_status;
#Set Nginx Cache
add_header Cache-Control no-cache;
expires 12h;
}
后半部分是具体的伪静态规则,我这个是tp的。如果有put或者delete,还想要对405再特殊处理下
加了这个后,如果后台是用vue做的,接口不同,用vue的代理模式就好了
上传漏洞
这个方案的思想来自java,都说java比php安全,但是语言本身哪有什么谁比谁安全的说法,不过仔细想一下,不是java安全,是java的形式无意中成就了他的安全。
我们都知道java是达成jar包或者war包放在服务器上(jsp就不说了),也就是说即便被上传了木马文件,也运行不了,jar包本身又不能被修改,所以自然也就没有了上传漏洞。
按照此思路去改造php的站点,首先,找到你的网站的入口文件,以tp为例,是public/index.php,在nginx的站点配置文件上设置,如果是php文件,只允许index.php访问,其他访问都是404
set $flag 0;
if ($uri ~* /.*\.php) {
set $flag "${flag}1";
}
if ($uri != "/index.php") {
set $flag "${flag}2";
}
# fastadmin系统会有个随机的php文件作为后台入口,注意此时不能用=,因为$uri是会包含 /a.php/user/index 的,所以要用~检索是否包含
if ($uri !~ "/345DFGii41.php") {
set $flag "${flag}3";
}
if ($flag = "0123") { #注意,如果加了345DFGii41.php的判断,这里的012要改成0123,也就是加几个这里就加多少数字
return 404;
}
1,尽量放在站点配置文件比较考上的位置,如果写在下面,容易被别的规则先运行,就执行不到它了
2,如果你的入口文件有多个,那么久按照这个套路一直写(我水平有限,只能这样,希望有大神指点)
这样即便被上传了木马,也运行不了。
然后设置除了上传目录外,所有目录权限都为555(不可写),因为虽然木马文件传了没用,但是万一代码漏洞能造成index.php被改了怎么办。
然后就发现一个很讨厌的问题,设置555后,ftp也不能上传了,因为ftp也是www权限,自然也就上传不了了。
此时如果你是用jenkins或者跳板机这种比较高端的途径,那可以忽略,这里直说我们这种低端玩家。
用root权限肯定还是能传的,传完了后再改成555和www权限,这样是可以的。但是平常小站点开发,有点小问题IDE改完了直接快捷键ftp提交,省时省力,不想用root这种方式。
这个解决思路来源于宝塔,发现用面板还是可以改文件,并且改完了还是www用户。猜测宝塔应该是用一个更高级的权限去修改文件,然后再重新改权限为www。所以想到这么个办法。
ftp设置一个新目录,增加一个目录监控,ftp上传上文件后,再用root命令把那个新目录的文件同步到网站目录,并且重置权限。
目录监控软件【inotifywait】,安装:yum install inotify-tools -y
编写监控脚本
#!/bin/bash
#date:20200126
#explain:监控目录是否发生变化
CHECKDIR="/www/wwwroot/bpc_ftp/" #监控目录路径
PHPFILE="/www/wwwroot/inotifywait/bpc.php" #发送脚本路径
function CheckDir {
inotifywait -mqr --timefmt '%y-%m-%d %H:%M:%S' --format '%w %T %f %e' -e 'delete,close,create,moved_from' $CHECKDIR|while read event;
do
echo $event
php $PHPFILE $event
done
}
CheckDir
脚本具体什么意思大家自行百度,都说的很详细。这里说一下我自己遇到的坑。
首先是–format后面的参数设置,百度搜到的别人写的,都没有%w,这样就造成监听到的只有文件,没有文件所在的目录。最开始试了各种方法都不行,最后还是得靠仔细看参数说明。
然后是-e后面的,这个是要监听的动作,这里有个大坑,我是好一会才绕过来。
按照一般的想法,要监听创建,覆盖,删除这3个动作,软件没有覆盖这个动作,有个修改(modify)。这里就要理解一下文件传输的基本逻辑,首先传输一个文件,显示创建一个文件,然后是不断往这个文件加内容,最后是关闭文件。如果你传一个10M的文件,那么这个加内容的动作,会触发N多次的modify,服务器就挂了。所以要监听文件被关闭的动作。
脚本首先是要启动inotifywait,然后把监听到的内容发给php脚本,让php去复制文件(写shell确实比较蛋疼,这里用python也比较好,不过既然是php站,就尽量少引用别的东西了)
启动脚本并且后台运行【nohup bash /tmp/test/inot.sh >> outhup.log 2>&1 &】
查看是否运行【ps -eo pid,etime,cmd|grep “inot”| grep -v grep】
关闭脚本【ps -ef |grep inot |awk ‘{print $2}’|xargs kill -9】
下面是PHP脚本
<?php
function Directory($dir){
return is_dir ( $dir ) or Directory(dirname( $dir )) and mkdir ( $dir , 0555) and chown($dir,"www") and chgrp($dir,"www");
}
$file_path='/www/wwwroot/bpc_ftp/'; //ftp的上传目录
$www_path='/www/wwwroot/bpc.yataisannong.com/'; //网站文件的目录
$argv=$_SERVER['argv'];
if(!$argv){
exit;
}
file_put_contents(__DIR__."/log.txt",var_export($argv,1)."\r\n",FILE_APPEND);
$action=$argv[5];
$path=$argv[1];
$file=$argv[4];
//网站对应目录
$_path=str_replace($file_path,$www_path,$path);
if($action=='CLOSE_NOWRITE,CLOSE'){
Directory($_path);
copy($path.$file,$_path.$file);
chmod($_path.$file,0555);
chown($_path.$file,"www");
chgrp($_path.$file,"www");
}elseif($action=='CREATE,ISDIR'){
Directory($_path.$file);
}elseif($action=='DELETE' || $action=='MOVED_FROM'){
if(is_file($_path.$file)){
unlink($_path.$file);
}
}elseif($action=='DELETE,ISDIR'){
if(is_dir($_path.$file)){
rmdir($_path.$file);
}
}
php要开启chmod和chgrp这两个函数
总结
平民方案,大神直接绕过即可
感谢这篇文章对我的初始帮助
https://www.cnblogs.com/yanjieli/p/10370503.html