11.28 限定某个目录禁止解析php
需求:我们某些目录允许用户上传图片、文档等文件,如果有恶意用户上传一些php木马文件,我们的服务器就危险了,所以对于用户能够上传的目录,我们需要限定某个目录禁止解析php。#(sql注入漏洞了解一下)(web安全了解一下)
场景:猜想是SQL漏洞,抓包查询查询数据库的请求,发现某个不寻常的请求来源于某个图片目录下的php文件,里面写有一句话木马。
<Directory /usr/local/apache2.4/docs/dummy-host2.example.com/upload>
php_admin_flag engine off
<FilesMatch (.+)\.php(.*)>
Order allow,deny
Deny from all
</FilesMatch>
</Directory>
filesmatch可有可无。虽然都不能解析,但是没有filesmatch会被看到源码,不友好。
[root@localhost: ~]# curl -x127.0.0.1:80 'http://dummy-host2.example.com/upload/123.php' -I
HTTP/1.1 403 Forbidden
Date: Mon, 06 Aug 2018 15:31:39 GMT
Server: Apache/2.4.32 (Unix) PHP/5.6.30
Content-Type: text/html; charset=iso-8859-1
我们把filesmatch注释掉之后,
[root@localhost: ~]# vim /usr/local/apache2.4/conf/extra/httpd-vhosts.conf
[root@localhost: ~]# /usr/local/apache2.4/bin/apachectl graceful
[root@localhost: ~]# curl -x127.0.0.1:80 'http://dummy-host2.example.com/upload/123.php'
<?php
echo "123.php";
?>
访问123.php就会变为源码,不会被解析。这种静态文件目录下是不建议放置php文件的。
11.29 限制user_agent
user_agent可以看做一个浏览器的标识。
CC攻击
攻击者借助代理服务器生成指向受害主机的合法请求,实现DDOS和伪装就叫:CC(ChallengeCollapsar)。
CC主要是用来攻击页面的。大家都有这样的经历,就是在访问论坛时,如果这个论坛比较大,访问的人比较多,打开页面的速度会比较慢,访问的人越多,论坛的页面越多,数据库压力就越大,被访问的频率也越高,占用的系统资源也就相当可观。
一个静态页面不需要服务器多少资源,甚至可以说直接从内存中读出来发给你就可以了,但是论坛就不一样了,我看一个帖子,系统需要到数据库中判断我是否有读帖子的权限,如果有,就读出帖子里面的内容,显示出来——这里至少访问了2次数据库,如果数据库的数据容量有200MB大小,系统很可能就要在这200MB大小的数据空间搜索一遍,这需要多少的CPU资源和时间?如果我是查找一个关键字,那么时间更加可观,因为前面的搜索可以限定在一个很小的范围内,比如用户权限只查用户表,帖子内容只查帖子表,而且查到就可以马上停止查询,而搜索肯定会对所有的数据进行一次判断,消耗的时间是相当的大。
CC就是充分利用了这个特点,模拟多个用户(多少线程就是多少用户)不停的进行访问(访问那些需要大量数据操作,就是需要大量CPU时间的页面).这一点用一个一般的性能测试软件就可以做到大量模拟用户并发。
用几千台几万台肉鸡去同时访问一个网站,而这个网站服务器性能不好或者没有高并发处理,就很容易挂掉这个网站。
这些访问都是正常请求,我们可以通过识别这个规律的user_agent来阻止cc攻击。
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP_USER_AGENT} .*curl.* [NC,OR] //这里是两个条件,没有OR默认为AND
RewriteCond %{HTTP_USER_AGENT} .*baidu.com.* [NC] //NC标识忽略大小写
RewriteRule .* - [F] //F为forbidden
</IfModule>
这样使用curl访问就会被禁止。
[root@localhost: ~]# curl -x127.0.0.1:80 http://dummy-host2.example.com/123.php
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /123.php
on this server.<br />
</p>
</body></html>
但是使用chrome访问就可以。
同样curl -A可以定义user_agent,更换agent就可以了。
[root@localhost: ~]# curl -A "lalala" -x127.0.0.1:80 http://dummy-host2.example.com/123.php
123.php[root@localhost: ~]#
[root@localhost: ~]# curl -A "baiDU" -x127.0.0.1:80 http://dummy-host2.example.com/123.php
[root@localhost: ~]# vim /usr/local/apache2.4/conf/extra/httpd-vhosts.confst: ~]#
[root@localhost: ~]# curl -A "baiDU.com" -x127.0.0.1:80 http://dummy-host2.example.com/123.php
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /123.php
on this server.<br />
</p>
</body></html>
[root@localhost: ~]# tail -5 /usr/local/apache2.4/logs/dummy-host2.example.com-access_20180806.log
192.168.244.1 - - [06/Aug/2018:23:47:31 +0800] "GET /123.php HTTP/1.1" 301 246 "-" "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3486.0 Safari/537.36"
192.168.244.1 - - [06/Aug/2018:23:47:31 +0800] "GET /123.php HTTP/1.1" 200 7 "-" "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3486.0 Safari/537.36"
127.0.0.1 - - [06/Aug/2018:23:50:07 +0800] "GET http://dummy-host2.example.com/123.php HTTP/1.1" 200 7 "-" "lalala"
127.0.0.1 - - [06/Aug/2018:23:50:23 +0800] "GET http://dummy-host2.example.com/123.php HTTP/1.1" 200 7 "-" "baiDU"
127.0.0.1 - - [06/Aug/2018:23:50:51 +0800] "GET http://dummy-host2.example.com/123.php HTTP/1.1" 403 216 "-" "baiDU.com"
这就是如何过滤user_agent的方法。
11.30/31 php相关配置
查看php的配置文件的路径,
[root@localhost: ~]# /usr/local/php5/bin/php -i | grep -i "loaded configuration file"
Loaded Configuration File => /usr/local/php5/etc/php.ini
当然,这里查看的有可能不是你站点的配置,想查看站点的配置文件应该在站点的根目录写一个带有phpinfo()的页面,去查询配置文件。
在这里我重新编辑了一下host2的主页,
这里的路径就是准确的pho.ini的位置。
打开php.ini,查看一下配置。最重要的就是disable functions,就是列出的函数在php文件中是不能使用的,
disable_functions = eval,assert,popen,passthru,escapeshellarg,escapeshellcmd,passthru,exec,system,chroot,scandir,chgrp,chown,escapeshellcmd,escapeshellarg,shell_exec,proc_get_status,ini_alter,ini_restore,dl,pfsockopen,openlog,syslog,readlink,symlink,leak,popepassthru,stream_socket_server,popen,proc_open,proc_close,phpinfo
一般线上会禁掉phpinfo,因为会暴露自己的目录信息。
[root@localhost: ~]# curl -x192.168.244.128:80 'dummy-host2.example.com/index.php'
host2
[root@localhost: ~]# cat /usr/local/apache2.4/docs/dummy-host2.example.com/index.php
<?php
echo "host2\n";
phpinfo();
?>
线下的时候我们还需要使用phpinfo,所以暂时先打开。
data.timezone也需要定义一下,如果不定义会出现警告。
PHP Warning: Unknown: It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set date.timezone to select your timezone. in Unknown on line 0
我们可以把时区定义到上海,重庆,北京等,
date.timezone = Asia/Shanghai
php_error也是在线下显示帮助你查找错误,
; http://php.net/display-errors
display_errors = On
比如打开之后,我把phpinfo禁掉,就会报错,也会泄露目录信息。
[root@localhost: ~]# curl -x192.168.244.128:80 'dummy-host2.example.com/index.php'
host2
<br />
<b>Warning</b>: phpinfo() has been disabled for security reasons in <b>/usr/local/apache2.4/docs/dummy-host2.example.com/index.php</b> on line <b>3</b><br />
所以线上关闭就好。如果想常闭,那我们配置一下error_log就好了。
; http://php.net/log-errors
log_errors = On
error_log = /tmp/php_errors.log
; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
; Development Value: E_ALL
; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT
; http://php.net/error-reporting
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
我们重新访问一下,
root@localhost: ~]# curl -x192.168.244.128:80 'dummy-host2.example.com/index.php'
host2
<br />
<b>Warning</b>: phpinfo() has been disabled for security reasons in <b>/usr/local/apache2.4/docs/dummy-host2.example.com/index.php</b> on line <b>3</b><br />
[root@localhost: ~]# cat /tmp/php
php_errors.log php-gdtest/
[root@localhost: ~]# cat /tmp/php_errors.log
[07-Aug-2018 12:21:04 Asia/Shanghai] PHP Warning: phpinfo() has been disabled for security reasons in /usr/local/apache2.4/docs/dummy-host2.example.com/index.php on line 3
[root@localhost: ~]# ll /tmp/php_errors.log
-rw-r--r-- 1 daemon daemon 173 Aug 7 12:21 /tmp/php_errors.log
可以看到日志的属主是httpd进程的用户,如果没有生成日志,一定要查看定义的目录有没有设置权限。
也可以先创建目录文件,然后给一个777的权限。
我们在做一个测试,在php中加一行错误代码。
[root@localhost: ~]# head !$
head /usr/local/apache2.4/docs/dummy-host2.example.com/123.php
<?php
echo "123.php\n";
jaljlsgjl;ag
?>
[root@localhost: ~]# curl -x192.168.244.128:80 'dummy-host2.example.com/123.php'
123.php
<br />
<b>Notice</b>: Use of undefined constant jaljlsgjl - assumed 'jaljlsgjl' in <b>/usr/local/apache2.4/docs/dummy-host2.example.com/123.php</b> on line <b>3</b><br />
<br />
<b>Notice</b>: Use of undefined constant ag - assumed 'ag' in <b>/usr/local/apache2.4/docs/dummy-host2.example.com/123.php</b> on line <b>4</b><br />
[root@localhost: ~]# !cat
cat /tmp/php_errors.log
[07-Aug-2018 12:21:04 Asia/Shanghai] PHP Warning: phpinfo() has been disabled for security reasons in /usr/local/apache2.4/docs/dummy-host2.example.com/index.php on line 3
[07-Aug-2018 12:27:48 Asia/Shanghai] PHP Notice: Use of undefined constant jaljlsgjl - assumed 'jaljlsgjl' in /usr/local/apache2.4/docs/dummy-host2.example.com/123.php on line 3
[07-Aug-2018 12:27:48 Asia/Shanghai] PHP Notice: Use of undefined constant ag - assumed 'ag' in /usr/local/apache2.4/docs/dummy-host2.example.com/123.php on line 4
[root@localhost: ~]# curl -x192.168.244.128:80 'dummy-host2.example.com/123.php' -I
HTTP/1.1 200 OK
Date: Tue, 07 Aug 2018 04:28:20 GMT
Server: Apache/2.4.32 (Unix) PHP/5.6.30
X-Powered-By: PHP/5.6.30
Cache-Control: max-age=0
Expires: Tue, 07 Aug 2018 04:28:20 GMT
Content-Type: text/html; charset=UTF-8
下面再介绍一个安全相关的open_basedir安全选项。
一台服务器上有多个站点,而某一个站点被黑被拿到权限,很有可能其他站点也会暴露,所以我们可以通过目录隔离的方式保护其他站点。
open_basedir = /usr/local/apache2.4/docs/dummy-host2.example.com/:/tmp
那这时候我打开host1的时候就会报错,
[root@localhost: ~]# curl -x192.168.244.128:80 dummy-host.example.com/
<br />
<b>Warning</b>: Unknown: open_basedir restriction in effect. File(/usr/local/apache2.4/docs/dummy-host.example.com/index.php) is not within the allowed path(s): (/usr/local/apache2.4/docs/dummy-host2.example.com/:/tmp) in <b>Unknown</b> on line <b>0</b><br />
<br />
<b>Warning</b>: Unknown: failed to open stream: Operation not permitted in <b>Unknown</b> on line <b>0</b><br />
<br />
<b>Fatal error</b>: Unknown: Failed opening required '/usr/local/apache2.4/docs/dummy-host.example.com/index.php' (include_path='.:/usr/local/php5/lib/php') in <b>Unknown</b> on line <b>0</b><br />
虽然这样能保护host1,但是没办法访问host1不是我们的目的。
我们应该通过站点来禁止目录访问。php.ini是针对所有站点的,如果把目录设置成我的所有站点目录(/usr/local/apache2.4/docs/)又失去了意义。这个只能在虚拟主机的配置文件中做。
php_admin_value open_basedir "/usr/local/apache2.4/docs/dummy-host2.example.com/:/tmp"
针对不同的虚拟主机创建不同的open_basedir。
扩展
apache开启压缩
apache2.2到2.4配置文件变更
apache options参数
apache禁止trace或track防止xss
apache 配置https 支持ssl
11.32 PHP扩展模块安装
动态扩展模块安装。
虽然我们PHP安装的时候指定安装大部分模块,后面有需求还是需要手动再次添加模块。
需求,PHP安装完之后我们要安装一个redis的模块。redis是一个nosql模块,在LAMP模式下当缓存来用。
下载安装包
[root@localhost: ~]# cd /usr/local/src/
[root@localhost: src]# wget https://codeload.github.com/phpredis/phpredis/zip/develop
--2018-08-09 00:19:25-- https://codeload.github.com/phpredis/phpredis/zip/develop
Resolving codeload.github.com (codeload.github.com)... 13.229.189.0, 13.250.162.133, 54.251.140.56
Connecting to codeload.github.com (codeload.github.com)|13.229.189.0|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 240091 (234K) [application/zip]
Saving to: ‘develop’
100%[==============================================>] 240,091 224KB/s in 1.0s
2018-08-09 00:19:28 (224 KB/s) - ‘develop’ saved [240091/240091]
[root@localhost: src]# ls
apr-1.6.3 httpd-2.4.32 mariadb-10.3.8.tar.gz php-7.1.6
apr-1.6.3.tar.gz httpd-2.4.32.tar.gz mysql-5.5.61 php-7.1.6.tar.bz2
apr-util-1.6.1 isl-0.19 mysql-5.5.61.tar.gz
apr-util-1.6.1.tar.gz isl-0.19.tar.bz2 php-5.6.30
develop mariadb-10.3.8 php-5.6.30.tar.gz
develop其实是一个zip文件
[root@localhost: src]# file develop
develop: Zip archive data, at least v1.0 to extract
[root@localhost: src]# mv develop phpredis-develop.zip
[root@localhost: src]# zi
zic zip zipcloak zipgrep zipinfo zipnote zipsplit
[root@localhost: src]# zip -d phpredis-develop.zip
zip error: Nothing to do! (phpredis-develop.zip)
[root@localhost: src]# unzip phpredis-develop.zip
Archive: phpredis-develop.zip
399edf44f90f5c0b7be4f3bdefb6b402eda5f0af
creating: phpredis-develop/
inflating: phpredis-develop/.gitignore
inflating: phpredis-develop/.gitmodules
inflating: phpredis-develop/.travis.yml
inflating: phpredis-develop/COPYING
inflating: phpredis-develop/CREDITS
inflating: phpredis-develop/INSTALL.markdown
inflating: phpredis-develop/ISSUE_TEMPLATE.md
inflating: phpredis-develop/README.markdown
inflating: phpredis-develop/arrays.markdown
inflating: phpredis-develop/cluster.markdown
inflating: phpredis-develop/cluster_library.c
inflating: phpredis-develop/cluster_library.h
inflating: phpredis-develop/common.h
inflating: phpredis-develop/config.m4
inflating: phpredis-develop/config.w32
inflating: phpredis-develop/crc16.h
inflating: phpredis-develop/debian.control
creating: phpredis-develop/debian/
inflating: phpredis-develop/debian/changelog
extracting: phpredis-develop/debian/compat
inflating: phpredis-develop/debian/control
inflating: phpredis-develop/debian/copyright
inflating: phpredis-develop/debian/postinst
inflating: phpredis-develop/debian/postrm
inflating: phpredis-develop/debian/rules
creating: phpredis-develop/liblzf/
inflating: phpredis-develop/library.c
inflating: phpredis-develop/library.h
inflating: phpredis-develop/mkdeb-apache2.sh
inflating: phpredis-develop/mkdeb.sh
inflating: phpredis-develop/package.xml
inflating: phpredis-develop/php_redis.h
inflating: phpredis-develop/redis.c
inflating: phpredis-develop/redis_array.c
inflating: phpredis-develop/redis_array.h
inflating: phpredis-develop/redis_array_impl.c
inflating: phpredis-develop/redis_array_impl.h
inflating: phpredis-develop/redis_cluster.c
inflating: phpredis-develop/redis_cluster.h
inflating: phpredis-develop/redis_commands.c
inflating: phpredis-develop/redis_commands.h
inflating: phpredis-develop/redis_session.c
inflating: phpredis-develop/redis_session.h
creating: phpredis-develop/rpm/
inflating: phpredis-develop/rpm/php-redis.spec
extracting: phpredis-develop/rpm/redis.ini
inflating: phpredis-develop/serialize.list
creating: phpredis-develop/tests/
inflating: phpredis-develop/tests/RedisArrayTest.php
inflating: phpredis-develop/tests/RedisClusterTest.php
inflating: phpredis-develop/tests/RedisTest.php
inflating: phpredis-develop/tests/TestRedis.php
inflating: phpredis-develop/tests/TestSuite.php
inflating: phpredis-develop/tests/getSessionData.php
inflating: phpredis-develop/tests/make-cluster.sh
inflating: phpredis-develop/tests/mkring.sh
inflating: phpredis-develop/tests/regenerateSessionId.php
inflating: phpredis-develop/tests/startSession.php
使用phpize工具生成configure
[root@localhost: src]# cd phpredis-develop/
[root@localhost: phpredis-develop]# ls
arrays.markdown CREDITS mkdeb.sh redis_cluster.c
cluster_library.c debian package.xml redis_cluster.h
cluster_library.h debian.control php_redis.h redis_commands.c
cluster.markdown INSTALL.markdown README.markdown redis_commands.h
common.h ISSUE_TEMPLATE.md redis_array.c redis_session.c
config.m4 liblzf redis_array.h redis_session.h
config.w32 library.c redis_array_impl.c rpm
COPYING library.h redis_array_impl.h serialize.list
crc16.h mkdeb-apache2.sh redis.c tests
[root@localhost: phpredis-develop]# /usr/local/php5/bin/phpize
Configuring for:
PHP Api Version: 20131106
Zend Module Api No: 20131226
Zend Extension Api No: 220131226
[root@localhost: phpredis-develop]# ls configure
configure
配置
[root@localhost: phpredis-develop]# ./configure --with-php-config=/usr/local/php5/bin/php-config
一般来说就这个选项就好
make && make install之后
[root@localhost: phpredis-develop]# echo $?
0
[root@localhost: phpredis-develop]# make install
Installing shared extensions: /usr/local/php5/lib/php/extensions/no-debug-zts-20131226/
[root@localhost: phpredis-develop]# ls /usr/local/php5/lib/php/extensions/no-debug-zts-20131226/
opcache.so redis.so
在一个目录里就生成了两个.so文件。就是我们需要的模块。
但是这个时候php还是不支持redis的
[root@localhost: phpredis-develop]# /usr/local/php5/bin/php -m | grep redis
[root@localhost: phpredis-develop]#
配置文件中可以定义扩展目录的位置
; Directory in which the loadable extensions (modules) reside.
; http://php.net/extension-dir
; extension_dir = "./"
; On windows:
; extension_dir = "ext"
也可以查询当前的扩展模块目录
[root@localhost: phpredis-develop]# /usr/local/php5/bin/php -i | grep -i extension_dir
extension_dir => /usr/local/php5/lib/php/extensions/no-debug-zts-20131226 => /usr/local/php5/lib/php/extensions/no-debug-zts-20131226
sqlite3.extension_dir => no value => no value
添加模块
extension=redis.so
[root@localhost: phpredis-develop]# /usr/local/php5/bin/php -m | grep redis
redis
扩展包可以在源码包中查找
[root@localhost: phpredis-develop]# ls /usr/local/src/php-5.6.30/ext/
bcmath fileinfo mbstring pdo_firebird shmop tokenizer
bz2 filter mcrypt pdo_mysql simplexml wddx
calendar ftp mssql pdo_oci skeleton xml
com_dotnet gd mysql pdo_odbc snmp xmlreader
ctype gettext mysqli pdo_pgsql soap xmlrpc
curl gmp mysqlnd pdo_sqlite sockets xmlwriter
date hash oci8 pgsql spl xsl
dba iconv odbc phar sqlite3 zip
dom imap opcache posix standard zlib
enchant interbase openssl pspell sybase_ct
ereg intl pcntl readline sysvmsg
exif json pcre recode sysvsem
ext_skel ldap pdo reflection sysvshm
ext_skel_win32.php libxml pdo_dblib session tidy
如果没有就去官网下载,常用的在r.aminglinux.com里也有。
扩展