上一篇 下一篇 分享链接 返回 返回顶部

香港云服务器配置图片防盗链时,端口、权限与日志审计如何设置?

发布人:Minchunlin 发布时间:2026-09-30 13:18 阅读量:2
香港云服务器配置图片防盗链时,端口、权限与日志审计如何设置?

先确定目标状态与前置条件

要解决“图片防盗链功能在香港云服务器怎么配置?”这个问题,不能只添加一条 Referer 规则。较稳妥的目标状态应同时满足:

  • 公网仅开放图片站点需要的 80、443 端口,远程管理端口只允许管理来源地址访问。
  • Nginx 工作进程对图片目录只有读取和进入目录的权限,没有删除、修改和上传权限。
  • 图片请求按允许的站点域名判断来源,外部页面引用时返回 403。
  • 图片访问单独记录日志,能够区分正常访问、防盗链拦截、文件不存在和服务端配置错误。
  • Nginx、系统组件和加密组件保持在组织允许的补丁范围内。
  • 所有防火墙、权限和配置变更都有备份,并且可以通过控制台或现有管理连接回滚。

以下步骤以“Linux 云服务器 + Nginx + 本地静态图片目录”为例。开始前准备好以下信息:

  • 图片访问域名,例如 img.example.com。
  • 网站允许引用图片的域名,例如 example.com、www.example.com。
  • 图片实际目录,例如 /srv/www/example.com/public/images。
  • 云平台实例的安全组或网络访问控制权限。
  • 具备 sudo 权限的管理账号,以及可用的控制台登录方式。
  • 一张已经存在的测试图片,例如 /images/test.jpg。
  • 变更前的 Nginx 配置、权限清单和防火墙规则备份。

如果图片并非由本机 Nginx 直接读取,而是由应用接口、对象存储或其他服务返回,应将防盗链规则放到实际处理图片请求的那一层,不能直接套用本地文件目录配置。

第一步:确认系统、Nginx 和监听端口

先确认发行版、Nginx 服务状态、工作进程账号和当前监听端口。不要在未确认服务名和配置路径的情况下直接修改文件。

cat /etc/os-release

nginx -v
sudo systemctl status nginx --no-pager

ps -eo user,group,pid,ppid,comm,args | grep '[n]ginx'
sudo nginx -T 2>/dev/null | grep -E '^[[:space:]]*user[[:space:]]'
sudo ss -lntup

重点确认以下结果:

  • Nginx 主进程通常由 root 启动,工作进程使用配置中的普通账号,例如 www-data 或 nginx。
  • 80、443 应由 Nginx 监听;如果网站使用其他端口,先确认该端口确有业务用途。
  • 数据库、缓存、内部应用端口不应直接暴露在公网监听地址上。对于只供本机访问的服务,优先监听 127.0.0.1 或受控内网地址。
  • 若 nginx -T 失败,先处理现有配置问题,不要继续叠加防盗链配置。

变更前创建配置备份。备份文件可能包含域名、路径和密钥,保存后应限制读取权限。

sudo sh -c 'umask 077; nginx -T > /root/nginx-before.txt'
sudo cp -a /etc/nginx/nginx.conf /etc/nginx/nginx.conf.before-hotlink

如果使用独立配置文件,还要单独备份该文件。例如:

sudo cp -a /etc/nginx/conf.d/site.conf \
  /etc/nginx/conf.d/site.conf.before-hotlink

第二步:收紧云端和系统防火墙端口

端口控制需要同时检查云平台安全组和服务器本机防火墙。云端规则未放行时,本机即使监听正常,公网也无法访问;本机防火墙过度放行时,云端规则也不能替代主机侧的最小暴露原则。

建议先形成如下端口边界:

用途常见端口访问范围
HTTP 网站访问80/TCP按业务需要对公网开放
HTTPS 网站访问443/TCP按业务需要对公网开放
远程管理实际 SSH 端口仅允许管理出口地址或管理网络
数据库、缓存、内部应用实际业务端口不对公网开放,按内网或本机范围限制

先检查当前规则,确认正在使用的是哪一种防火墙。不要同时启用多套防火墙管理工具。

