前两天帮一个朋友收拾一台被打过洞的服务器,后台被塞了一个挖矿脚本,CPU直接拉满。排查了整整一晚上,最后的结论不是哪个0day漏洞,而是三处极其基础、基础到很多人根本不会看一眼的配置问题:SSH端口全网可连、站点管理接口直接暴露在公网、某个可写目录的权限配得过于宽松。Web服务器安全这个领域,给人的第一印象往往是需要对抗顶级黑客、研究复杂攻击链,但根据我个人处理过的这类案例,十次被攻破有七八次都倒在入门知识点上,而且栽的姿势基本一模一样。
这篇文章是《Web服务器安全知识点简述》系列的第一篇,把服务器层面最容易出问题、也最容易被忽略的安全地基过一遍,包括攻击面收敛、最小权限模型、TLS配置、流量型攻击防护,以及日志监控。适合刚接手服务器运维工作的新人,也适合那些平时主要写业务代码、偶尔要自己管服务器的全栈工程师。我不打算堆一堆高深的漏洞利用细节,而是把真正可落地的配置方法和判断逻辑讲透,你看完就能拿这份清单去自查手上的机器。
1. 安全基线:为什么默认安装的服务器就是半个靶子
所谓的安全基线,不是说让你装多高级的安全设备,而是先把那些默认存在的东西全部收紧。很多站点装完Web服务就直接把域名解析过来,浏览器能打开页面就算上线了。这个流程里,入侵窗口就已经被打开了不止一个。
1.1 安装即暴露:默认配置里的常见风险点
第一个口子是默认端口和默认页面。不少建站套件在安装时会额外起一个管理端口,常见的是8080、8888这类高位端口,用来给用户提供一个图形化维护界面。问题在于,这类端口在多数情况下会全局监听,没有任何来源限制,默认密码也往往还是第一次安装时随手设的弱口令。端口扫描器扫到端口,再配合弱口令字典跑一遍,收益高得惊人。
第二个口子是默认后台路径。很多套件后台固定挂在某个路径上,路径本身不算漏洞,但如果后台不限制来源IP、不开启二次验证,就等同于把管理钥匙挂在门口。我有一次在一个模拟项目X的环境里做测试,装完某套件后默认后台路径直接是固定且可访问的,登录页还标注了版本号,等于主动告诉攻击者可以查哪些已知问题。
第三个口子是测试文件。部分镜像会在网站根目录留下测试脚本、展示用的示例项目,打开就能读到系统版本、绝对路径。这些文件看起来人畜无害,但实际上等于主动把服务器底细交了出去。
这三个问题有个共同点:它们都不是需要深挖的漏洞,而是暴露面与默认口令的组合拳。对攻击者来说,扫一个常规端口然后尝试弱口令的成本,比研究漏洞低太多,成功率也高太多。安全基线的第一件事,就是把这类额外暴露面全部清掉。
具体做起来不复杂,装完系统之后,我会按下面这个顺序过一遍:
- 安装时选择最小化组件,只勾选真正需要的功能,不需要的一律不装;
- 检查监听端口,看哪些端口在对外网提供服务,逐条确认用途;
- 删掉自带的测试页面、示例站点和默认文档;
- 改掉所有默认账号密码,不需要的默认账号直接禁用。
这一套操作下来,表面攻击面已经小了一大圈。别小看这一步,我见过不少被攻破的服务器,问题恰恰就出在一个没人记得的默认页面上。
1.2 从端口扫描结果反推攻击面
我一直觉得,做安全加固前第一步不是盲目查漏洞,而是搞清楚自己的机器对外到底敞开了哪些门。对一台新接手的服务器,我会先从一个外部视角盘一遍开放端口,不需要复杂工具,眼下很多开源端口扫描器都能做到,一条命令就能把结果列出来。
有一台我接手过的线上机器,当时扫出来的端口情况大概是这样的:
| 开放端口 | 常见来源 | 风险程度 | 处置思路 |
|---|---|---|---|
| 22 | SSH远程管理 | 中 | 只允许管理网段访问,改用密钥登录 |
| 80/443 | Web服务对外访问 | 低 | 必须保留,保持配置干净 |
| 8080 | 面板或备用服务 | 高 | 若无公网需求,绑定内网或直接移除 |
| 3306 | 数据库服务监听 | 高 | 立即关闭外网监听,只留本地回环 |
这张表我定期会对线上机器跑一遍,重点关注那些不在预期内的端口。端口本身不是漏洞,但每一个端口都意味着一个可交互的入口,入口越多,出问题的概率越大。数据库监听如果暴露在公网上,配合弱口令就是灾难现场;而8080这种端口,往往是某些历史遗留服务偷偷跑着没人管。对于不在预期内的服务,果断停掉、删掉,别想着“以后可能还用得上”。最小化暴露原则在服务器安全里的地位,和最小权限原则一样重要。
2. 最小权限模型的落地:运行身份、目录边界与模块裁剪
最小权限是一个老生常谈的词,落地到Web服务器上,就是三个问题:进程以什么身份运行、目录结构允许谁读写、服务模块到底装了哪些。绝大多数配置失误,都出在这三处。
2.1 工作进程为什么必须是无特权用户
先从运行身份说。见过不少把Web服务进程直接以管理员权限跑的服务器,理由是图省事、权限不够会报错。这个做法的问题在于:攻击者如果能利用某个上传点拿到一句脚本后门,这个后门就继承了进程的全部权限。以管理员权限运行时,后续操作一路畅通:写系统目录、改任务计划、装持久化后门,全部不需要再提权。
反过来,如果进程以无特权的普通用户跑,攻击者进来之后能动的东西极其有限,最多在Web目录里折腾,想再往上突破,还得另找漏洞。同样是拿到一句话后门,权限模型不同,后续损失就是天壤之别。我处理过一个案例,对方站点根目录权限过宽,被塞了一个小后门文件,结果攻击者利用Web账号直接改写了另一个相邻业务的静态资源,影响范围比预期大得多。
具体落地我建议分三步。第一步,创建专用账号,不要把普通的登录账号拿来跑服务,也不要用系统默认的访客账号,单独建一个不能登录系统的服务账号,属于标准做法。第二步,把主应用目录的读写权限按需分配,程序文件由管理员账号拥有,服务账号只有读权限。第三步,在Web服务配置里明确指定运行身份。
如果系统用的是现代服务管理机制,还可以加上只读文件系统、禁止获取权限等加固选项。这些配置写起来都不难,核心就是把进程的权限边界尽可能缩到最小。
顺便说下模块裁剪。Web服务有很多可加载的功能模块,有些是默认编译进来的,但你的业务可能根本用不到。少一个模块,就少一份被利用的风险。我个人的经验是,编译安装时尽量静态编译,不用的功能直接不装;动态加载的模块,也定期盘点一遍,把长期不用的禁掉。有些人觉得模块放着不用就没风险,但模块所在的代码路径不会凭空消失,一旦对应的历史问题被公开,扫描器就能立刻识别出来。
2.2 目录读写分离与上传目录的执行隔离
目录权限是另一个高频事故区。很多站点为了省事,把整个网站根目录一次性地让Web服务账号可写,这样做确实方便,但也意味着,任何一个通过业务漏洞拿到上传能力的攻击者,都可以直接往根目录塞文件,然后想办法让文件被当成脚本执行。
这里的问题不在上传本身,而在于存储和执行边界没有分开。
合理的设计是读写分离。站点根目录里的大部分文件设为只读,由管理员账号管理;只有确实需要写入的目录,比如上传目录、缓存目录,才单独分配可写权限。对于上传目录,还要再做一层执行隔离:目录允许写入文件,但不允许里面的文件被当作动态脚本执行。
用一个常见配置来举例,在Web主机的配置片段里加上这样一个规则,表示对某个上传目录下的脚本类文件一律拒绝访问:
location ^~ /uploads/ { location ~* \.(php|php5|phtml|jsp|asp)$ { deny all; } }这一段配置的意思是:上传目录仍然可以正常访问图片、压缩包等静态文件,但一旦请求的是可执行脚本后缀,就直接返回拒绝。攻击者即便把脚本传上去,也没法在服务器上把它跑起来。注意这里的核心思想:目录可以允许写入,但绝不允许从该目录执行代码。
常见错误是只改权限,不关注文件类型。结果就是上传目录能写,也能执行,后门一传一个准。另一个容易忽视的细节是,不要让Web服务账号同时拥有站点根目录和其他敏感目录的写权限。目录之间的边界一旦打通,攻击者就可以从Web目录往别的目录拖文件。把这些边界在部署时定好,后面能省掉大量麻烦。
3. TLS配置:证书之外还有一堆隐性陷阱
现在说HTTPS。很多人觉得装个证书就算HTTPS了,其实证书只是第一道门。我接手过的站点里,有不少确实开着HTTPS,但协议版本还保留着老旧的口子,加密套件里也塞着已知强度问题的选项,甚至有的证书链没配完整,导致一部分用户访问时反复报安全隐患。这些都不算漏洞利用,但问题级别一点不低。
3.1 协议、套件与证书链:三个高频事故现场
第一个事故现场:旧协议没禁掉。SSLv3以及TLS1.0、TLS1.1这些老协议,都在历史上被验证存在可被利用的设计缺陷,常见攻击手法是利用协议漏洞干扰加密流程,让通信降级到更弱的加密状态。现在的浏览器基本已经不再允许这些老协议,但服务器配置里它们可能依然开着。建议配置里只保留TLS1.2和TLS1.3,其余协议直接关掉。
第二个事故现场:加密套件优先级不对。现代配置应该优先选择带前向保密能力的套件,也就是使用临时秘钥交换的套件,这样即使服务端私钥泄露,历史流量也无法被回放解密。配置示例大概是这样:
ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!3DES; ssl_session_cache shared:SSL:10m;这类配置写法在主流开源Web服务器里通用,具体语法稍有差异,核心就是限制协议范围、抬高套件优先级。
第三个事故现场:证书链不完整。有些站点只上传了域名证书,没有附带中间证书,导致多数浏览器理解不了信任链,直接弹出不受信任的提示。移动端应用对证书链尤其敏感,不少App接口连不上就因为这个。装配证书时,一定要把服务器证书和对应中间证书按顺序拼接成一个文件再配置,配置完之后立刻用外部网络环境访问测一遍,不要只在服务器本机测。
再补一点易踩坑的:证书续期。常见的免费证书生命周期通常只有90天,很多站点就是忘记续期导致HTTPS直接报错。建议在自动化脚本里加上到期检查,到期前主动提醒,别等着浏览器报错再来查。
另外还有HSTS。它能让浏览器强制使用HTTPS访问,对防降级很有帮助。但HSTS里的“自带预加载”一旦开启,生效后想撤销,要等一个较长的周期。域名还没完全稳定、有切回HTTP需求的站点,不要急着开预加载,可以先只开普通HSTS。
3.2 安全响应头:便宜又容易被漏掉的一层
另一类几乎零成本的加固是HTTP响应头。它们不涉及复杂的加解密度,只是在每次页面返回时带上一组声明,却能限制浏览器在处理页面时的默认行为。以下这些是基础配置里我基本都会加的:
- Content-Security-Policy:告诉浏览器页面允许加载哪些来源的资源,如果攻击者想通过注入脚本从外部加载代码,就会被这条规则拦下来;
- X-Frame-Options: SAMEORIGIN:声明页面不允许被其他站点用内嵌框架加载,能减少点击劫持类攻击;
- X-Content-Type-Options: nosniff:禁止浏览器猜测文件类型,避免服务器返回类型不明文件时被强行解析为可执行内容;
- Referrer-Policy:控制请求头里的来源信息携带策略,减少访问路径、查询参数等信息外泄。
示例组合大概是这样的:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; X-Frame-Options: SAMEORIGIN X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin这些响应头加上几乎不消耗性能,但也可能误伤业务。最容易翻车的是CSP:如果站点用了第三方统计、外部字体、CDN资源,而CSP规则里没有放行这些域名,页面上的资源会被全部拦掉,显示出来就是缺样式、报错一大堆。经验做法是先别急着执行最严格策略,而是把CSP先开成观察模式,跑一段时间把被拦截的域名收集一遍,确认没有影响业务后再逐步收紧。
4. 面对流量型攻击:基础限流能扛住什么
攻击里最让人头疼的不是单个漏洞,而是直接把服务器资源耗尽。这类流量型攻击在服务器端能做的其实是有限的,但有一些基础配置不做,会让服务器的扛击打能力掉一个数量级。这一节先说清楚哪些能自扛,哪些必须求助外部。
4.1 内核连接参数与请求速率限制的实际组合
连接耗尽类攻击的目标是让服务器把资源全耗在处理无效连接上。最常见的手法是通过大量伪造的半开连接,把连接表占满,让正常用户连不上。应对这个问题的第一道防线在内核层,开启SYN cookie后,系统在连接队列溢出时能通过一种无状态机制来确认请求的合法性,算是一种低成本高收益的缓解手段。相关内核参数建议这样设置:
net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_syn_retries = 2 net.ipv4.tcp_max_syn_backlog = 1024这里要说清楚,这些参数并不是让机器变成铁桶,而是让机器在高压力下不至于立刻瘫掉。真正的公平限制还要在业务入口层做:限制单IP同时建立的连接数,以及限制单IP单位时间内的请求速率。以常见入口配置为例,可以抽象成两层限制,第一层限制同一IP的并发连接数,第二层限制单位时间内的请求速率,整体写法类似这样:
limit_conn_zone $binary_remote_addr zone=conn_limit:10m; limit_conn conn_limit 20; limit_req_zone $binary_remote_addr zone=req_limit:10m rate=10r/s; limit_req zone=req_limit burst=20;这里主要表达的是两层意思:同一来源IP在入口层同时建立的连接数有上限,活跃请求过多时其他请求要排队等待。对绝大多数正常的浏览器用户来说,这些阈值余量充足,不会造成误伤;但扫描器、恶意爬虫和低级的连接耗尽攻击会立刻显得非常吃力。阈值设定建议先放宽一倍观察几天,再根据访问日志微调,不要一上来就拍一个极限值。
4.2 访问控制的分层思路:白名单优先于黑名单
做访问控制很容易陷入一个误区:拼命收集恶意IP,把黑名单越拉越长。坦白说,黑名单只能挡住已知的攻击来源,而攻击者换IP的成本极低。更好的思路是反过来,以白名单为主,以黑名单为辅,把访问控制分成几个层次。
第一层是网络层访问控制。防火墙规则只放行明确需要的端口,管理端口只允许特定网段访问。这是最基础也最有效的一层。
第二层是服务层访问控制。后台、管理接口这类敏感路径,直接在Web服务配置里限定来源IP,不在白名单内的网段一律拒绝。好处是即使网络层防火墙被绕过,服务层还能兜底。
第三层是自动化封禁。借助日志分析和封禁工具,读取多次登录失败的记录,超过阈值就把来源IP加入临时拒绝名单。这类工具实现成本很低,适合中小站点。
第四层是业务层控制。登录接口加上频率限制、验证码等机制,避免批量撞库。四层配合下来,效果比单靠任何一层强很多。
另外补充一点真实踩过的坑:有个业务站点曾经为图省事把海外IP段全部封掉,结果一查后台,发现有一大批海外华人用户正在正常使用,当天就撤销了规则。封禁策略要跟业务形态配套,不能拍脑袋。管理类入口必须白名单优先;面向普通用户开放的业务,封禁策略要谨慎,尤其不要凭一时冲动封掉大片IP段,否则误伤真实用户之后,处理投诉的时间远比封禁省下的时间多。
5. 日志与监控:危机发生前就该做好的事
最后聊聊日志和监控。我处理过不少事后排查的活,最崩溃的情况是登录服务器一看,日志要么没开,要么被清空了,只剩下一个被打得乱七八糟的现场。与其事后痛苦,不如在部署当天就把日志这件无聊但重要的事做好。
5.1 四类日志里最值得盯住的异常特征
需要重点关注的日志,我一般分成四类:
- 访问日志:记录每个请求的来源IP、请求路径、状态码、请求头等信息。它是分析攻击行为的第一手材料。
- 错误日志:记录服务运行中发生的异常,SQL语法错误、文件权限不足等都会在这里反映出来,攻击尝试经常在错误日志里留下痕迹。
- 认证日志:记录所有登录行为,尤其是SSH、管理后台的登录尝试。失败次数猛增是暴力破解的明确信号。
- 应用日志:按业务需要自行输出,记录业务层的异常操作。
这些日志的保留策略要提前定好,别舍不得磁盘空间。备份归档加定期清理,既保证出问题时能翻到,又不会让日志文件无限膨胀。
异常特征不用背,记住几个高频套路就够了。第一,单个IP在短时间内请求大量不存在的路径,状态码一堆404,多半是目录扫描。第二,大量POST请求打到本来不需要接受数据的接口上,可能有攻击者正在向目标提交恶意数据。第三,同一个IP反复更换请求头,完全不符合正常浏览器的习惯。第四,请求路径里出现超长参数、编码混用或明显意外的文件后缀,都要额外留意。
光有日志不分析等于白存。最简单的分析方式,就是先从访问日志里按状态码和来源IP做个聚合,比如统计昨天返回404最多的前十个IP,一条Shell命令就能跑出来:
awk '$9 == 404 {print $1}' /var/log/access.log \ | sort | uniq -c | sort -rn | head -20如果某个IP的404数量是其他正常用户的几十倍,那个人大概率有问题,值得再翻一下它的完整请求记录。
5.2 用小型脚本先把告警跑起来
监控也不一定非要上复杂的监控平台。小规模服务器,用shell脚本加上定时任务,就能覆盖绝大多数核心告警。你可以从这几个指标入手:最近一段时间内认证失败的次数、最近一段时间内错误状态码的数量、磁盘使用率、系统负载。写一个脚本,把每个指标统计出来,超过阈值就发通知,大概几十行代码就够。
脚本逻辑不需要多花哨,核心就是“统计—比较—触发”。例如:
#!/bin/bash LIMIT=20 FAILED=$(journalctl --since "10 min ago" 2>/dev/null | grep -c "Failed password") if [ "$FAILED" -gt "$LIMIT" ]; then echo "检测到大量认证失败,最近10分钟共 $FAILED 次" # 可以在这里追加通知逻辑 fi脚本能不能起作用,关键在阈值怎么定。建议第一次上线时把阈值放得很宽,比如允许10分钟内失败20次,先把工具跑起来,观察一周获取正常基线,再逐步收紧。
这里有个很现实的坑:告警设置太灵敏,每天给手机推几十条消息,过不了两周人就会麻木,最后真正出事时反而没人看。宁可先把阈值放粗、少打扰,也不能让看监控的人产生告警疲劳。日志轮转也要定期检查,避免一个文件涨到好几个GB后既影响读取又拖慢服务器。这些基础工作不酷,但真到被攻击那一天的复盘里,它们才是救命的关键。
写到这里,服务器侧的安全地基算是过了一遍。回顾我处理过的案例,真正把网站搞挂的往往不是那些酷炫的高级手法,而是攻击面没收紧、权限放太宽、日志没留底这类基础问题。你先把攻击面收敛、最小权限、TLS、访问控制和日志监控这五件事做扎实,再谈那些更复杂的安全设备也不迟。
这是系列的第一篇,下一篇我打算把目光转向Web应用本身,聊聊注入类攻击、跨站脚本这类应用层攻击在真实站点上是怎么被拦截的,以及它们和服务器侧配置怎么配合。如果你有想优先听的话题,可以留言告诉我,我会根据反馈调整后面几篇的侧重点。