安装Apache

1
apt-get install apache2

在浏览器中访问127.0.0.1,出现如下页面则代表安装成功

安装MySQL

安装MySQL服务端

1
apt-get install mysql-server

安装MySQL客户端

1
apt-get install mysql-client

以root用户身份登录MySQL

1
mysql -u root

为root用户设置密码’123456’

1
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';

退出MySQL

1
2
mysql> quit
Bye

后续再登录MySQL需使用刚才设置的密码

1
2
mysql -u root -p
Enter password:

安装PHP

1
apt-get install php

如此会安装最新版本的php

/var/www/html 下新建一个phpinfo.php 文件

1
vim /var/www/html/phpinfo.php

编辑如下内容:

1
2
3
<?php
phpinfo();
?>

在浏览器中访问此文件,若能够看到PHP相关信息说明PHP安装成功且PHP代码能在此环境运行

到此,LAMP环境算是初步部署好了。下面,简要记录下Nginx的安装,PHP连接MySQL的操作,以及一些简单加固技术。

安装Nginx

Nginx与Apache都能提供Web服务,两者区别见 浅析Nginx与Apache的区别

有一点要说明的是,Apache对PHP支持比较简单,但Nginx本身不会对php文件进行解析,需要配合php-fpm(第三方的fastcgi进程管理器)才能实现对PHP的解析支持。

关于这一点详情可见 Nginx工作原理和优化总结

注意,如果Apache服务已经开启,在使用Nginx前需手动关闭Apache服务

1
service apache2 stop

安装Nginx

1
apt-get install nginx

如果之前没有安装过Apache,直接在浏览器中访问127.0.0.1,出现如下页面则代表nginx安装成功。若已安装过Apache,则访问127.0.0.1/index.nginx-debian.html

但此时还无法运行php代码,如无法访问phpinfo页面,还需安装php-fpm,注意根据php版本安装对应的php-fpm

1
apt-get install php8.0-fpm

以及修改nginx配置文件

1
vim /etc/nginx/sites-available/default

:wq,保存退出后重启nginx服务,使修改过的配置信息生效

1
service nginx restart

然后和上面一样,成功访问127.0.0.1/phpinfo.php则说明nginx已经能够支持PHP

PHP连接MySQL

方式:通过MySQLi扩展

安装mysqli扩展

1
apt-get install php8.0-mysqli

/var/www/html/目录下创建connect.php,编辑内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$dbhost = 'localhost'; // mysql服务器主机地址
$dbuser = 'root'; // mysql用户名
$dbpass = '123456'; // mysql用户对应的密码
$conn = mysqli_connect($dbhost, $dbuser, $dbpass);
if(! $conn )
{
die('Could not connect: ' . mysqli_error());
}
echo '数据库连接成功!'."<br>";
$sql="select user,host from mysql.user";
$result=mysqli_query($conn,$sql);
if (mysqli_num_rows($result) > 0) {
// 输出数据
while($row = mysqli_fetch_assoc($result)) {
echo $row["user"]."@".$row["host"]."<br>";
}
} else {
echo "0 结果";
}
mysqli_close($conn);
?>

访问127.0.0.1/connect.php,页面上即出现“数据库连接成功!”,并执行mysql语句,返回了查询的数据。

安全加固

加固Linux系统

远程登陆

在使用和管理服务器时,往往我们需要远程登陆服务器,这就需要我们保证远程登陆过程的安全性。以下方法可以一定程度提高远程登陆的安全性。

  1. 强制使用高强度用户密码(数字、字母、字符的组合且长度14位以上)
  2. 更改 SSH 默认的端口(22)为随机端口
  3. 禁止 root 身份的远程登陆
  4. 使用公钥认证机制进行远程登陆

现以 Ubuntu 系统为例,完成上述操作:

要强制用户使用高强度密码,需要安装额外的模块 libpam-cracklib

1
apt-get install libpam-cracklib

在 Ubuntu 中,密码策略(规定密码的长度,字符等)定义在 /etc/pam.d/common-password 文件中

如果要规定密码长度为 14,包含大小写字符数字和字符,可以在 pam_unix.so的前一行,添加:

1
password required pam_cracklib.so try_first_pass retry=3 minlen=14 lcredit=-1 ucredit=-1 dcredit=-1 ocredit=-1 difok=2 reject_username

上述配置的选项的描述如下,详情参考 libpam-cracklib 文档

选项 描述
retry=N 设置密码时的,最大重试次数
minlen=N 新密码的最小长度
lcredit=N 最少小写字母数,小于0,正常计算 minlen,大于0,计算 minlen 额外加 1
ucredt=N 最少大写字母数,小于0,正常计算 minlen,大于0,计算 minlen 额外加 1
dcredit=N 最少数字的数,小于0,正常计算 minlen,大于0,计算 minlen 额外加 1
ocredit=N 最少其它字符数,小于0,正常计算 minlen,大于0,计算 minlen 额外加 1
difok=N 和旧密码不同的字符数
reject_username 禁止用户名作为密码

更改ssh默认端口,禁止root身份登录

修改 ssh的配置文件 /etc/ssh/ssh_config

找到 # Port 22 把22更改为其他端口号

找到 #PermitRootLogin,更改为:

1
PermitRootLogin no

保存后重启ssh服务,下次远程登陆时则需要指定该端口进行登陆,而且无法使用 root 用户登陆

1
ssh username@ip-addr -p portnum

使用公钥认证机制进行远程登陆:

SSH 登陆 提供公钥认证机制的登陆,即不需要密码的登陆方式,但是需要在客户端生成公钥和私钥,并将公钥发送给服务端,服务端将公钥添加到相应用户的配置文件中。