sudo ss -lntup

sudo ufw status verbose 2>/dev/null || true
sudo firewall-cmd --state 2>/dev/null || true
sudo nft list ruleset 2>/dev/null | head -n 120

Ubuntu 或 Debian 使用 UFW 时

将 ADMIN_IP 替换为实际管理出口地址。执行前保留当前 SSH 会话,并准备第二个会话或云控制台,避免规则错误导致失联。

sudo ufw status numbered | sudo tee /root/ufw-before-hotlink.txt

sudo ufw allow from ADMIN_IP to any port 22 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw --force enable

sudo ufw status verbose

如果 SSH 使用的不是 22,必须替换为实际端口。default deny incoming 会影响所有没有明确允许的入站连接,包括临时的运维端口,因此要先核对现有业务。

RHEL 系列使用 firewalld 时

先确认活动区域,再添加网站服务规则。不要直接假定区域名称。

sudo firewall-cmd --get-active-zones
sudo firewall-cmd --list-all

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

sudo firewall-cmd --list-all

管理端口应根据现有规则限制来源。不要在未确认当前 SSH 连接已匹配新规则前,删除原有管理访问规则。

防火墙调整完成后,从外部网络测试网站端口;同时在服务器上确认端口仍由正确服务监听:

sudo ss -lntp | grep -E ':(80|443)\b'
curl -I http://127.0.0.1/

若本机 curl 正常、外部访问失败,优先检查云平台安全组、实例网络访问控制和域名解析;若本机也失败,再检查 Nginx 服务和主机防火墙。

第三步:确认管理认证和补丁状态

图片防盗链规则只控制图片请求来源,不能替代服务器管理认证。远程管理账号应使用密钥或组织批准的强认证方式,禁止多人共用账号,并限制不必要的管理员权限。

先查看 SSH 的生效配置:

sudo sshd -T | grep -Ei \
'port|permitrootlogin|passwordauthentication|pubkeyauthentication'

只有在已经通过密钥登录、第二个会话验证成功,并且保留云控制台入口的情况下,才考虑收紧 SSH 配置。修改前备份文件,修改后先校验,再重新加载,不能直接重启验证。

sudo cp -a /etc/ssh/sshd_config \
  /etc/ssh/sshd_config.before-hardening

sudoedit /etc/ssh/sshd_config

可根据实际认证策略检查或设置以下方向:

PermitRootLogin prohibit-password
PubkeyAuthentication yes
PasswordAuthentication no

如果服务器仍依赖密码登录、没有可用密钥或存在自动化任务依赖密码认证,不要直接关闭密码认证。修改完成后执行:

sudo sshd -t
sudo systemctl reload sshd 2>/dev/null || sudo systemctl reload ssh

补丁操作同样应先备份配置,并在维护窗口执行。不要根据教程中的版本号判断是否最新,而应以当前系统仓库和组织补丁策略为准。

Ubuntu 或 Debian 可以先查看待更新内容:

sudo apt-get update
apt list --upgradable

RHEL 系列可以执行:

sudo dnf check-update
sudo dnf updateinfo list updates

确认影响范围后再安装更新。Nginx、OpenSSL、系统内核和 SSH 组件属于应重点关注的范围。内核更新可能需要重启,重启前应确认云控制台可用、业务已安排维护窗口,并保留变更前配置。

第四步:按最小权限保护图片目录

防盗链配置只能阻止部分外部引用,无法弥补文件目录权限过宽的问题。图片目录的基本原则是:

  • Nginx 工作账号可以进入目录并读取图片。
  • Nginx 工作账号不能写入、删除或修改已发布的图片。
  • 发布账号与运行账号分离。
  • 图片目录不放置脚本、配置文件和密钥。
  • 不使用 777,也不要为了“方便访问”让工作账号拥有目录所有权。

先确认图片目录及其父目录的权限:

IMG_ROOT=/srv/www/example.com/public/images

sudo namei -l "$IMG_ROOT"
sudo stat -c '%A %U:%G %a %n' \
  /srv /srv/www /srv/www/example.com \
  /srv/www/example.com/public "$IMG_ROOT"

