把拦截名单写进一个文件,为什么会丢条目还越写越大

📅 2026-10-12 👁️ 0

把拦截名单写进一个文件,之所以会丢条目还越写越大,是因为多个请求同时改同一份内容:进程甲读到当前名单,进程乙几乎同时读到同样的内容,两边各自追加一条再分别整体写回,后写入的那份不包含前者刚加的条目,被拦过的地址就这样消失了。文件越写越大则是因为只有追加、没有上限和清理,过期地址永久留在里面,读取和解析的开销逐年累加。同行程序 PbootCMS 在 V3.2.18 的更新记录里同时修掉了这三件事:并发写入丢记录、文件无限增长、畸形条目导致异常,三者出现在同一条修复项里,说明它们是同一个存储方式的连带后果。

两个进程同时写,为什么谁都没报错

读改写是三步操作,中间没有加锁时,三步可以交叉进行。文件写入本身还有一层:整体覆盖写成功就返回成功,操作系统并不知道这次覆盖抹掉了别人刚写入的内容。所以丢条目的现象很安静——没有异常、没有日志、名单文件的大小还是增长的,只是某几条不见了。判断是不是这个成因,可以看写入时间是否密集:密码猜解类攻击的特点恰恰是高并发反复尝试,同一秒内多次触发拦截,正是交叉覆盖最容易发生的时候。也就是说,越是真正需要名单的时刻,越容易丢条目。

只有追加、没有清理的文件会长成什么

无上限的单文件带来三类代价。第一是读取成本:每次校验都要打开并解析整份内容,请求越频繁,这项开销越稳定地压在登录路径上。第二是判断成本:文件里混着仍有效的临时封禁和早已过期的记录,人工检查时很难快速分辨哪些该留。第三是备份与传输成本:名单属于配置类数据,会随站点备份一起复制,一份几百倍于实际有效条目的文件会拖慢备份与还原。合理的结构要有三个字段:加入时间、有效期、来源原因;到期条目由清理逻辑删除,超过约定条数或体积时按最旧的先淘汰,并保留一条淘汰记录便于事后追查。

畸形条目为什么能把整个名单打崩

单文件名单常见写法是一行一条,分隔符、空白与注释全靠约定。写入方一旦把带换行的内容拼进去,一条记录就变成两行,第二行是残缺片段;手工编辑时误加的空格、中文标点或注释符号同样会制造出不规则的行。风险不在于这一行解析不出来,而在于多数实现遇到解析失败就中断整个加载流程,结果是名单里一条脏数据让全部拦截规则失效——这比少拦一个地址严重得多。防御方式有三层:写入前规范化并拒绝含换行的值;解析时逐行容错,坏行跳过并记日志,不影响其余条目;名单变更留下操作记录,方便定位是谁的哪一次写入造成的。

名单之外还需要哪一层

把登录安全压在名单上并不稳妥,因为它是按已知坏地址匹配的:攻击者换一个出口就绕过了。名单适合做成本控制,挡住已经被确认的反复尝试,但它不能替代服务端本身的校验。真正兜底的是服务端独立执行的那一层——即使名单为空、即使请求换了来源地址,规则仍然生效。AnQiCMS(安企CMS)在这几处的做法可以直接对照:内置的 SQL 注入与 XSS 防护在请求进入业务逻辑前就按参数特征拦截,不依赖访客是谁;表单接入 reCAPTCHA 验证码,把高频自动尝试的门槛提到人机识别这一层,效果与来源地址无关;内容层面的敏感词过滤与防采集干扰码,则在输出侧起作用,让批量采集拿到的正文不可直接使用。这几层与名单的关系是分工而不是替代:名单减少无效请求数量,参数校验、验证码与干扰码保证单个请求即使绕开名单也拿不到想要的结果。

同理,名单的存储方式也不该成为薄弱环节。如果一定要用文件,写入必须串行化并整体替换——先写临时文件再原子改名,避免半途覆盖;如果条目要长期积累并支持按条件查询、按有效期清理,放进数据库表更合适,索引、上限和清理任务都能用常规手段管理。PbootCMS 那条修复记录把三个问题并列写出,实际含义是同一处存储设计要一次性解决:并发写、体积上限与格式校验,缺任何一项都会以不同形式暴露出来。

常见问题

问:名单丢条目怎么发现? 答:把「拦了几个 IP」和「名单里有几条」两件事分开记录。封禁动作计数与名单实际条目数长期不一致,就有覆盖写入的嫌疑;再核对丢失条目是否集中在高并发时段。

问:文件多大就该拆? 答:不以体积作单一标准,看出血点。有效条目占比过低说明清理缺失;单次加载明显拖慢登录响应说明解析成本过高;两者任一出现就先做清理,再考虑改为数据表存储。