首先客户端需要生成密钥对:

1
ssh-keygen -t rsa -b 4096

使用默认文件名,一路 Enter 到底,生成的密钥对文件在 ~/.ssh/ 目录下,其中 id_rsa 为私钥,id_rsa.pub 为公钥。

接着将公钥文件上传到服务器:

1
ssh-copy-id username@ip-addr -p portnum

该命令会把公钥文件的内容,写入到 /home/username/ssh/authorized_keys 文件中,也可以手动添加内容。如此一来就可以直接以 username 的身份,而不用使用密码登陆服务器了。

加固Apache

禁止访问外部文件

禁止 Apache 访问 Web 目录之外的任何文件,修改 httpd.conf 配置文件,其中 /web 为网站根目录。

1
2
Order Allow,Deny
Allow from /web

禁止目录列出

目录列出会导致明显信息泄露或下载,可修改 httpd.conf 配置文件:

将Options Indexes FollowSymLinks中的Indexes去掉,就可以禁止 Apache 显示该目录结构。Indexes的作用就是当该目录下没有 index.html 文件时,自动显示目录结构。

隐藏版本信息

隐藏 Apache 的版本号及其它敏感信息。修改 httpd.conf 配置文件:

1
ServerSignature Off ServerTokens Prod

限制HTTP请求方法

禁用 PUT、DELETE 等危险的 HTTP 方法。

修改 httpd.conf 配置文件,只允许 get、post 方法。

1
2
3
4
5
6
<Location />  
<LimitExcept GET POST CONNECT OPTIONS>
Order Allow,Deny
Deny from all
</LimitExcept>
</Location>

加固Nginx

隐藏版本信息

​ Nginx 默认开启 Server Token(显示版本号),这样使得 Nginx 的版本号很容易被获取,如下图为访问域名不存在资源时的返回页面,可以看到 Nginx 的版本号

​ 在配置文件 /etc/nginx/nginx.conf 中 http 块中添加,保存并重启nginx服务。

1
server_tokens off

​ 关闭后,访问域名下不存在的资源,返回页面中没有了 Nginx 的版本信息。

限制访问敏感资源

有些资源我们可能不想对外开放,比如一些版本控制的备份文件,如.git/.svn等,这些暴露了可能会将整个项目的结构或是源代码都泄露了,可通过如下设置限制对其的访问

1
2
3
location /test/ { 
deny all;
}

限制HTTP请求方法

​ 编辑配置文件,添加如下内容:

1
2
3
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 500;
}

​ 保存并重启nginx服务(只允许常用的GET和POST方法,顶多再加一个HEAD方法)

限制IP访问

​ 编辑配置文件,在server标签内添加如下内容:

1
2
3
4
5
6
location / {
deny 192.168.1.1; #拒绝IP
allow 192.168.1.0/24; #允许IP
allow 10.1.1.0/16; #允许IP
deny all; #拒绝其他所有IP
}

加固MySQL

运行 mysql_secure_installation 工具(安装 mysql 后自带的 shell 脚本),进行 mysql 的安全检查,

根据提示,设置密码为最高级别,并为 root 用户设置密码,最后同意以下选项:

  • Remove anonymous users? – 删除匿名用户
  • Disallow root login remotely? – 禁止远程使用 root 用户登陆
  • Remove test database and access to it? – 删除测试数据库和其访问权限
  • Reload privilege tables now? – 重载授权表

加固PHP

当文件不存在时停止 PHP 处理

Nginx 对于 PHP 的默认配置使得 PHP 解释器接受所有以 .php结尾的 URI,这样一来就会存在很大的风险,存在任意代码执行漏洞,具体解释见Passing Uncontrolled Requests to PHP

修改 /etc/php/7.4/fpm/php.ini文件(不同系统该文件位置略有不同),设置cgi.fix_pathinfo=0,可以禁止 PHP 解释器查找文件系统中不存在的文件。

禁用危险的 PHP 函数

当以下函数不使用时,可修改 php.ini 以禁用:

1
disable_functions =exec,eval,phpinfo,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source

限制文件上传功能

如果网站不需要文件上传功能,应该禁用,修改php.ini

1
file_uploads=Off

如果需要上传功能,则设置上传文件的大小,根据实际情况设置,如头像图片上传1M足矣,修改php.ini

1
2
file_uploads=On
upload_max_filesize=1M

设置 POST 方法传输数据的大小

POST 方法是当客户端需要向服务器发送数据时使用的,该方法可被用于对服务器进行 Dos 攻击等,所以需要将其能传输的数据大小设置成合理的数值,如果网站不需要上传文件等数据量大的操作,4KB也应该足够了。

1
post_max_size=1K

防止 PHP 版本信息泄露

1
expose_php = Off

限制 PHP 脚本的最长执行时间

1
2
3
4
# set in seconds
max_execution_time = 30 #最长执行时间 30 s
max_input_time = 30 #脚本解析输入最长时间30s
memory_limit = 40M #脚本最大使用内存40M

这样可以有效防止大规模的 DOS 攻击。

关闭错误信息提示

一般 PHP 环境在没有连接到数据库或者其他情况下会有错误提示信息,错误信息中可能包含 PHP 脚本当前的路径信息或者查询的 SQL 语句等信息,这类信息如果暴露给黑客是不安全的,因此建议禁止该错误提示:

1
display_errors = Off 

如果确实要显示错误信息,一定要设置显示错误信息的级别。例如,只显示警告以上的错误信息:

1
error_reporting = E_WARNING & E_ERROR