MENU

搭建LNMP环境及一些安全加固总结

August 3, 2019 • Read: 2949 • Web阅读设置

0x00 准备与目标

目标:准备学习环境,学习Web服务器的搭建过程,并做相应的加固学习

系统:CentOS Linux release 7.6.1810

目的:正确配置环境,写一个简单的前后端交互的SQL查询PHP页面,能够运行PHP代码并且可以使用PHP连接MYSQL,成功执行MYSQL的语句。

0x01 搭建LNMP环境

  • Q:什么是LNMP?
  • A:LNMP代表的就是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构。
L:Linux是一类Unix计算机操作系统的统称,是目前最流行的免费操作系统。代表版本有:debian、centos、ubuntu、fedora、gentoo等。
N:Nginx是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。
M:Mysql是一个小型关系型数据库管理系统。
P:PHP是一种在服务器端执行的嵌入HTML文档的脚本语言。
这四种软件均为免费开源软件,组合到一起,成为一个免费、高效、扩展性强的网站服务系统。

1.安装Nginx

首先要配置下防火墙,关于CentOS7防火墙控制着打开关闭查看端口。

查看打开的端口:

firewall-cmd --list-ports

打开80端口:

firewall-cmd --zone=public --add-port=80/tcp --permanent

允许http,https通信:

firewall-cmd --zone=public --add-service=http  --permanent    #允许http通信
firewall-cmd --zone=public --add-service=https  --permanent  #允许https通信

重启防火墙生效:

#重启firewall  
firewall-cmd --reload  
#停止firewall  
systemctl stop firewalld.service  
#禁止firewall开机启动  
systemctl disable firewalld.service 

在这里了解了下这些命令的具体含义:

–zone #作用域

–add-port=80/tcp #添加端口,格式为:端口/通讯协议

–permanent #永久生效,没有此参数重启后失效

之后就是需要安装Nginx需要gcc环境和一些库重启生效:

yum install -y gcc-c++
yum install -y pcre pcre-devel
yum install -y zlib zlib-devel
yum install -y openssl openssl-devel
reboot    #重启

这里了解下这些基本的库和Nginx要用gcc的原因:

gcc:Nginx编译依赖gcc环境。PS:后来好像没发现什么地方用到这个。

pcre:表达式库,Nginx中的http模块则使用到了该库来解析正则表达式。

zlib:用于压缩和解压缩,该库有很多方式,nginx使用zlib对http包的内容进行gzip。

openssl:安全套接字层密码库,这个不是很了解,查了下说是有一些密码算法、常用的密钥和证书封装管理功能和SSL协议,Nginx可以支持https协议即在SSL协议上传输http。

最后配置Nginx官方源,下载和安装最新版Nginx:

rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
yum install nginx

1564597332986.png

完事之后了解下Nginx的配置文件,默认安装完会存在这个位置:

主配置文件:/etc/nginx/nginx.conf
默认配置文件:/etc/nginx/conf.d/default.conf

配置文件这里要记着,一会儿可能要踩坑。

执行命令启动Nginx:

systemctl start nginx   # 启动

这样算是暂时成功了。

1564597822058.png

2.安装PHP

避免踩坑先把CentOS默认安装的PHP5.4卸载了。

使用命令:

rpm -qa|grep php  #查看所有PHP包
rpm -e 包名  # rpm -e 加包名删除原有的包

首先需要配置下PHP的yum源,这里我安装PHP7.2:

rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm

安装PHP7.2和一些扩展:

yum  install php72w php72w-cli php72w-common php72w-devel php72w-embedded php72w-fpm php72w-gd php72w-mbstring php72w-mysqlnd php72w-opcache php72w-pdo php72w-xml

→这步本人出了错误一直出Error 404:

1564669647090.png
查了下别人遇到同样的问题,可能是yum镜像数据库坏了。附上解决方法:

yum clean all
rpm --rebuilddb
yum update
yum install automake libtool flex bison pkgconfig gcc-c++ boost-devel libevent-devel zlib-devel python-devel ruby-devel

然后就是启动php-fpm,因为Nginx是通过php-fpm处理PHP文件:

systemctl start php-fpm  #启动
systemctl enable php-fpm  #设置开机启动

完成。

1564674728396.png
让Nginx支持PHP,在default.conf配置:

vi /etc/nginx/conf.d/default.conf
#里面修改以下的内容

location ~ \.php$ {
    root          /usr/share/nginx/html;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
}

#格式要对齐避免继续踩坑 
    
systemctl restart nginx  #重启服务

最后测试运行一个php文件:

vi /usr/share/nginx/html/index.php #编辑文件
代码如下:
<?php
    echo 'hello!';
?>

1564676426139.png

成功!

3.安装MySQL

这里安装安装MySQL5.7,比较简单的三步骤:

rpm -Uvh  http://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm
# 更新yum源
yum -y install mysql-community-server
# 安装MySQL
mysql -V
# 查看版本号

1564681113647.png

然后配置MySQL基本项:

systemctl start mysqld  #启动MySQL服务
systemctl enable mysqld  #设置MySQL服务开机自启动
grep 'temporary password' /var/log/mysqld.log
#查看/var/log/mysqld.log文件,获取并记录root用户的初始密码
2019-08-01T17:39:46.781453Z 1 [Note] A temporary password is generated for root@localhost: vqssdEq1DG)=   #这里我的初始密码为 vqssdEq1DG)=

MySQL的安全配置命令:

mysql_secure_installation

接下来几步是MySQL的安全配置,会重置初始密码等几步操作:

  • 重置root账号密码。
Enter password for user root: #输入上一步获取的root用户初始密码
The 'validate_password' plugin is installed on the server.
The subsequent steps will run with the existing configuration of the plugin.
Using existing password for root.
Estimated strength of the password: 100 
Change the password for root ? ((Press y|Y for Yes, any other key for No) : Y 
#是否更改root用户密码,输入Y
New password:
#输入新密码,长度为8至30个字符,必须同时包含大小写英文字母、数字和特殊符号。特殊符号可以是()` ~!@#$%^&*-+=|{}[]:;‘<>,.?/
Re-enter new password: #再次输入新密码
Estimated strength of the password: 100 
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : Y
  • 输入Y删除匿名用户账号。
By default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment.
Remove anonymous users? (Press y|Y for Yes, any other key for No) : Y  #是否删除匿名用户,输入Y
Success.
  • 输入Y禁止root账号远程登录。
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : Y 
#禁止root远程登录,输入Y
Success.
  • 输入Y删除test库以及对test库的访问权限。
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : Y #是否删除test库和对它的访问权限,输入Y
- Dropping test database...
Success.
  • 输入Y重新加载授权表。
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : Y 
#是否重新加载授权表,输入Y
Success.
All done!

1564683512898.png

这里我设置的允许root账号远程连接数据库,所以有一步是No。

也可以在MySQL执行命令:

use mysql;
update user set host = '%' where user = 'root'; 
# 使mysql root用户可以连接上任意的ip地址
FLUSH PRIVILEGES;

至此MySQL安装配置部分结束。


0x02 简单的后端至前端的数据交互页面

1.前后端数据交互的方法和原理

这里学习了CSDN某大佬的博文前后端数据交互的方法和原理

主要就是四个问题:

  • 前端请求数据如何发送到后端?
  • 后端如何接受前端的请求数据?
  • 后端如何对前端发送的数据进行处理?
  • 后端返回给前端处理结果和如何写入数据?

这里这个大佬的博文讲述的很详细。

我写了段简单测试PHP能否连接数据库成功的代码:

<?php 
$link=mysqli_connect("localhost","root","123456"); 
if(!$link) echo "Failed!"; 
else echo "OK!";
mysqli_close(); 
?> 

命名为test.php,使用命令创建:

vi /usr/share/nginx/html/test.php

1564731169642.png

2.数据库部分

前面允许了远程连接数据库,一般来说为了安全起见不需要在配置里设置不允许远程连接,这里直接在本机用Navicat连接服务器。

创建数据库center。

1564684487246.png

创建数据表users。

1564685279245.png

在数据表中插入几条数据。

1564685152594.png

3.PHP部分

按照想法做了一个简单的数据交互PHP页面,前端GET传输id参数值,数据库传输对应参数的用户名和密码,在web根目录创建文件名为user.php。页面代码如下:

vi /usr/share/nginx/html/user.php
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>用户信息</title>
  </head>
  <body>
<?php
//连接数据库
$con = mysqli_connect('localhost','root','Afk120120.');
//选择数据库
$db = mysqli_select_db($con,'center');
if (!$db) {
        die('指定数据库打开失败:'.mysqli_error($con));
}
//设计数据库字符集
mysqli_set_charset($con,"utf8");

if(isset($_GET['id']))
{
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysqli_query($con,$sql);
$row = mysqli_fetch_array($result);
    if($row)
    {
      echo "<font size='5' color= 'black'>";
      echo '你的用户名:'. $row['username'];
      echo "<br>";
      echo '你的密码:' .$row['password'];
      echo "</font>";
      }
    else
    {
    echo '<font color= "black">';
    print_r(mysqli_error());
    echo "</font>";
    }
}
    else { echo "请输入ID作为参数值!";}
?>
  </body>
</html>

1564734038863.png

1564734217426.png

到此,完成一个简单数据交互的PHP页面!

0x03 安全加固

因为以前没有系统学过安全加固的方法,但是按照个人理解,LNMP环境的加固应该是把每个单独的应用做单独的安全配置,从而加固整个LNMP集成环境。

1.PHP加固

php的配置文件可以通过phpinfo();显示的位置查到,这里存在于/etc/php.ini。

参考了文章LNMP PHP环境下的安全加固设置经验

主要从以下几个方向做安全配置:

禁用不安全的PHP函数

disable_functions = passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server,fsocket,phpinfo

通常我们禁止php执行以上函数,是因为以上php函数均可执行系统命令,如ping、net、netstat等命令。如果攻击者从前端通过这些函数执行提权、cat /etc/shadow 等操作,啧啧,后果可以想象。

关闭PHP错误日志

display_errors = Off

这个也重要,避免报错暴露物理路径造成的文件上传和SQL报错注入、直接写shell等,避免提供给攻击者有用的信息。

开启HttpOnly

session.cookie_httponly = 1

避免攻击者利用XSS漏洞通过document对象盗用cookie登录凭证。

关闭PHP信息

expose_php = Off

避免暴露PHP版本的一些相关信息。

限制跨目录访问

open_basedir = ./:/tmp:/home/www/

限制用户只能访问配置的目录,而不能访问范围以外的目录。但是这个配置会对PHP操作io流的速度产生影响。

定义上传目录

upload_tmp_dir = /tmp

限制上传文件到指定的目录,可以避免因为权限分配造成的文件上传目录穿越。

以上是比较实用的PHP安全配置,还有一些其他的配置,可以再多做了解,视情况而定做加固操作。

2.MySQL加固

MySQL安全配置除了前文所说通过执行MySQL的安全配置命令进行的一系列安全操作:

mysql_secure_installation

还有一些需要注意的加固方案:

MySQL服务器权限控制

通常在实际操作的过程中,我们不给予最大的权限去操作数据库,而是额外建立一个普通用户,赋予用户操作单个数据库的权限,这样,尽可能避免同时有多个数据库时单个数据库信息的泄露影响到其他的数据库和服务器。

认证授权

仅允许root账号从本地连接数据库,不允许其通过外部网络连接数据库。

mysql> grant all privileges on *.* to 'root' @localhost identified by 'password'with grant option;
mysql> flush priveleges;

删除空口令账号和匿名账号。

`mysql>USE mysql;`

禁止使用MySQL命令行 历史记录

在Linux系统,执行以下命令可以查看是否存在mysql的历史命令记录文件:

find / -name ".mysql_history"

如果存在,则需要进行如下加固:

(1)删除.mysql_history文件;

(2)设置环境变量MYSQL_HISTFILE为/dev/null,并添加到shell的初始化脚本中,创建mysql_history到/dev/null的链接:

ln -s /dev/null $HOME/.mysql_history 

3.Nginx加固

了解了下,Nginx发展的历史,似乎Nginx本身出现的安全漏洞比较少,当然除了解析漏洞。所以Nginx的安全加固侧重于对Web应用的加固,起到WAF的作用。

Nginx加固暂时想到的是代替一部分WAF的作用,那么最常用的就是上传位置关闭PHP解析和正则过滤SQL注入的关键词了,暂时想到这两种。参考这位大佬的博文有关SQL注入的加固手法,很详细,nginx中防止SQL注入规则。具体实现通过修改Nginx的配置文件conf来完成:

过滤SQL关键词

if ($query_string ~* ".*('|--|union|insert|drop|truncate|update|from|grant|exec|where|[select](http://www.111cn.net/tags.php/select/)|and|or|count|chr|mid|like|[iframe](http://www.111cn.net/tags.php/iframe/)|script|alert|webscan|dbap[ps](http://www.111cn.net/fw/photo.html)ecurity|style|confirm|innerhtml|innertext|class).*")
{ return 500; }
if ($uri ~* .*(viewsource.jsp)$) { return 404; }
if ($uri ~* .*(/~).*) { return 404; }

过滤头

if ($http_user_agent ~ ApacheBench|WebBench|Jmeter|JoeDog|Havij|GetRight|TurnitinBot|GrabNet|masscan|mail2000|github|wget|curl) { return 444; }
if ($http_user_agent ~ "Go-Ahead-Got-It") { return 444; }
if ($http_user_agent ~ "GetWeb!") { return 444; }
if ($http_user_agent ~ "Go!Zilla") { return 444; }
if ($http_user_agent ~ "Download Demon") { return 444; }
if ($http_user_agent ~ "Indy Library") { return 444; }
if ($http_user_agent ~ "libwww-perl") { return 444; }
if ($http_user_agent ~ "Nmap Scripting Engine") { return 444; }
if ($http_user_agent ~ "~17ce.com") { return 444; }
if ($http_user_agent ~ "WebBench*") { return 444; }
if ($http_referer ~* 17ce.com) { return 444; }
if ($http_referer ~* WebBench*") { return 444; }

关闭文件上传目录的PHP解析

location ~* ^/(attachments|data)/.*\.(php|php5)${
    deny all;
}

4.CentOS加固

系统加固目前了解的还不够深入,但是上个月打ISCC线下私地是Linux系统,在通过拿到WebShell之后赛方给了连接的SSH密码,权限不是很够但是在低权限情况下做的加固手段这里可以写一下:

删除普通用户:

cat /etc/passwd
cat /etc/shadow
userdel

查看端口占用情况关闭不必要的进程:

netstat-antup   # 查看到对外的端口开放情况,只留下题目上所留的三个端口,其它的都
kill -s 9  PID  # 杀掉进程

但是在CentOS 7引入了firewall作为默认防火墙。前文安装Nginx顺道提了一下关于端口查看和防火墙生效。这里再做下记录:

firewall-cmd --zone=public --add-port=3306/tcp --permanent # 打开端口3306
firewall-cmd --remove-port=3306/udp --permanent # 关闭端口3306
systemctl restart firewalld.service # 重启防火墙
systemctl stop firewalld.service # 启动防火墙
netstat -lntp # 查看端口
netstat -lnp|grep 8080 # 查看端口8080被哪个进程占用

禁止su提权。通常情况下普通用户通过执行su -命令,输入正确的命令即可登录root账号对系统进行操作,但是加强安全性,可以单独建立一个管理组只允许该组用户可以执行su -来切换到root账号。非该组的用户即使执行su -输入正确密码也无法切换root账号。通常为wheel组。

  • 修改/etc/pam.d/su
vi /etc/pam.d/su                                         #   ← 打开这个配置文件
auth required /lib/security/$ISA/pam_wheel.so use_uid    #  ← 找到此行,去掉行首的“#”
  • 修改/etc/login.defs
echo “SU_WHEEL_ONLY yes” >> /etc/login.defs #  ← 添加语句到行末以上操作完成后,可以再建立一个新用户,然后用这个新建的用户测试会发现,没有加入到wheel组的用户,执行“su -”命令,即使输入了正确的root密码,也无法登录为root账号。

开启selinux。SELinux的全称是Security Enhanced Linux, 就是安全加强的Linux。在SELinux之前,root账号能够任意的访问所有文档和服务;如果某个文件设为777,那么任何用户都可以访问甚至删除;这种方式称为DAC(主动访问机制),很不安全。

Selinux的配置文件位置:/etc/selinux/config,它还有个链接在/etc/sysconfig/selinux

使用config文件来配置selinux,reboot重启生效。

vi  /etc/sysconfig/selinux
selinux=distabled  # 修改

最后就是打补丁,感觉安全人员需要关注实时资讯,关注漏洞披露,在公开的EXP出来之前打上披露漏洞的安全补丁。

0x04 总结

这是一次踩坑二十多次的环境搭建和安全加固总结,前期遇到了yum安装报错:There are no enabled repos,遇到了跟这篇博客大佬一样的问题CentOS 报错:There are no enabled repos;,解决了这个问题,又遇到国内换源的问题,要装一大堆依赖包,又踩坑了Nginx访问php页面变成下载的问题,发现配置文件中,内容的格式一定要对齐,不然的话,想不通问题就完了,很多人以为重装就能好,哈哈,会费很多时间然后继续在这个点卡壳,朋友跟我一块装的时候还碰到了PHP5.x后默认不支持mysql前缀函数而需要用mysqli前缀的函数,从PHP5开始,PHP5不在自动开启对mysql的支持,而是放到扩展函数库中。所以用户需要在拓展函数库中开启mysql函数库。这些支持都可以在php.ini配置文件中看到。平时都是直接拿来用,并没有仔细思考配置的问题。然后就是安全加固的配置,这次用的是CentOS7.6,版本比较新,系统上遇到的命令问题也比较多,firewall这个命令之前没有接触过,毕竟自己一个造轮子专业,专业技能不能跟网络专业的大佬比哈哈,也就AWD遇到需要做系统加固这块,至于PHP安全配置很早之前了解过,这次总结一下,Nginx的配置还需要继续踩坑。。。以后在遇到类似的问题尽可能的避免踩坑哈哈。以上,为期几天的环境搭建和加固,对于文中如果有错误的地方欢迎指正,互相交流!

2019.8.2日 20:45

原文作者:Keefe

原文链接:搭建LNMP环境及一些安全加固总结

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

Last Modified: January 5, 2020
Leave a Comment

3 Comments
  1. [...]在安全加固之前,可以先搭建模拟环境,同时也便于今后学习。这一点从Keefe_blog学来。[...]

  2. [...]在安全加固之前,可以先搭建模拟环境,同时也便于今后学习。这一点从Keefe_blog学来。[...]

  3. [...]在安全加固之前,可以先搭建模拟环境,同时也便于今后学习。这一点从Keefe_blog学来。[...]