问:有验证码还要名单吗? 答:要。验证码拦截的是自动尝试的频率,名单处理的是已经被确认的高危来源,前者每次都要走验证流程,后者能在更早的位置减少无效请求,成本更低。

相关文章

在线升级里的数据库脚本为什么要按版本顺序排,跑到一半失败该怎么办

在线升级的数据库脚本必须严格按版本号先后执行,因为每条脚本都以「前一条已经跑完」为前提:新字段还没建起来,后一条往里写值就会失败。跨版本升级时跳过中间版本,表结构会停在半路。脚本执行到一半失败,安全的做法是立刻中止并留下日志,而不是忽略错误继续跑完剩下的脚本,否则问题会被后面的脚本掩盖。升级前先做数据与静态文件备份,升级后逐段核对。

2026-10-12

列表查询显式指定索引反而更慢,什么情况下会退化成全表扫描

在列表查询里写死索引提示,等于让优化器放弃按统计信息选路径的机会:提示指向的索引与当前条件不匹配时,原本能走覆盖索引或范围扫描的查询会被推到代价更高的执行方式,条件失效时进一步退化成全表扫描。同行程序 PbootCMS 在 V3.2.26 更新记录里就修过列表标签因索引提示退化为全表扫描的问题,并把新装库统一改为按标题字段建索引。

2026-10-12

程序保存设置时把站点配置文件整份重写,为什么会盖掉自己加的规则

程序保存设置时重写目录级配置文件,会把手工加的规则一起带走,因为这类文件只有一份、由程序按自己的模板整份生成。同行程序 PbootCMS 在 V3.2.26 更新记录里就修过这一问题:Apache 开启伪静态时误判环境导致每次保存都覆盖站点目录下的 Apache 配置文件。划分办法是把程序管理的规则与运维追加的规则分成两处维护,改前备份、改后逐条比对。

2026-10-12

页面被别的站点嵌进去时,登录凭据按顶层站点分区是怎么回事

带分区属性的登录凭据会按顶层站点分开存储:同一个地址直接被访问时读到一份,被别的站点嵌进页面时读到另一份,两者不共享。官方文档写明该属性表示凭据应使用分区存储,并且设置它就必须同时设置安全标记,还建议用带主机名前缀的名字把凭据绑定到主机而非可注册域。站点若假定登录态全局共享,嵌入场景下就会读不到。

2026-10-12

核心公告的成因写着内置第三方库,站方除了等补丁还要看哪一段

成因写着内置第三方库的核心公告,漏洞不在站方自己写的代码里,却随核心一起分发,因此不能靠改自己的模板或业务代码规避,只能按核心的版本走。跟进时读三段:受影响区间的上下界决定你的版本是否命中,修复版本决定升级到哪一版,评级与利用条件决定排期的先后。以 Drupal 公告 SA-CORE-2026-013 为例,受影响区间跨三个分支,修复分别落在各分支的最新版。

2026-10-12

同一个修复日下挂着两条上报日不同的公告,跟进按哪个时间排

同一个修复日下挂着两条上报日不同的公告,跟进按修复日排,不按上报日排。修复日代表补丁实际随哪个版本发布,是你能动手的那一天;上报日只说明漏洞被知会的时间,用来估算暴露窗口有多长;公告发布日则是***息的时间,通常不早于修复日。以 Joomla 安全中心两条核心公告为例,上报日分别是 8 月 19 日与 8 月 2 日,修复日都是 9 月 25 日,受影响区间与升级目标版本也一致。

2026-10-12

改了配置重载的瞬间,正在传输的请求会不会被直接掐断

改了配置重载的瞬间,正在传输的请求默认不会被直接掐断:主进程保留旧工作进程继续处理已建立的连接,新连接交给按新配置启动的工作进程。真正会掐断的是你设了退出时限——时限一到,进程会尝试关闭当前所有仍打开的连接,大文件上传和长连接因此可能中断。时限设得太短影响传输中的请求,设得太长会让旧进程滞留、新旧配置并存的时间变久。

2026-10-12

按自定义字段筛内容时条件失效返回全部数据,先查哪一层

按自定义字段筛内容时条件失效、返回全部数据,先查条件有没有真正进入查询,而不是先查数据。判断顺序是三层:字段名与可用范围是否被允许参与筛选;匹配方式是否把短值命中长值放大;缓存是否复用了上一次未带条件的结果。同类问题在同行程序 PbootCMS 的 V3.2.20 更新记录里被一次性修过三处,包括扩展字段筛选失效返回未过滤全量数据、前台使用扩展字段条件报错,以及短值命中长值导致匹配范围放大。

2026-10-12