打印机安全加固实战:CUPS与RAW端口防护

打印机安全加固这件事,在内网安全检查里几乎每次都被排在最后:设备是行政买的、驱动是厂商装的、IP 是随手分的,没人给它打补丁,也没人改过默认口令。但一台暴露 9100 端口的网络打印机,等于给攻击者留了一个不需要凭据、可以写入任意文件、还能反向连出的落脚点。本文按可复现的顺序,把 Linux 打印服务器(CUPS)从”默认全开”收敛到”仅授权可用”。

适用场景

以下三类环境最需要做这套加固:一是办公网与业务网未做隔离,员工电脑能直连打印机管理页面;二是用 Linux 服务器做打印共享,CUPS 的 Web 管理界面(631 端口)被顺手暴露在公网或大内网;三是合规检查中被问到”网络打印设备是否纳入资产管理”。如果你的打印机型号支持 IPP Everywhere,这套方案同样适用,只是把 CUPS 部分替换为设备自带的访问控制项。

反过来,纯 USB 直连、不接入网络的单机打印机不在此列,它的风险面在主机侧的外设管控,不在协议层。

前置条件

  • 一台运行 CUPS 的 Linux 主机(Debian/Ubuntu 或 RHEL 系均可),具备 root 权限。
  • 已盘点打印机 IP 段,能在同网段执行扫描。
  • 确认业务上真正需要的打印方式:IPP(631)、RAW/JetDirect(9100)、LPD(515)三者取其一即可,不必全开。
  • 防火墙使用 nftables 或 firewalld,二选一,本文给两套写法。

原理说明:打印协议栈的攻击面

网络打印有三条主流通道:IPP 走 HTTP 语义,端口 631,支持认证与加密;RAW/JetDirect 是裸 TCP 9100 端口,客户端把 PCL 或 PostScript 数据流直接灌进去,没有任何认证机制;LPD 在 515 端口,协议比 IPP 老一代,只做基于源地址的信任判断。

真正的问题出在 RAW 通道上。它接受任意字节流,而 PCL/PJL 指令集里包含文件系统操作、NVRAM 读写、甚至”显示消息到面板”这类交互指令。攻击者只要能把一段构造好的 PJL 数据推到 9100,就可能读取设备存储的配置文件、把设备当成匿名文件暂存区,或者篡改面板显示做社工。更隐蔽的用法是把打印机当成内网代理:部分固件支持通过 PJL 设置目标地址,实现从打印机侧发起连接。

CUPS 侧的风险点则是配置默认值。Listen 0.0.0.0:631、Browsing On、WebInterface Yes 这三项同时成立时,任何能访问 631 的人都能看到打印队列、任务内容摘要,甚至通过 Web 界面添加打印机。CUPS 历史上多个提权漏洞(cups-browsed 系列)也正是从这条路径打进来的。

所以加固思路很明确:能关的关掉,必须留的加认证,认证不了的用防火墙按源 IP 限死。打印机本身通常改不动,但打印服务器和边界是可控的。

操作步骤

第一步:端口与型号测绘

先摸清现状,不要凭印象改配置。下面这条命令扫出网段内所有开放打印端口的设备,并尝试取设备信息。注意 9100 端口开放即意味着无认证写入通道存在。

nmap -n -Pn -p 515,631,9100,161 --open 192.168.20.0/24 -oG print_scan.txt

# 只看开放了 RAW 9100 的主机
grep "9100/open" print_scan.txt

# 取打印机型号与固件(SNMP 若未改团体名)
snmpwalk -v2c -c public 192.168.20.31 1.3.6.1.2.1.43.5.1.1.16

把结果整理成清单:IP、型号、固件版本、开放端口、归属部门。后面每一条策略都要能对应到清单上的某一行,否则就是无效加固。

第二步:收敛 CUPS 监听面

编辑 /etc/cups/cupsd.conf,只保留本机回环与内网指定网段,关闭浏览发现与 Web 管理界面。改完必须重启服务,CUPS 不读 SIGHUP 重载部分指令。

cp /etc/cups/cupsd.conf /etc/cups/cupsd.conf.bak

# 关键三行:只监听回环 + 关闭发现 + 关闭 Web 界面
sed -i 's/^Listen .*/Listen localhost:631/' /etc/cups/cupsd.conf
sed -i 's/^Browsing .*/Browsing Off/' /etc/cups/cupsd.conf
sed -i 's/^WebInterface .*/WebInterface No/' /etc/cups/cupsd.conf

# 关闭 IPv6 自动发现(若不需要)
echo "BrowseLocalProtocols none" >> /etc/cups/cupsd.conf

systemctl restart cups
ss -tlnp | grep 631

预期输出只剩 127.0.0.1:631。如果仍然看到 *:631 或 [::]:631,说明配置里有更靠后的 Listen 指令覆盖了前面的设置,用 grep -n Listen /etc/cups/cupsd.conf 逐条查。

第三步:启用认证与访问控制

仅监听回环还不够,打印任务本身需要区分身份。CUPS 的访问控制写在 <Location> 段里,按路径分级:管理接口必须要求管理员组,打印接口至少要求本机用户。

<Location />
  Order allow,deny
  Allow localhost
  Allow 192.168.20.0/24
</Location>

<Location /admin>
  Order allow,deny
  Allow localhost
  AuthType Default
  Require user @SYSTEM
</Location>

<Location /admin/conf>
  AuthType Default
  Require user @SYSTEM
  Order allow,deny
  Allow localhost
</Location>

其中 Require user @SYSTEM 表示只允许 lpadmin 组成员操作,这是 CUPS 的默认管理组。确认组成员后,把普通运维账号从该组移除,避免人人可改队列。

