|

wordpress最小迁移,docker部署与Security建议

前段时间本网站被黑了,网站目录下的php文件里被挂上了跳转到菠菜网站的代码,被云服务商的客服打电话要求赶紧修改。。。本想删掉恶意代码了事,结果一查吓一跳,这个网站已经毒得不成样子,无孔不入地被植入恶意脚本和代码了。

于是我也没有办法,只能迁移到新的docker容器里了。然而部署过程中又发现了许多问题,那么我应该写篇文章记录下来。

那么想要迁移wp该怎么做呢?那些个迁移插件我觉得就别想着用了,首先是安全问题:你如果是像我一样被黑了所以要迁移,那么只应该保留最低限度的网站内容,也就是用户信息和文章信息等。像插件和主题那些东西极大概率已经被注入了恶意代码,只能是先把新网站搭建好了再去手动安装主题和插件。其次是收费问题,md,这些迁移插件都是同样一副嘴脸——刚开始免费用,后面把大部分功能变成收费。不交钱就别想安心迁移,真是坏透了!

WordPress最小迁移需要考虑哪些文件?

官方最推荐的最小迁移,要保留的只有三部分内容:

  • 数据库中的数据:存储了用户信息,文章内容,网站设置等数据
  • wp-content/uploads目录:主要存储了文章中的图片
  • wp-config.php配置文件

前两者显然容易被篡改,但是wp-config.php最好也别完全信任,应该自己重新写或者至少应该仔细check有没有被加料。

1. 导出DB的内容,以mysql为例:

先导出数据库,针对旧的DB执行:

mysqldump -u<user> -h<host> --port <port> -p<password> --single-transaction <db_name> > wordpress.sql

2. 创建新的docker容器(wordpress和mysql)

这里使用docker-compose.yml把wordpress和mysql创建在同一个网络里,这样它们的通信也能稍微快一些。

version: "3.8"

services:
  mysql:
    image: mysql:8.0
    container_name: wordpress_mysql
    restart: unless-stopped
    ports:
      - "127.0.0.1:<port>:3306"  # 建议指定127.0.0.1,外部无法访问比较安全
    environment:
      MYSQL_ROOT_PASSWORD: <root_password>
      MYSQL_DATABASE: wordpressdb
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: <password>
    volumes:
      - <host machine path>:/var/lib/mysql
    networks:
      - wp_net

  wordpress:
    image: wordpress:6.9-php8.3-apache
    container_name: wordpress_server
    restart: unless-stopped
    depends_on:
      - mysql
    ports:
      - "<port>:80"
    environment:
      WORDPRESS_DB_HOST: wordpress_mysql:3306
      WORDPRESS_DB_NAME: wordpressdb
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: <password>
    volumes:
      - <host machine path>:/var/www/html
    networks:
      - wp_net

networks:
  wp_net:
    driver: bridge

然后执行以下命令来启动容器:

docker compose up -d

成功启动后连接上新的mysql服务器,把之前导出的数据导进去:

SOURCE /path_To_SQL_File/wordpress.sql

3. wp-config.php配置文件:

针对一个全新的wp-config.php配置文件,在写完自己的数据库相关配置之后仅需要添加下面的内容就可以了,但请先检查它们是否已经存在于文件中。

// 如果你的环境是通过容器运行wordpress和apache,然后在宿主机使用nginx等代理来配置HTTPS,需要以下配置
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false) {
	$_SERVER['HTTPS'] = 'on';
}

// 下面两行用于禁止后台编辑php文件,防止黑客入侵
define('DISALLOW_FILE_EDIT', true);   // not allow edit php in backend
define('DISALLOW_FILE_MODS', false);  // allow install/update/delete plugins and themes

// 下面两行用于启用自动更新,但只更新小版本,大版本不会自动更新。但如果已经对wp-admin和wp-includes锁权限了的话下面的内容是无用的,会更新失败。
define('AUTOMATIC_UPDATER_DISABLED', false); // allow auto update
define('WP_AUTO_UPDATE_CORE', 'minor'); // but only small versions

// 下面一行用于让wordpress不需要FTP即可安装插件和主题
define('FS_METHOD', 'direct'); // to avoid FTP requirement on the webpage

// 下面几行用于启用debug,启用后可以在浏览器控制台和一些日志文件里看到信息
//define('WP_DEBUG', true);
//define('WP_DEBUG_LOG', true);
//define('WP_DEBUG_DISPLAY', true);
//@ini_set('display_errors', 1);


/* That's all, stop editing! Happy publishing. */

4. 拷贝wp-contents/uploads目录

接下来我们把旧网站上的uploads目录复制到新网站,在复制之前强烈建议在uploads目录下执行一次下面的命令,检查一下是否有恶意脚本存在。这里是90%的黑客攻击切入点。

find ./ -type f ! -iname "*.jpg" ! -iname "*.png"

按理说这个目录下只应该存在图片文件,其它一切文件都可能是有问题的,建议删除。

宿主机代理设置

配置完以上内容,我们就能够用HTTP协议访问容器的端口来打开页面了。但如果需要配置域名和https,我们还需要在宿主机上配置nginx或类似的代理工具。

以下是我的Nginx配置,仅供参考:

/etc/nginx/nginx.conf

