该LNMP环境是在windows的VMware Workstation Pro中的Centos虚拟机上搭建的,各个软件的版本为:Centos7 + Nginx1.12.2 + Mysql-5.6.38 + PHP7.2.0。
0x01 前置知识
linux上进程有5种状态:
1.运行(正在运行或在运行队列中等待)
2.中断(休眠中, 受阻, 在等待某个条件的形成或接受到信号)
3.不可中断(收到信号不唤醒和不可运行, 进程必须等待直到有中断发生)
4.僵死(进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放)
5.停止(进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行)
使用带"-l"选项的ls命令时,将以长格式显示文件或目录的详细信息,其中包括了该文件的权限和归属等参数。例如,执行如下操作列出当前目录下子目录和文件的详细属性。
第1个字符:表示该文件的类型,可以是d(目录)、b(块设备)、c(字符设备文件)、-(普通文件)、l(链接文件)等。
第2~4个字符:表示该文件的属主用户(user)对该文件的访问权限。
第5~7个字符:表示该文件的属组内各成员用户对该文件的访问权限。
第8~10个字符:表示其他任何用户(Other)对该文件的访问权限。
“r、w、x"分别表示可读、可写、可执行。若需要去除对应的权限,则使用”-"表示。
whereis
whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b)、man说明文件(参数-m)和源代码文件(参数-s)。如果省略参数,则返回所有信息。
和find相比,whereis查找的速度非常快,这是因为linux系统会将系统内的所有文件都记录在一个数据库文件中,当使用whereis和下面即将介绍的locate时,会从数据库中查找数据,而不是像find命令那样,通 过遍历硬盘来查找,效率自然会很高。
但是该数据库文件并不是实时更新,默认情况下时一星期更新一次,因此,我们在用whereis和locate 查找文件时,有时会找到已经被删除的数据,或者刚刚建立文件,却无法查找到,原因就是因为数据库文件没有被更新。
Nginx 主进程(master process)会以 root 权限运行,之后主进程会读取 /etc/nginx/nginx.conf
文件中的 user 模块的配置,nginx 会使用配置指定的用户启动工作进程( worker process)。
那为什么主进程需要使用 root 权限?因为只有 root 可以监听小于1024的端口号,通常 webserver 使用 80/443 端口,这也就是为什么需要 root 来运行了。如果要更改非 root 用户来运行,需要更改下面的文件用户和用户组,当然你也就不能使用 <1024 的端口了。
- 精确匹配
location =
(完全一致) - 前缀匹配
location ^~
(匹配上以后不再进行后续的正则搜索) - 正则匹配
location ~
(区分大小写)location ~*
(不区分大小写) - 匹配不带任何修饰的前缀匹配
location /
Linux中的shell,是指一个面向用户的命令接口,表现形式就是一个可以由用户录入的界面,这个界面也可以反馈运行信息。
从逻辑上讲,用户在登录到Linux系统中之后,每时每刻都处在某个目录之中,此目录被称做工作目录或当前目录(Working Directory)。工作目录是可以随时改变的。用户初始登录到系统中时,其主目录(HomeDirectory)就成为其工作目录。
1、basedir参数
原文说明为:Path to installation directory. All paths are usually resolved relative to this.
解释:该参数指定了安装 MySQL 的安装路径(mysql安装目录),填写全路径可以解决相对路径所造成的问题。
2、datadir 参数
原文说明为:Path to the database root.
解释:该参数指定MySQL的数据文件的存放目录,数据库文件即我们常说的 MySQL data 文件。
- phpinfo() 功能描述:输出 PHP 环境信息以及相关的模块、WEB 环境等信息。危险等级:中
- passthru() 功能描述:允许执行一个外部程序并回显输出,类似于 exec()。危险等级:高
- exec() 功能描述:允许执行一个外部程序(如 UNIX Shell 或 CMD 命令等)。危险等级:高
- system() 功能描述:允许执行一个外部程序并回显输出,类似于 passthru()。危险等级:高
- chroot() 功能描述:可改变当前 PHP 进程的工作根目录,仅当系统支持 CLI 模式 PHP 时才能工作,且该函数不适用于 Windows 系统。危险等级:高
- scandir() 功能描述:列出指定路径中的文件和目录。危险等级:中
- chgrp() 功能描述:改变文件或目录所属的用户组。危险等级:高
- chown() 功能描述:改变文件或目录的所有者。危险等级:高
- shell_exec() 功能描述:通过 shell 执行命令,并将执行结果作为字符串返回。危险等级:高
- proc_open() 功能描述:执行一个命令并打开文件指针用于读取以及写入。危险等级:高
- proc_get_status() 功能描述:获取使用 proc_open() 所打开进程的信息。危险等级:高
- error_log() 功能描述:将错误信息发送到指定位置(文件)。安全备注:在某些版本的 PHP 中,可使用 error_log() 绕过 PHP safe mode, 执行任意命令。危险等级:低
- ini_alter() 功能描述:是 ini_set() 函数的一个别名函数,功能与 ini_set() 相同。具体参见 ini_set()。危险等级:高
- ini_set() 功能描述:可用于修改、设置 PHP 环境配置参数。危险等级:高
- ini_restore() 功能描述:可用于恢复 PHP 环境配置参数到其初始值。危险等级:高
- dl() 功能描述:在 PHP 进行运行过程当中(而非启动时)加载一个 PHP 外部模块。危险等级:高
- pfsockopen() 功能描述:建立一个 Internet 或 UNIX 域的 socket 持久连接。危险等级:高
- syslog() 功能描述:可调用 UNIX 系统的系统层 syslog() 函数。危险等级:中
- readlink() 功能描述:返回符号连接指向的目标文件内容。危险等级:中
- symlink() 功能描述:在 UNIX 系统中建立一个符号链接。危险等级:高
- popen() 功能描述:可通过 popen() 的参数传递一条命令,并对 popen() 所打开的文件进行执行。危险等级:高
- stream_socket_server() 功能描述:建立一个 Internet 或 UNIX 服务器连接。危险等级:中
- putenv() 功能描述:用于在 PHP 运行时改变系统字符集环境。在低于 5.2.6 版本的 PHP 中,可利用该函数 修改系统字符集环境后,利用 sendmail 指令发送特殊参数执行系统 SHELL 命令。危险等级:高
0x02 安装虚拟机
官网下载Centos7对应的ios文件,在VMware上配置。配置完成后出现了localhost变成了bogon的问题,网上查询后得知是DNS反向解析出错导致。先使用ip add
查询到虚拟机自动配置的ip,再使用vi /etc/hosts
将 ip localhost 写入本地hosts文件,重启后问题解决。
1.更新系统软件:
yum update
2.查看是否已安装wget:
rpm -qa|grep wget
否则安装:
yum install wget
3.查看是否已安装编译器:
rpm -qa|grep gcc
否则安装:
yum install gcc gcc-c++
0x03 安装Nginx
1.安装nginx依赖包
nginx的Rewrite模块和HTTP核心模块会使用到PCRE正则表达式语法:
yum -y install pcre pcre-devel
nginx的各种模块中需要使用gzip压缩:
yum -y install zlib zlib-devel
安全套接字层密码库:
yum -y install openssl openssl-develv
2.下载nginx包并解压(到/usr/local/src目录中)
cd /usr/local/src
wget http://nginx.org/download/nginx-1.12.2.tar.gz
tar -zxvf nginx-1.12.2.tar.gz
3.编译安装(到/usr/local/nginx目录中)
cd nginx-1.12.2
./configure --prefix=/usr/local/nginx
make
make install
4.创建并设置nginx运行账号
groupadd nginx
useradd -M -g nginx -s /sbin/nologin nginx
cd /usr/local/nginx/conf
vim nginx.conf
设置user参数如下:
user nginx nginx
其他配置参数暂时无需改动。
5.设置nginx为系统服务
vim /lib/systemd/system/nginx.service
文件内容:
[Unit]
Description=nginx
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target
6.设置nginx开机自启动
systemctl enable nginx.service
7.开启nginx服务
systemctl start nginx.service
查看nginx是否启动成功:
ps aux | grep nginx
在虚拟机浏览器中访问测试:
http://localhost
8.防火墙开放80端口(nginx默认使用80端口,可在nginx.conf中配置,若无需进行远程访问则不需要开放端口)
永久开放80端口:
firewall-cmd --zone=public --add-port=80/tcp --permanent
重启防火墙:
firewall-cmd --reload
查看防火墙开启状态:
systemctl status firewalld
查看80端口是否开放成功:
firewall-cmd --zone=public --query-port=80/tcp
可在windows宿主主机浏览器直接访问虚拟机ip测试是否可以成功访问。
0x04 安装MySQL
1.卸载已有mysql
查看是否已安装mysql:
rpm -qa mysql
有则卸载:
rpm -e --nodeps
文件名称
是否存在与mysql相关的文件或目录:
whereis mysql
是则删除:
rm -rf
文件名称
rm -rf /usr/lib64/mysql
rm -rf /usr/share/mysql
存在/etc/my.cnf,则需要删除:
rm /etc/my.cnf
2.安装编译mysql需要的依赖包
yum install libevent* libtool* autoconf* libstd* ncurse* bison* openssl*
3.安装cmake(mysql5.5之后需要用cmake支持编译安装)
查看是否已安装cmake:
rpm -qa cmake
没有则下载编译安装:
cd /usr/local/src
wget http://www.cmake.org/files/v2.8/cmake-2.8.12.1.tar.gz
tar -zxvf cmake-2.8.12.1.tar.gz
cd cmake-2.8.12.1
./configure
make
make install
检查cmake是否安装成功:
cmaek --version
4.下载mysql包并解压(到/usr/local/src)
cd /usr/local/src
wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.38.tar.gz
tar -zxvf mysql-5.6.38.tar.gz
5.编译安装(到/usr/local/src/mysql-5.6.38)
cd mysql-5.6.38
cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/usr/local/mysql/data -DSYSCONFDIR=/etc -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_MEMORY_STORAGE_ENGINE=1 -DWITH_READLINE=1 -DMYSQL_UNIX_ADDR=/var/lib/mysql/mysql.sock -DMYSQL_TCP_PORT=3306 -DENABLED_LOCAL_INFILE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci
make
make install
6.配置mysql
groupadd mysql
useradd -M -g mysql -s /sbin/nologin mysql
chown -R mysql:mysql /usr/local/mysql
7.初始化配置
cd /usr/local/mysql/scripts
./mysql_install_db --basedir=/usr/local/mysql --datadir=/usr/local/mysql/data
--user=mysql
8.设置mysql为系统服务
vi /lib/systemd/system/mysql.service
文件内容:
[Unit]
Description=mysql
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/mysql/support-files/mysql.server start
ExecStop=/usr/local/mysql/support-files/mysql.server stop
ExecRestart=/usr/local/mysql/support-files/mysql.server restart
ExecReload=/usr/local/mysql/support-files/mysql.server reload
PrivateTmp=true
[Install]
WantedBy=multi-user.target
9.设置mysql服务开机自启动
systemctl enable mysql.service
10.启动mysql
systemctl start mysql.service
启动失败,使用/usr/local/mysql/support-files/mysql.server restart
启动查看详细错误原因:
“mysql_safe Directory ‘/var/lib/mysql’ for UNIX socket file don’t exists.”
手动创建:
mkdir /var/lib/mysql
chown -R mysql:mysql /var/lib/mysql
再次启动
查看是否启动成功:
ps aux|grep mysql
11.登录mysql并设置root密码
/usr/local/mysql/bin/mysql -u root
set password=password('root');
退出sql命令行:
exit;
0x05 安装PHP
1.安装php依赖包
yum install libxml2 libxml2-devel openssl openssl-devel bzip2 bzip2-devel libcurl libcurl-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel gmp gmp-devel libmcrypt libmcrypt-devel readline readline-devel libxslt libxslt-devel
2.下载php包并解压
cd /usr/local/src
wget https://www.php.net/distributions/php-7.2.0.tar.gz
tar -zxvf php-7.2.0.tar.gz
3.编译安装(到/usr/local/php目录)
cd php-7.2.0
./configure --prefix=/usr/local/php --disable-fileinfo --enable-fpm --with-config-file-path=/etc --with-config-file-scan-dir=/etc/php.d --with-openssl --with-zlib --with-curl --enable-ftp --with-gd --with-xmlrpc --with-jpeg-dir --with-png-dir --with-freetype-dir --enable-gd-native-ttf --enable-mbstring --with-mcrypt=/usr/local/libmcrypt --enable-zip --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-mysql-sock=/var/lib/mysql/mysql.sock --without-pear --enable-bcmath
(注:–with-mcrypt参数指定的是libmcrypt的安装目录。Php7不再使用mysql的库来支持mysql的连接,而是启用了mysqlnd来支持,所以php7的编译已经不再使用–with-mysql参数指定mysql的安装位置了,若想支持mysql,需要设置–enable-mysqlnd、–with-mysqli和–with-pdo-mysql=mysqlnd参数,–with-mysql-sock指定的是编译mysql时-DMYSQL_UNIX_ADDR参数指定的文件)
make
make install
4.将php包解压目录中的配置文件放置到正确位置(configure命令中的–with-config-file-path设置的位置)
cp php.ini-development /etc/php.ini
5.创建并设置php-fpm运行账号
groupadd www-data
useradd -M -g www-data -s /sbin/nologin www-data
cd /usr/local/php/etc
cp php-fpm.conf.default php-fpm.conf
vi php-fpm.conf
发现搜索不到“user”(设置运行账号的位置),但发现文件的最后一行:
所以:
cd php-fpm.d
cp www.conf.default www.conf
vi www.conf
搜索“user”设置运行账号:
user=www-data
group=www-data
6.配置nginx支持php
vi /usr/local/nginx/conf/nginx.conf
修改完成之后记得重启nginx服务:
systemctl start nginx.service
7.设置php-fpm为系统服务
vi /etc/systemd/system/php-fpm.service
文件内容:
[Unit]
Description=php-fpm
After=network.target
[Service]
Type=forking
ExecStart=/usr/local/php/sbin/php-fpm
PrivateTmp=True
[Install]
WantedBy=multi-user.target
8.设置php-fpm服务开机自启动
systemctl enable php-fpm.service
9.启动php-fpm
systemctl start php-fpm.service
查看是否启动成功:
ps aux | grep php-fpm
10.写一个php脚本测试nginx是否已支持php,php是否已支持mysql(到/usr/local/nginx/html/目录)
<?php
$servername = "192.168.202.128";
$username = "root";
$password = "root";
// 创建连接
$conn = mysqli_connect($servername, $username, $password);
// 检测连接
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
echo "Connection successful";
?>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ri5oasEf-1627636075363)(C:\Users\lyz\AppData\Roaming\Typora\typora-user-images\image-20210705153307176.png)]
至此,LNMP环境搭建完成。
0x06 总体加固
1.关闭不需要的端口
只打开如下端口:
服务 | 端口 |
---|---|
ssh | 443(限制IP) |
http | 80 |
https | 443 |
ftp | 21,20000-30000 |
2.配置https
3.设置目录权限
防止其他用户读取自己的敏感文件,防止配置信息泄露,防止其他用户在自己的目录中写shell。
0x07 Nginx加固
1.配置nginx的配置文件nginx.conf
#隐藏nginx的版本信息
http
{
server_tokens off;
}
# 设置timeout设低来防御DOS攻击
http
{
client_body_timeout 10;
client_header_timeout 30;
keepalive_timeout 30;
send_timeout 10;
}
#封杀各种user-agent
除非经过伪装,恶意扫描工具一般都会在user-agent里留下某些特征字眼,比如scan,nmap等。我们可以用正则匹配这些字眼,从而达到过滤的目的,请根据需要调整。
if ($http_user_agent ~* "java|python|perl|ruby|curl|bash|echo|uname|base64|decode|md5sum|select|concat|httprequest|httpclient|nmap|scan" ) {
return 403;
}
if ($http_user_agent ~* "" ) {
return 403;
}
具体的非法user-agent还得慢慢从日志中逐个提取。
# 封杀特定的url
location ~ ^/(\.user.ini|\.ht|\.git|\.svn|\.project|LICENSE|README.md) {
deny all;
}
# 封杀特定的http方法和行为
if ($request_method !~ ^(GET|POST|HEAD)$ ) {
return 405;
}
if ($http_range ~ "\d{9,}") {
return 444;
}
# 强制网站使用域名访问,可以逃过IP扫描,比如
if ( $host !~* 'abc.com' ) {
return 403;
}
# url 参数过滤敏感字
if ($query_string ~* "union.*select.*\(") {
rewrite ^/(.*)$ $host permanent;
}
if ($query_string ~* "concat.*\(") {
rewrite ^/(.*)$ $host permanent;
}
#必须携带 referer
if ($http_referer = "" ) {
return 403;
}
2.overlayfs
假设网站根目录/var/www/html有一个目录upload是要求可读写的,其他只读即可:
mkdir -p /data/lower
mkdir -p /data/upper
mkdir -p /data/worker
mv /var/www/html/upload /data/upper/
mv /var/www/html/* /data/lower/
mount -t overlayfs overlay -o lower=/data/lower,upper=/data/upper,workdir=/data/worker /var/www/html
0x08 MySQL加固
1.phpMyAdmin管理数据库限制
进入 phpMyAdmin 目录,找到 config.inc.php,如果没有,可以将根目录下的config.sample.inc.php 复制为 config.inc.php。
编辑 config.inc.php,添加下面两行代码,其中 192.168.202.128 是允许访问 phpMyAdmin 的 IP,Access denied 是未经授权访问时的提示信息:
?$ip_prefix = '111.18.89.174';
if (substr($_SERVER['REMOTE_ADDR'], 0, strlen($ip_prefix)) != $ip_prefix ) die('Access denied');
2.修改phpMyAdmin访问mysql数据库所占用的端口号
修改phpmyadmin目录下libraries下的配置文件config.default.php:
$cfg['Servers'][$i]['port'] = ''
#上面是默认语句,默认端口为3306
$cfg['Servers'][$i]['port'] = '3310';
#我们修改端口,指定为3301,这个可以自由指定
0x09 PHP加固
1.配置php的配置文件php.ini
#禁用不安全的PHP函数
disable_functions = show_source,system,shell_exec,passthru,exec,popen,proc_open,proc_get_status,phpinfo
#关闭php错误日志
display_errors = On
改为
display_errors = Off
#隐藏php版本信息
expose_php = On
改为
expose_php = Off
#禁止动态加载链接库
disable_dl = On;
改为
enable_dl = Off;
#禁用打开远程url
allow_url_fopen = On
改为
allow_url_fopen = Off
2.php上传分离
将文件上传到远程服务器,例如nfs等。当然,也可以调用事先写好的php接口。 即使有文件上传漏洞,但是文件被上传到了静态服务器上,木马等文件也无法执行。
例如:
php站点php.server.com,目录/data/site/www
静态文件站点static.server.com,目录/data/site/static
文件直接被传到了/data/site/static,上传的文件无法通过php.server.com访问,只能通过static.server.com访问,但是static.server.com不支持php,故恶意文件无法执行。
3.使用open_basedir限制虚拟主机跨目录访问
[HOST=php.server.com]
open_basedir=/data/site/php.server.com/:/tmp/
[HOST=test.server.com]
open_basedir=/data/site/test.server.com/:/tmp/
如上配置的意思是php.server.com下的php程序被限制在open_basedir配置的两个目录下, 不可以访问到其他目录。如果没有做以上的配置,那么test.server110.com与php.server.com的程序可以互相访问。
如果其中一个站点有漏洞被黑客植入了webshell,那么他可以通过这个站点拿下同一台服务器的其他站点,最后挂木马。
4.设置php用户只读
0x10 安全思考
如果不进行网站加固,则会导致配置信息泄露、敏感文件被读取。还会存在sql注入、文件上传、文件包含、csrf、xss等漏洞。黑客不仅可以轻而易举地获取到敏感数据,甚至还能利用这些漏洞获得webshell,得到网站的控制权,进而造成:
1.劫持网站,导致用户点击进入网站随即跳转到不良网站。
2.网站首页被篡改成恶意的内容,网站被挂马,被植入黑链。
3.网站文件被恶意删除。