getent group lpadmin
# 只保留必要的运维账号
gpasswd -d intern lpadmin

第四步:用防火墙封死 RAW 9100

CUPS 无法给 9100 加认证,所以这一层必须由防火墙承担。策略是:打印机只接受打印服务器的 9100 连接,其余来源一律丢弃并记日志。nftables 写法如下,192.168.20.10 为打印服务器地址。

nft add table inet printfilter
nft add chain inet printfilter forward '{ type filter hook forward priority 0 ; policy accept ; }'

nft add rule inet printfilter forward ip daddr 192.168.20.31 tcp dport 9100 ip saddr 192.168.20.10 accept
nft add rule inet printfilter forward ip daddr 192.168.20.31 tcp dport 9100 log prefix "PRINT_DENY " drop
nft add rule inet printfilter forward ip daddr 192.168.20.31 tcp dport 631 ip saddr 192.168.20.0/24 accept

nft list chain inet printfilter forward

firewalld 环境用富规则等价实现:

firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=192.168.20.10 destination address=192.168.20.31 port port=9100 protocol=tcp accept'
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 destination address=192.168.20.31 port port=9100 protocol=tcp drop'
firewall-cmd --reload
firewall-cmd --list-rich-rules

规则顺序有意义:先放行、后丢弃,nftables 与 firewalld 都按先后匹配。如果写反,合法打印会一起被拦。

第五步:限制任务能力与 PJL 通道

打印机固件通常提供”关闭 PJL 文件系统访问””禁用远程面板””关闭 FTP/Telnet 管理”这类开关,位置在 Web 管理页的 Security 或 Network 菜单下。这一步无法用统一命令覆盖,但必须逐台做,检查项固定为四条:关闭 PJL 文件系统、改掉默认 admin 口令、关闭未使用的 IPP over USB 与云打印、固件升级到厂商当前版本。

在 CUPS 侧能做的补充是限制单任务体积,避免有人把 9100 当文件仓库:

# /etc/cups/cupsd.conf 中加入,限制单任务 50MB
MaxRequestSize 52428800

# 打印队列级别限制(每用户最多 20 个待处理任务)
lpadmin -p office-printer -o job-k-limit=20

第六步:开启审计留痕

加固之后要能被验证,所以必须留日志。CUPS 自带 PageLogFormat,能记录谁在什么时间打印了多少页,配合 journald 可回溯异常批量打印。

# /etc/cups/cupsd.conf
PageLogFormat %{%Y-%m-%d %H:%M:%S}t [Job %j] user=%u dest=%p pages=%P

systemctl restart cups
journalctl -u cups --since "24 hours ago" | grep "\[Job"

防火墙侧的 PRINT_DENY 日志同样要收:连续出现说明有人在做端口探测,单次出现可能是终端配置错误。这二者的处置方式完全不同,不要混为一谈。

配置验证

改完之后按下面四项逐条确认,缺一项这套加固就是不完整的。

  • 监听面:ss -tlnp | grep -E '631|9100',打印服务器上 9100 不应处于 LISTEN 状态,631 应只在回环。
  • 未授权管理:从普通员工机访问 http://打印服务器:631/admin,应返回 401 或连接拒绝,而不是登录框。
  • 跨段写入:从非授权网段执行 nc -zv -w3 192.168.20.31 9100,应超时或被拒绝;同时 journalctl -k | grep PRINT_DENY 应出现对应记录。
  • 合法打印:从打印服务器实际提交一次任务,lpstat -W completed -o 能看到完成记录,且页数与实际一致。

常见问题

Q1:加固后财务的针式打印机打不出票据了,怎么排查?

针式打印机多数只支持 RAW 9100 或并口转网口,不走 IPP。先确认该设备的 9100 连接源地址是否在放行名单里——财务电脑如果直接连打印机而不是经打印服务器中转,就会命中丢弃规则。处置方式二选一:把财务终端加入放行名单,或者把财务队列改到打印服务器统一中转。前者的代价是策略颗粒度变粗,后者需要重装驱动。日志里 PRINT_DENY 记录的源 IP 就是判断依据。

Q2:打印机没有认证功能,等于无论如何都能被内网任何人使用?

不是。设备侧确实加不了认证,但可以用网络层做等效控制:把打印机划进独立 VLAN,只允许打印服务器跨段访问 9100,其余终端一律走打印服务器中转。这样使用者的身份由打印服务器上的系统账号体现,设备侧只需要信任一个源地址。代价是打印服务器成为单点,需要给它做高可用或至少保证队列可快速重建。这个取舍在几十台打印机的办公网里是划算的。

Q3:CUPS 升级后配置被覆盖怎么办?

发行版升级通常不会覆盖 cupsd.conf,但会生成 cupsd.conf.default 或 .rpmnew 文件。真正容易被重置的是 /etc/cups/printers.conf 里的队列选项。建议把加固后的配置纳入配置管理(Ansible 模板或 git 仓库),每次升级后跑一次 diff,而不是靠人工记忆。

总结

打印机安全加固的核心不是装什么工具,而是把三条通道收紧:631 只留回环且强制管理员认证,9100 用防火墙按源 IP 限死,515 若不用直接关掉。设备侧再补上改默认口令、关 PJL 文件系统、升级固件三项,配合日志留痕,一台打印机的攻击面就从”任意内网主机可匿名写入”降到”单一授权源可打印”。

这项工作的性价比很高:配置量小、不影响业务、检查时能直接拿出证据。唯一的前提是先把资产清单做出来——没有清单的加固,改完也说不清改了什么。