http {

    upstream tempWordpress {
        server 127.0.0.1:<your container port>;
    }
    ......

/etc/nginx/sites-enable/wordpress

server {

    listen 443 ssl http2;
    server_name <your domain name>;
    
    ssl on;
	ssl_certificate <your certificate> 
	ssl_certificate_key <your certificate key>
    
    # Security, not allow run any script in uploads/ and languages/
    location ~* ^/wp-content/(uploads|languages|cache)/.*\.(php|phtml|phar|pl|py|cgi|sh)$ {
        deny all;
    }

    location / {
        proxy_pass http://tempWordpress/;   # the upstream name you wrote in nginx.conf
        proxy_redirect  off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
	
	proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Forwarded-Port 443;
        proxy_redirect off;

    }


    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    gzip on;
    gzip_min_length 5k;
    gzip_buffers 4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 3;
    gzip_types text/plain application/json application/javascript text/css application/xml text/javascript image/jpeg image/gif image/png;
    gzip_vary on;
}

server {
    listen 80;
    server_name <your domain name>;
    return 301 https://$host$request_uri;
}

至此,如果顺利的话你已经可以通过自己设置的域名,以https协议成功访问了。

针对安全性的建议 – 数据库

数据库也十分有可能被注入恶意脚本,或者被创建了用户,我们可以通过以下命令去筛查:

-- 搜索恶意脚本:
-- script
SELECT option_name FROM wp_options
WHERE option_value LIKE '%<script%';
-- base64
SELECT option_name FROM wp_options
WHERE option_value LIKE '%base64_decode%';
-- eval
SELECT option_name FROM wp_options
WHERE option_value LIKE '%eval(%';

-- 搜索文章中的链接:
SELECT ID, post_title
FROM wp_posts
WHERE post_content LIKE '%display:none%'
   OR post_content LIKE '%visibility:hidden%';

-- 搜索可疑的外链域名:
SELECT ID, post_title
FROM wp_posts
WHERE post_content LIKE '%http%'
  AND post_content NOT LIKE '%你的域名%';

-- 检查active_plugins,看看有没有自己不认识的插件(高危):
SELECT option_value
FROM wp_options
WHERE option_name = 'active_plugins';

-- 检查是否有不认识的用户,特别是管理员用户(高危):
SELECT ID, user_login, user_email
FROM wp_users;

针对安全性的建议 – 权限与更新(重要)

为了保证尽可能不受攻击,同时保证可用性,作为一个原则,我们应该做到的是:在不需要对目标进行写操作时,要用权限锁死目标。这里的目标主要包含wordpress核心和主题与插件。

下面是需要注意的目录平时应当保持的权限:

wp-admin	root:www-data	755   // 核心,仅在wordpress更新时暂时修改为www-data:www-data
wp-includes	root:www-data	755   // 核心,仅在wordpress更新时暂时修改为www-data:www-data
wp-content/uploads	www-data:www-data   755 // 用于上传图片,可以一直保持权限
wp-content/cache	www-data:www-data   755 // 一些插件需要使用缓存,可以一直保持权限
wp-content/languages	root:www-data	775 // 可以一直保持权限,以支持语言包自动更新
wp-content/upgrade	root:www-data	755 //仅在wordpress更新时暂时修改为www-data:www-data
wp-content/plugins	root:www-data	755 // 仅在需要修改插件时暂时改为www-data:www-data
wp-content/themes	root:www-data	755 // 仅在需要修改主题时暂时改为www-data:www-data
wp-content/index.php	root:www-data	644 // 可以一直保持权限

上面没提到的目标全都可以锁死,设置为被root:www-data拥有,目录权限755,文件权限644。

通过上述权限大家不难发现,平时最危险的是uploads目录,cache目录和languages目录,所以我们可以在宿主机Nginx上配置不允许访问这里的脚本,前文虽然也展示了整个Nginx配置,但是这里再强调一下:

    location ~* ^/wp-content/(uploads|languages|cache)/.*\.(php|phtml|phar|pl|py|cgi|sh)$ {
        deny all;
    }

为了方便,我制作了以下脚本用来切换权限模式(wordpress更新模式/插件和主题更新模式/平时严格模式)。为防止被黑客执行,建议把以下三个脚本的权限设置为 root:root + 700

wp_update_mode.sh :

#!/bin/bash
cd /var/www/html

chown -R www-data:www-data wp-admin
chown -R www-data:www-data wp-includes
chown -R www-data:www-data wp-content/upgrade

echo 'in wp update mode'

plugin_theme_update_mode.sh :

#!/bin/bash
cd /var/www/html

chown -R www-data:www-data wp-content/plugins
chown -R www-data:www-data wp-content/themes

echo "in plugin/theme update mode"

strict_mode.sh :

#!/bin/bash

cd /var/www/html

# 核心目录
chown -R root:www-data wp-admin
chmod 755 wp-admin

chown -R root:www-data wp-includes
chmod 755 wp-includes

# 上传、缓存、语言包、index.php 保持原权限
chown -R www-data:www-data wp-content/uploads
chmod 755 wp-content/uploads

chown -R www-data:www-data wp-content/cache
chmod 755 wp-content/cache

chown -R root:www-data wp-content/languages
chmod 775 wp-content/languages

chown -R root:www-data wp-content/index.php
chmod 644 wp-content/index.php

# 插件和主题保持 root:www-data
chown -R root:www-data wp-content/plugins
chmod 755 wp-content/plugins

chown -R root:www-data wp-content/themes
chmod 755 wp-content/themes

# upgrade 保持 root:www-data
chown -R root:www-data wp-content/upgrade
chmod 755 wp-content/upgrade

echo "in strict mode"

此外,一些Debug经验

由于最小迁移时不会带插件和主题,如果原网站用了默认以外的主题,可能会出现访问时页面白屏,且浏览器控制台和服务器中都不显示任何报错。此时不要单纯使用域名访问,可以尝试访问域名加/wp-admin/install.php,或者/wp-admin/login.php。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注