端口一关,数据无忧:PHP服务器安全实战
|
凌晨三点二十,机房空调嗡嗡响得像台老拖拉机,我盯着监控屏右下角那块胶布——去年贴的,遮着个反复飘红的IO wait告警灯,早糊成灰黄色了。泡面桶斜插在工位底下,汤汁干了半圈,是昨天晚饭。 五一前一晚八点,小王在运维群甩了个截图:“PHP-FPM子进程卡在uninterruptible sleep,load 47.3”。我没回。心想这数字虚高,肯定是cron里那个日志轮转脚本又卡住stat()了——它卡过七次,每次kill -9完重启fpm,世界就清静了。我原以为这次也是……结果十分钟后,nginx开始502,接着DNS解析超时告警弹出来两回,第三回没弹,因为Zabbix Agent自己掉线了。 九点零三分,客服电话杀进来。声音抖:“李工,用户说付款页白屏,支付宝回调一直收不到……”我说查log,她打断:“页面根本没加载,连js都不动。”我切到服务器敲netstat -tuln,一眼扫过去——80、443、9000全在。再扫第二眼:9001没了。9001是fpm的备用端口,去年搞灰度切流临时开的,上线后忘关。我手指悬在键盘上顿了两秒,手心发黏。这个我到现在也没想明白:为啥关9001会把80和443的TCP队列一起塞死?tcpdump抓包看SYN-ACK发出去就丢,可conntrack表里全是ESTABLISHED…… 十一点十五,老板在群里发了个红包,配文:“辛苦兄弟们,节后调休双倍”。小张立马回:“谢老板”,顺手把红包领了。我没点。泡面汤渣粘在桶沿,我拿笔盖刮了刮,刮下来一缕油膜。窗外雨停了,但排水沟还在咕嘟咕嘟响,跟机房那台拖拉机空调节奏错拍。 十二点半,磁盘报警炸了:/var/log满了。不是/var/log/nginx,是/var/log/audit——审计日志里堆着整整三十七万行“avc: denied { bind } for port=9001”,全是SELinux拦的。我们压根没开SELinux,但去年重装系统时Ansible脚本从旧模板抄了这句,没删。没人动过auditd.conf,所以它默默记了一年半。删日志前我手抖了一下,rm -rf /var/log/audit/ 手误多按了个空格,变成 rm -rf /var/log/ audit/ ——audit目录还在,但log下面全空了。小张凑过来问:“李哥,你刚删的是不是fpm的慢日志?”我说不是,他说哦,转身去重启rsync,说主库备份脚本昨儿就卡在“waiting for lock”上没动。
文章配图,仅供参考 凌晨一点四十七,我摸黑拔了网线。不是整机下电,就拔了bond0的物理口——想看服务到底赖在哪儿不走。结果拔了三秒钟,zabbix恢复了,80端口活了,但443死透。证书没过期,openssl s_client -connect 看得到CN和SubjectAltName,握手却卡在Client Key Exchange。重装openssl?重载nginx?都试了。最后是小王用strace盯了五分钟,发现nginx worker进程在反复open("/etc/ssl/private/xxxx.key")然后EACCES,明明权限600,ls -l看着都对。后来才发现——那个key文件属主是root:wheel,而我们把nginx跑在www-data组,但chown时漏改了group。就差那一个冒号,差那三分钟。 两点五十分,我靠在值班室铁皮椅上打盹,椅子吱呀一声,惊醒了。隔壁小学操场传来早锻炼的广播声,是个女声,正念《春晓》:“春眠不觉晓……”我忽然想起上个月搬家,搬家公司把我的机械硬盘当废品扔进楼道垃圾桶,我蹲着翻了四十来分钟,捡回来三块盘片,其中一块主引导扇区被划出指甲盖大的银痕——数据倒是全在,因为做了xfsdump,但那个备份脚本,从没跑成功过,cron里写的是“0 2 /backup/dump.sh >> /dev/null 2>&1”,而dump.sh第一行是#!/bin/bash -e,里面有个tar命令路径写错了,所以每次执行都exit 1,然后被>>吞得干干净净。没人见过error log。 五点零八分,终于能ping通外网。我打开手机浏览器,输入自己域名——跳https,锁头绿的。点进去,首页有张轮播图,是五一促销的横幅,底下还写着“倒计时:2天16小时”。小张说这图上周就传好了,CMS后台早就点了发布。我嗯了一声,鼠标悬在“立即抢购”按钮上,没点。旁边监控屏右下角,那块胶布边缘翘起来了,露出底下红灯一闪一闪,微弱得像快没电的手电筒。 中午十二点,同事老赵拎着两盒药过来,板蓝根冲剂,包装盒上印着“预防季节性呼吸道感染”。他把一盒放我桌上,另一盒往自己兜里揣:“我闺女发烧三天了,今早退了,说要吃烤肠。”我撕开板蓝根铝箔,水是温的,有点苦。客服刚微信甩来新截图:用户反馈支付成功页没跳转,订单状态一直是“待支付”。我扫了一眼——又是那个fpm响应头里少写的Location字段,三年前就该修,修了两次,又回滚了,因为前端兼容逻辑太野。 现在想,真要挑最蠢的错误,不是漏关端口,不是audit日志疯长,也不是证书属组搞错。是去年十一前,我把所有PHP项目的error_reporting设成了E_ALL & ~E_NOTICE,加了个注释:“避免日志污染”。结果线上跑了四个月,没人发现PDO预处理语句里混了变量拼接——SQL注入没爆,是因为攻击者只扫到了502页面。直到五一前扫描器报出/backup/phpinfo.php,才扒出根目录下躺着个被遗忘的调试文件,连chmod都还是777。 端口一关,数据无忧。这话听着荒唐,可细想——关的哪是端口?是那一层层叠起来的侥幸,是“先上线再说”的呼吸声,是备份脚本里那个永远沉默的exit 1,是监控屏上胶布盖着的红灯,是别人闺女发烧时你还盯着nginx error_log里第327行那个重复的warning。优胜劣汰?汰掉的不是人,是那些被当成背景音的噪音:空调嗡嗡、排水沟咕嘟、还有你心里那个总说“下次再修”的声音。 明天先把告警阈值调了。把Zabbix里所有“disk usage > 90%”的触发器,改成 > 82%。得留点余量——给泡面汤干掉的时间,给小王查strace的时间,也给我自己蹲垃圾桶翻硬盘的时间。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


PHP工程师私藏的12个冷门高效技术资源
PHP Web安全实战:SQL注入防护精要
PHP工程师三年技术栈变迁实录
小程序服务器安全:端口管控与数据保护实践
全平台适配:PHP多端网站资源优化实战方案
全平台多端适配的PHP资源优化实战方案