sudo find "$IMG_ROOT" -maxdepth 2 -type f -printf '%M %u:%g %p\n' | head

确认 Nginx 工作账号后,再判断是否需要调整。下面以工作组为 www-data 的情况示例;如果实际账号是 nginx,应替换为已核实的账号和组。

WEB_GROUP=www-data
IMG_ROOT=/srv/www/example.com/public/images

id "$WEB_GROUP"

如果当前目录已经由 root 或发布账号拥有,且 Nginx 具备读取权限,不必为了统一格式进行递归修改。确实需要调整时,先保存权限清单,再分别处理目录和文件,避免使用没有边界的 chmod -R 777。

sudo find "$IMG_ROOT" -xdev -printf '%m %u %g %p\n' \
  | sudo tee /root/images-permissions-before.txt

sudo find "$IMG_ROOT" -xdev -type d \
  -exec chown root:"$WEB_GROUP" {} +

sudo find "$IMG_ROOT" -xdev -type f \
  -exec chown root:"$WEB_GROUP" {} +

sudo find "$IMG_ROOT" -xdev -type d -exec chmod 750 {} +
sudo find "$IMG_ROOT" -xdev -type f -exec chmod 640 {} +

750 和 640 适用于确认 Nginx 工作账号属于 WEB_GROUP 的场景。如果发布程序需要写入图片,应让发布程序写入临时目录,再由受控发布流程复制到只读目录;不要直接给 Nginx 工作账号增加写权限。

调整后验证:

sudo -u www-data test -r "$IMG_ROOT/test.jpg" && echo "readable"
sudo -u www-data test -w "$IMG_ROOT/test.jpg" && echo "writable" || echo "not-writable"

如果实际工作账号不是 www-data,把命令中的账号替换为 ps 和 Nginx 配置中确认的工作账号。not-writable 通常是图片发布目录应有的结果。

第五步:配置 Referer 防盗链

允许指定网站引用图片

以下配置适用于图片由本机 Nginx 直接提供,且站点根目录已经在 server 块中配置。把域名和图片扩展名替换为实际值。

log_format 必须放在 http 上下文中,不能放在 server 或 location 中:

log_format image_hotlink
    '$remote_addr [$time_iso8601] '
    '"$request_method $uri" '
    'status=$status bytes=$body_bytes_sent '
    'referer="$http_referer" '
    'ua="$http_user_agent" '
    'request_time=$request_time';

在实际处理图片的 server 块中增加规则:

location ~* \.(?:jpg|jpeg|png|gif|webp|svg|avif)$ {
    valid_referers none blocked example.com www.example.com *.example.com;

    if ($invalid_referer) {
        return 403;
    }

    try_files $uri =404;

    access_log /var/log/nginx/image_access.log image_hotlink;
}

配置含义如下:

  • example.com 和 www.example.com 是允许引用图片的域名,应只保留真实业务域名。
  • *.example.com 允许其子域名引用。如果并非所有子域名都可信,应改为逐个列出。
  • none 允许没有 Referer 的请求,适合需要兼容地址栏访问、隐私策略或部分客户端访问的公开图片。
  • blocked 允许存在但被浏览器隐藏协议部分的来源。若业务不需要兼容此类请求,可以删除。
  • $invalid_referer 为真时返回 403,但这不是用户身份认证。
  • try_files $uri =404 确保不存在的图片返回 404,避免把“文件不存在”误判为防盗链命中。

如果图片只存放在 /images/ 路径,更建议把规则合并到现有的图片路径配置中,避免影响站点其他静态资源:

location ^~ /images/ {
    valid_referers none blocked example.com www.example.com *.example.com;

    if ($invalid_referer) {
        return 403;
    }

    try_files $uri =404;
    access_log /var/log/nginx/image_access.log image_hotlink;
}

同一个请求只能命中实际生效的 location。如果已有正则、前缀或特殊静态资源配置,不要盲目新增第二个 location,先用下面的命令查看最终合并结果:

sudo nginx -T > /tmp/nginx-effective.txt
grep -n -A25 -B5 'location.*images\|location.*jpg\|image_access.log' \
  /tmp/nginx-effective.txt

需要更严格访问控制时使用签名地址

Referer 可以被客户端伪造,因此它适合控制普通网页的外部引用,不适合保护私有图片、付费内容或必须确认访问权限的资源。

如果图片必须经过用户授权,应由业务系统生成带有效期的签名地址,或者确认 Nginx 已编译 secure_link 模块后再配置。先检查模块:

nginx -V 2>&1 | grep -- '--with-http_secure_link_module'

确认模块存在后,可以在图片 location 中按业务约定配置:

secure_link $arg_md5,$arg_expires;
secure_link_md5 "$secure_link_expires$uri REPLACE_WITH_SERVER_SECRET";

if ($secure_link = "") {
    return 403;
}

if ($secure_link = "0") {
    return 410;
}

应用端必须按照同一规则生成签名、过期时间和 URL 安全编码。REPLACE_WITH_SERVER_SECRET 不能提交到前端代码仓库,也不能写入公开日志。当前端或应用无法生成匹配签名时,不要启用这段配置,否则所有图片请求都会被拒绝。

第六步:启用日志审计并保护日志文件

图片日志至少应记录访问来源、请求路径、状态码、返回大小、用户代理和请求耗时。示例中的 $uri 不包含查询字符串,可避免把签名参数直接写入访问日志;如果确实需要记录查询参数,应先评估其中是否含有令牌或个人信息。

确认日志目录不会被网站静态访问:

sudo stat -c '%A %U:%G %n' /var/log/nginx
sudo ls -l /var/log/nginx/

日志配置生效后,重点观察:

  • 200:图片被正常返回。
  • 403:来源不在允许列表,或签名认证失败。
  • 404:URL 存在但文件不存在,或者 root 与实际目录不一致。
  • 5xx:Nginx、上游服务或文件处理链路存在异常,不能简单归为防盗链拦截。

验证日志是否有记录:

sudo tail -f /var/log/nginx/image_access.log

统计近期拦截和不存在的请求:

sudo grep 'status=403' /var/log/nginx/image_access.log | tail -n 20
sudo grep 'status=404' /var/log/nginx/image_access.log | tail -n 20

检查系统是否已经为 Nginx 配置日志轮转:

sudo grep -R "nginx\|image_access.log" \
  /etc/logrotate.conf /etc/logrotate.d 2>/dev/null

日志轮转策略应根据审计要求、磁盘空间和隐私规定确定。日志不应放在网站可下载目录中,也不应让普通网站账号任意读取。若日志包含来源地址和用户代理,应按组织的数据保留和访问控制要求管理。

第七步:测试配置并完成上线验证

修改完成后,先测试语法,再平滑加载。nginx -t 未通过时不要执行 reload。

sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status nginx --no-pager

使用真实域名和测试图片验证。将示例域名替换为实际域名:

IMAGE_URL='https://img.example.com/images/test.jpg'

curl -I -e 'https://www.example.com/article/1' "$IMAGE_URL"
curl -I -e 'https://other.example.net/page' "$IMAGE_URL"
curl -I "$IMAGE_URL"

结果判断:

  • 允许域名带 Referer 请求应返回 200,并且响应内容类型应为图片类型。
  • 外部域名请求应返回 403。
  • 在配置包含 none 时,不带 Referer 的请求通常会被允许;删除 none blocked 后,同类请求应被拦截。
  • 图片不存在时应返回 404,这说明请求已到达静态文件处理规则,但路径没有对应文件。
  • 如果所有请求都是 403,优先检查域名列表、Referer 是否带端口、严格模式是否误伤,以及请求是否命中了另一条 location。
  • 如果允许域名请求是 404,检查 root、alias、文件名大小写和目录权限。
  • 如果外部访问超时,先按“监听端口—主机防火墙—云端安全组—域名解析”的顺序排查。

还可以使用状态码和响应大小进行快速检查:

curl -sS -o /dev/null \
  -w 'status=%{http_code} type=%{content_type} size=%{size_download}\n' \
  -e 'https://www.example.com/article/1' \
  "$IMAGE_URL"

常见失败处理与回滚

Nginx 提示配置语法错误

常见原因包括:

  • log_format 放进了 server 或 location。
  • valid_referers 拼写错误或配置上下文不正确。
  • 当前 Nginx 未包含 secure_link 模块,却加入了相关指令。
  • 新增的 location 与已有配置冲突。

处理顺序:

sudo nginx -t
sudo nginx -T 2>&1 | less
sudo journalctl -u nginx -n 80 --no-pager

如果是独立文件导致错误,可以先临时移出配置目录,确认旧配置恢复:

sudo mv /etc/nginx/conf.d/image-hotlink.conf \
  /etc/nginx/conf.d/image-hotlink.conf.disabled

sudo nginx -t
sudo systemctl reload nginx

所有图片都返回 403

先分别测试允许域名、外部域名和无 Referer 请求。若允许域名仍然是 403,检查:

  • valid_referers 中是否写入了实际页面域名。
  • 是否误把图片域名、管理域名或带端口的域名遗漏。
  • 浏览器或上游服务是否去除了 Referer。
  • 请求是否命中了另一条图片 location。
  • 是否同时启用了签名地址,但前端没有生成签名参数。

图片返回 404 或权限拒绝

确认 Nginx 工作账号能够沿父目录逐级进入,并读取目标文件:

sudo -u www-data namei -l /srv/www/example.com/public/images/test.jpg
sudo -u www-data test -r \
  /srv/www/example.com/public/images/test.jpg && echo readable

如果实际账号不是 www-data,替换为已经确认的工作账号。不要直接把目录改成 777,应修正 root、alias、文件路径或组读取权限。

日志没有记录图片请求

检查当前请求是否进入了预期的 server 和 location:

sudo nginx -T | grep -n -A20 -B5 'image_access.log'
sudo tail -n 50 /var/log/nginx/error.log

如果访问图片时日志为空,常见原因是:

  • 请求使用了另一个 server_name。
  • 更高优先级的 location 先处理了请求。
  • access_log off 在上层或当前块中生效。
  • 日志文件路径、权限或轮转配置有误。

变更后需要完整回滚

Nginx 配置回滚应优先恢复变更前文件,而不是临时删除某一条指令:

sudo cp -a /etc/nginx/nginx.conf.before-hotlink \
  /etc/nginx/nginx.conf

sudo nginx -t
sudo systemctl reload nginx

如果只修改了站点配置,则恢复对应的 site.conf.before-hotlink。防火墙回滚应依据 /root/ufw-before-hotlink.txt 或变更前的 firewalld 记录逐条恢复,不要在不了解原规则的情况下执行全量清空。

权限回滚应使用变更前的权限清单或发布系统基线。若使用了 getfacl 保存 ACL,应通过对应的 setfacl --restore 恢复;没有权限基线时,不要凭经验递归改回所有者和模式,因为这可能影响发布任务和其他静态资源。

上线验收清单

  • [ ] 云平台安全组仅开放业务所需端口,管理端口已限制来源。
  • [ ] 主机防火墙规则已备份,且没有关闭当前管理连接。
  • [ ] Nginx 工作账号已经确认,图片目录没有写权限。
  • [ ] 图片域名和允许引用域名已逐项核对。
  • [ ] nginx -t 通过,reload 后服务状态正常。
  • [ ] 允许域名请求返回预期成功状态。
  • [ ] 外部域名请求返回 403。
  • [ ] 不带 Referer 的请求行为符合公开图片或严格模式的业务要求。
  • [ ] 不存在的图片返回 404,没有被错误地当作防盗链拦截。
  • [ ] 图片访问日志能够记录成功、拦截和不存在请求。
  • [ ] 日志不在网站可访问目录,读取权限符合审计要求。
  • [ ] Nginx、SSH、系统和加密组件已按维护窗口完成补丁核查。
  • [ ] 已保留 Nginx 配置、防火墙规则和目录权限的回滚材料。
目录结构
全文