阅读视图

RSS阅读器安利:Fluent Reader

前言

由于之前在网上搜寻到RSS订阅工具都差强人意,今天闲来无事,偶然发现了一款颜值非常高的开源免费RSS订阅器:Fluent Reader , 于是便想写一篇文章安利下

如果想要使用,微软商店直接搜索下载即可,也可以前往 GitHub 上下载,此仓库包含了APP端软件包

至于更多RSS订阅源可跳转相关链接处

RSS 介绍

RSS(Really Simple Syndication,真正简单的分发)是一种用于发布和订阅网站内容的数据格式和协议。它通过简单的 XML 格式来传递网站的文章、新闻、博客等信息,允许用户通过订阅器(RSSReader)获取网站内容的最新更新,而无需直接访问网站。
RSS 最早出现在 1999 年,由 Netscape 公司创立。随后,RSS 标准逐渐发展,演变为不同的版本和格式。常见的 RSS 版本包括 RSS 0.9x、RSS 1.0、RSS 2.0 和 Atom 等。

RSS 的工作原理如下:

  1. 网站创建并维护 RSS 文件:网站管理员会将网站的文章、新闻、博客等内容整理成 XML 格式的 RSS 文件,并将其发布在网站的特定位置,通常是一个预定义的 RSS Feed URL。
  2. 用户使用订阅器订阅 RSS Feed:用户可以使用 RSS 订阅器(也称为 RSS Reader 或 Feed Reader)来订阅感兴趣的网站的 RSS Feed。订阅器会定期检查订阅的 RSSFeed,以获取其中的更新。
  3. 订阅器获取更新:当订阅的网站有新的文章或内容发布时,RSS Feed 文件会被更新。订阅器会检测到这些更新,并将最新的内容显示在用户的订阅列表中。

RSS 的优点包括:

  • 方便获取信息:用户可以一站式地收集和查看多个网站的内容更新,无需频繁访问每个网站。
  • 自动化更新:订阅器会定期检查更新,用户不需要手动去查看是否有新的内容发布。
  • 隐私保护:RSS 订阅不需要提供个人信息,保护用户的隐私。

随着社交媒体和其他内容分发平台的兴起,RSS 的使用逐渐减少。然而,RSS 仍然被许多网站和博客用于提供内容更新,并且一些专门的订阅器应用程序仍然广泛使用,满足了一部分用户对于个性化内容订阅的需求。

软件截图




相关链接

GitHub地址:Fluent Reader
RSS入门指南:高效获取信息,你需要这份 RSS 入门指南 - 少数派 (sspai.com)
使用体验:Windows平台最美RSS阅读器-Fluent Reader上手体验 - 知乎 (zhihu.com)

友情提醒:卸载软件时,记得导出相关订阅源进行备份
  •  

Baby Anti-Spam 自建反垃圾评论系统

很久之前,就经常收到akismet的授权提醒,对应一个错误码10010。

刚开始还以为是多域名访问导致的授权校验出问题了。后来换了n个key,同时添加了插件hook掉所有的垃圾评论检测逻辑,让全部走统一的域名,结果前几天又收到这个提醒了。

插件代码:

<?php
/**
 * Plugin Name: Akismet 单一主域名(多域名站点)
 * Description: 当站点配置了多个域名时,强制发往 Akismet 的请求只使用一个主域名,避免被计为多站点触发 10010。
 * Version: 1.0
 * Author: obaby
 *
 * 使用:在下方设置 AKISMET_CANONICAL_HOME 为主域名(或留空则用 WordPress「设置」里的站点地址)。
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * 主域名(规范 URL,不要末尾斜杠)。留空则使用 get_option( 'home' )。
 * 例如: https://www.example.com
 */
if ( ! defined( 'AKISMET_CANONICAL_HOME' ) ) {
    define( 'AKISMET_CANONICAL_HOME', 'https://zhongxiaojie.cn' );
}

/**
 * 获取发往 Akismet 时使用的唯一主域名 URL。
 */
function akismet_single_domain_get_canonical_home() {
    $home = AKISMET_CANONICAL_HOME;
    if ( $home === '' || $home === null ) {
        $home = get_option( 'home' );
    }
    return untrailingslashit( $home );
}

/**
 * 将任意 URL 替换为使用主域名的版本(只改 host,保留 path/query)。
 */
function akismet_single_domain_normalize_url( $url, $canonical_home ) {
    if ( empty( $url ) || ! is_string( $url ) ) {
        return $url;
    }
    $parsed = wp_parse_url( $url );
    $canon  = wp_parse_url( $canonical_home );
    if ( empty( $canon['scheme'] ) || empty( $canon['host'] ) ) {
        return $url;
    }
    $scheme = isset( $parsed['scheme'] ) ? $parsed['scheme'] : $canon['scheme'];
    $host   = $canon['host'];
    $path   = isset( $parsed['path'] ) ? $parsed['path'] : '/';
    $query  = isset( $parsed['query'] ) ? '?' . $parsed['query'] : '';
    $frag   = isset( $parsed['fragment'] ) ? '#' . $parsed['fragment'] : '';
    return $scheme . '://' . $host . $path . $query . $frag;
}

/**
 * 统一 verify-key / get-subscription / get-stats 的 blog 为主域名。
 */
add_filter( 'akismet_request_args', function ( $request_args, $path ) {
    $paths = array( 'verify-key', 'get-subscription', 'get-stats' );
    if ( ! in_array( $path, $paths, true ) ) {
        return $request_args;
    }
    $canon = akismet_single_domain_get_canonical_home();
    if ( ! empty( $request_args['blog'] ) ) {
        $request_args['blog'] = $canon;
    }
    return $request_args;
}, 10, 2 );

/**
 * 统一 comment-check(以及 recheck)的 blog、permalink,并把请求里的 HTTP_HOST 等改为主域名。
 */
add_filter( 'akismet_request_args', function ( $request_args, $path ) {
    if ( $path !== 'comment-check' ) {
        return $request_args;
    }
    $canon = akismet_single_domain_get_canonical_home();
    $parsed = wp_parse_url( $canon );
    if ( empty( $parsed['host'] ) ) {
        return $request_args;
    }
    $canon_host = $parsed['host'];

    $request_args['blog'] = $canon;
    if ( ! empty( $request_args['permalink'] ) ) {
        $request_args['permalink'] = akismet_single_domain_normalize_url( $request_args['permalink'], $canon );
    }

    // 让服务端看到的“当前请求”也统一为主域名,减少被计为多站点
    if ( isset( $request_args['HTTP_HOST'] ) ) {
        $request_args['HTTP_HOST'] = $canon_host;
    }
    if ( isset( $request_args['REQUEST_URI'] ) ) {
        $uri = $request_args['REQUEST_URI'];
        $request_args['REQUEST_URI'] = ( is_string( $uri ) && ( $p = wp_parse_url( $uri, PHP_URL_PATH ) ) !== null ) ? $p : '/';
    }
    if ( isset( $request_args['DOCUMENT_URI'] ) ) {
        $uri = $request_args['DOCUMENT_URI'];
        $request_args['DOCUMENT_URI'] = ( is_string( $uri ) && ( $p = wp_parse_url( $uri, PHP_URL_PATH ) ) !== null ) ? $p : '/';
    }
    return $request_args;
}, 10, 2 );

/**
 * 统一 submit-spam / submit-ham 的 blog、permalink。
 */
add_filter( 'akismet_request_args', function ( $request_args, $path ) {
    if ( ! in_array( $path, array( 'submit-spam', 'submit-ham' ), true ) ) {
        return $request_args;
    }
    $canon = akismet_single_domain_get_canonical_home();
    $request_args['blog'] = $canon;
    if ( ! empty( $request_args['permalink'] ) ) {
        $request_args['permalink'] = akismet_single_domain_normalize_url( $request_args['permalink'], $canon );
    }
    return $request_args;
}, 10, 2 );

这次授权的密钥撑得时间稍微长了点,但是最终还是收到了这个提醒,意思是需要订购商业版授权。我这个人站点为了发垃圾评论订购一个商业版授权,确实有些难以接受。

于是,我决定自建反垃圾评论系统,基于scikit-learn实现了现在的这套垃圾评论检测系统,训练数据一部分来源于github的开源数据,另外一个就是我自己博客的评论数据。为了保证样本正例和负例数量差别不至于过大,经过各种方式进行了多轮数据清洗。

如果想要评论识别更加准确,可以提供自己的博客评论数据,如果能提供垃圾评论更好。现在欠缺的主要是垃圾评论数据,正常的评论数据我已经提供几千条数据。

效果测试:

测试地址:https://anti-spam.zhongxiaojie.cn/test/spam

简介:

面向 中英混合 评论的 WordPress 垃圾识别方案:PHP 插件在评论入库前调用 本机 Python 服务,由小型多语种向量模型 + 分类器(或演示用规则)给出垃圾概率。

适合评论量不大、单机部署(例如 4 核 / 8GB RAM 的 Ubuntu),服务与 WordPress 同机时使用 127.0.0.1 即可。

目录结构:

baby_anti_spam/
├── README.md
├── screenshots/             # 文档:服务启动与 curl 自测示意
│   ├── service.png
│   └── test.png
├── service/                 # Python FastAPI 侧车服务
│   ├── .env.example
│   ├── requirements.txt
│   ├── requirements-ml.txt
│   ├── run.py
│   ├── app/
│   │   └── stats_backends/   # 统计存储:sqlite / mysql
│   └── scripts/
│       ├── init_stats_mysql.sql
│       └── init_stats_mysql.py
│       ├── train_sklearn.py
│       ├── download_embedding_model.py
│       └── download_embedding_model.sh
└── wordpress/baby-anti-spam/
    └── baby-anti-spam.php # WordPress 插件

关键配置:

变量 说明
SPAM_HOST 监听地址,同机建议 127.0.0.1
SPAM_PORT 端口,默认 8765
SPAM_API_SECRET 单密钥模式(兼容旧版):未配置 SPAM_API_KEYS 且未配置 SPAM_API_KEYS_FILE 时,仅此密钥有效,等价于 name=default、不限流(max_rpm=0)。与 WP 插件里填写的密钥一致
SPAM_API_KEYS 多密钥:JSON 数组。每项为 name(唯一,用于统计与限流分组)、key 或 secret(与请求头一致)、max_rpm 或 rpm(每分钟最大请求数,0 表示不限制)。与 SPAM_API_KEYS_FILE 合并时:先读文件条目,再追加本变量
SPAM_API_KEYS_FILE 可选,指向 JSON 文件,根节点为与上表相同结构的数组。文件必须存在,否则进程启动失败
SPAM_MODEL_PATH 训练得到的 *.joblib 路径;留空则取决于 SPAM_FALLBACK_RULES
SPAM_FALLBACK_RULES 无模型文件时是否启用内置极简规则(演示用);生产训练后应设为 false 并配置 SPAM_MODEL_PATH
SPAM_LABEL_THRESHOLD 可选,默认 0.8spam_score ≥ 此值时 JSON 中 label 为 spam,否则为 normal
SPAM_DFA_ENABLED 默认 true。为 true 时使用 dfa-python-filter/keywords 做敏感词检测;命中则直接 spam_score=1detail=dfa_sensitive(早于 sklearn)
SPAM_DFA_KEYWORDS_PATH 可选,自定义敏感词文件路径;留空则用 service/dfa-python-filter/keywords
SPAM_NON_CHINESE_FLOOR_ENABLED 默认 true。为 true 时若合并后的 author/email/url/text 中无任何 CJK 表意字符(主要针对中文训练语料),则将 spam_score 至少抬到 SPAM_NON_CHINESE_SPAM_FLOOR
SPAM_NON_CHINESE_SPAM_FLOOR 默认 0.9。与上项配合,在「无中文」评论上与 sklearn / 规则分取 max
SPAM_STATS_ENABLED 默认 true。为 true 时记录每次成功返回的 /v1/classify 请求与响应(失败 / 401 不落库),并允许 /v1/mark-spam 写入 spam_marks 表
SPAM_STATS_BACKEND sqlite(默认)或 mysql。选 mysql 时需安装 pymysql(已在 requirements.txt)并配置下方 MySQL 变量
SPAM_STATS_DB_PATH 仅 sqlite:数据库文件路径;留空则为 service/data/stats.sqlite(已加入 .gitignore
SPAM_STATS_MYSQL_HOST / SPAM_STATS_MYSQL_PORT 仅 mysql:默认 127.0.0.1 / 3306
SPAM_STATS_MYSQL_USER / SPAM_STATS_MYSQL_PASSWORD 仅 mysql:连接账号(user 必填)
SPAM_STATS_MYSQL_DATABASE 仅 mysql:库名(必填),默认示例 baby_spam_stats
SPAM_STATS_MYSQL_CHARSET 仅 mysql:默认 utf8mb4

系统服务启动截图:

wp插件配置:

项目地址:https://anti-spam.zhongxiaojie.cn

代码地址:https://cnb.cool/oba.by/baby-wp-anti-spam

说明:如果自己不想训练数据,下载发布版的spam_pipeline.joblib 放入指定目录下配置服务启动即可,baby-anti-spam.zip 为wp插件。

训练耗时大约11分钟:

  •  

清明假期收尾碎碎念

三天清明假期一晃就结束啦!
算算日子,上班还不到一个月,再过没几天又盼来五一假期,节奏还挺舒服。这三天懒得来回折腾回老家,就在合肥安安静静待着放松。

周五晚上窝着刷李世鑫的短剧,一看就停不下来,直接追到半夜,剧情看得我都跟着有种重生代入感,特别上头。结果假期第一天,生物钟根本不由人,早上八点准时醒,啥重生懒觉福利都没有,哈哈!

白天太阳特别好,气温一下子回升好几度,干脆把夏天的衣服都翻出来收拾,足足洗了四洗衣机,天气给力、阳光足,中午过后衣裳就全都晒干晾透了。
在宿舍待久了实在闷得慌,总不出去见见人,感觉人都要憋出毛病来。

本来打算自己骑车去罍街逛一逛,顺便在那边吃点东西再回来。刚好室友睡醒出门找吃的,随口聊了几句,俩人一拍即合,临时改主意想去爬大蜀山,后来他又喊上同小区另一位同事,我们三个人结伴出发。

之前也来过两次大蜀山,那时候带着孩子、推着遛娃车,顶多走到半山腰盘山公路就折返了,上段台阶路不好走,也没法往上登。这次算是实打实第一次成功登顶!

我们大概四点半从小区附近坐地铁到大蜀山,爬山的人特别多,合肥平时能逛的室外地方也不多,节假日大家都扎堆过来散心。
三人一人备一瓶水,顺着山路往上走,不知不觉就到了半山腰盘山路段。再往上就是陡峭石阶,同事说早年这里还没有防护栏杆,石阶也是后来翻新修缮的,应该是近几年游人太多,景区设施都整修升级了不少。

后半段台阶爬着真心累,上山的人挤人、挨着走,也快不起来,我们咬咬牙一路不停总算冲到山顶。山顶有两个观景平台,一处专门看日落,一处俯瞰合肥城区街景。
当天傍晚有点雾气,远处灰蒙蒙一片看不真切,我们到山顶差不多六点,夕阳快要落山,雾蒙蒙的拍景也不出片,有点小可惜,上面这个信号塔应该是最高处。

在城景平台看了会儿周边,往东正对着黄山路,天气通透的话应该能远远看到我们住的楼栋,可惜有雾找了半天也没对上位置。后来找路人帮忙拍了三人合照,也算打卡留念,第一次登顶圆满啦!能看出来下面这个书法大厦是在哪个位置么。


下山就快很多,全程上山加下山,一共花了一个半小时,运动量感觉跟在小区来回爬好几趟30层高楼差不多。
下山后几个人就近找地方吃饭,还喝了不少小麦果汁,一个个吃得肚饱喝足,之后全程走路溜达回去,硬生生走了一个多小时,当天累得浑身发沉。

昨天起床还没什么感觉,今早一醒,小腿、屁股两侧酸得厉害,刚下地走路都费劲。平时不怎么运动,突然来一波高强度爬山暴走,身体根本扛不住,看样子得缓好几天才能歇过来。
今晚没事就骑车出去溜达溜达了,今天可不敢猛走猛造了!
(上面内容用豆包润色了一下,意思跟我编辑的都一样,不同的人嘴里说出来是不一样哈)

  •  

两岁半小棉袄的快乐时光

 闺女在家向来能睡到十点多才起床,今天一大早刚醒就打来了视频。洗漱完之后,便跟着妈妈在梳妆台前有模有样地学化妆,小模样认真得很。

 不由得感叹,现在的小孩子真是聪明,接触新鲜事物也早。这才不到三岁的小家伙,做起事来常常透着一股小大人的模样,不像我们小时候,六七岁还在玩泥巴。
 奶奶给她做好吃的,她会竖起大拇指,按在奶奶额头上,奶声奶气地夸:“奶奶真棒,给你点个赞。” 穿衣服也讲究搭配,尤其偏爱小裙子,买衣服时必须让她自己挑,不喜欢的连试都不肯试,看上的便攥在手里不肯撒手。晚上妈妈搂着她睡觉,她会软软地说:“妈妈抱,爱妈妈。” 嘴甜的时候,心都要被她融化了。
 当然,撒泼耍赖的时候也毫不含糊,满地打滚、鬼哭狼嚎,真是让人又气又笑。可转念一想,孩子嘛,不哭不闹反倒不正常了。
 原本打算过完年送她去幼儿园适应一下,去学校一问,年龄太小人家不收,加上之前还不会自己上厕所,便作罢了。最近天气暖和,她才慢慢学会自理,干脆再玩几个月,等下半年满三岁再正式入园。
 前些天刚学会用筷子,之前一直用辅助筷都不太会,换成正常筷子,反倒用得有模有样。说来惭愧,我到现在都握不好标准的拿筷姿势,跟她一比还不如个小朋友。
 就让她好好享受这最后几个月无忧无虑的春日时光吧,等秋天,再正式开启她的小小 “修炼之路”。
[vplayer url="https://img.hxy.cc/file/blog/260329-2suiban2.mp4" /]

  •  

注册两个免费域名玩玩

 前两天看见军爸发的可托管CF的免费域名注册,本来是没需求的,昨天晚上收到了西部数码发过来的邮件,唯一闲置的域名也被一口价出掉了,那就没有闲置域名了,主要想要一个域名用来做博客站的境内外分路解析,用作CF的SAAS的中转域名,通过军爸的链接注册了一下,可以免费注册两个前缀,其实就是一个二级域名,主要是可以作为一个独立域名托管在CF上,刚好可以满足这个需求,省的再去单独注册一个域名了,注册好之后托管在彩虹的NS域名管理程序,到时候1年到期会有提醒,挺好。

 原本手里的域名和服务器该卖的卖,该丢的丢,现在就剩两个域名和一个虚拟主机了,不再购入新的了,说不定有朝一日也转去静态博客,虚拟主机也不要了。

  •  

周六和室友小酌一杯

 之前说过我的一个同学被优化了,当时没过多久,我们这边也收到了通知,要有7个人被裁掉,其中有一个就是我的室友,二月初好像就通知了,说是让干到三月底,年后一过来也是各种饭局,经常喊他吃饭喝酒说送他呢,前几天有天晚上回来也是晕乎乎的,说要跟我喝点呢。
 我就从京东下单买了两瓶古5,也是昨天一大早就送过来了,本来想着晚上的时候出去吃点喝点,赖了一上午床,中午爬起来想做点东西吃,看着冰箱里的菜,这不也能整几个么,就跟室友讲了一下,中午别让他点外卖了,我简单炒几个菜一块吃点。
 他说最近喝酒喝的有点多了,最后我整的古5也没喝,他去楼下超市搞了一箱啤的上来。应该是一点多开始吃的,最后干到了晚上六七点,就一直喝着聊天啥的,一箱啤的被我俩造完才算结束。
 最近也在和公司谈赔偿的事,他在这边干了七年多了,想的是至少能赔个N,但是之前领导找他谈过一次,说能赔个小几万,赔不了N,现在还在僵持着,下周再找机会再和领导PK一波吧,实在谈不妥再想别的办法。
 附个随手拍的图片吧,最后闷了个从家带回来的鸡肉,吃了一半才想起来拍个照片,哈哈,在家养了几个月的鸡还挺好吃。

  •  

修理修理几个手机

 距离上次更新已经一个月了,好像还没有间隔这么长时间断更过。主要还是今年春节放假时间太长了,前后将近放假一个月了。在家的时候基本上就跟家人在一起很少看手机,也就没有动力来更新博客了。
 本来公司通知的是3月10号上班,我本来最开始抢的是3月4号的票,后来了解了一下,挺多同事都是10号才过来,我又改了个票,从4号改到了8号,上周末才过来,新的一年的第一周已经过去,5天一过,又是一周。一天一天上班过的感觉比在家过的还快,还有一个半月,五一假期又能回家了。
 我之前用的有一个小米11的手机,一直做备用机。过年回家拿回家了,给我奶奶用了,把她原来的vivo Z1换了下来,原来那个手机太卡了。她日常用来微信视频通话和刷抖音,换下来的vivo Z1我就拿回来了,不安装任何APP,专门用来开热点使用。
 回去之前又购入了一台iPhone17,回家给我媳妇儿的新年礼物。然后我媳妇儿这边又退休下来一个iPhone13 Pro,我也拿回来了,专门收破烂,哈哈,之前她就就一直吐槽,他的iPhone13 Pro不存电,这次我拿回来直接在网上买了个电池,好像是有一个活动吧,然后100多拿下,日常是210左右,这带的还有安装服务,只贵了10块钱,周二的时候快递一到就去附近店里让人家给换上了,直接满血复活,续航杠杠的,备用机+1。

 再一个就是我的主用机小米14,用了有两年半了,日常工作用手机还是挺多的,算是重度使用,现在基本上就是需要一天三充。干脆也换个电池得了,然后就单独买了一块电池,回来自己换,实际操作下来倒也不难,只不过可能是第一次没经验,没把握好尺度,有一个角还没有完全撬开,抬了一下后盖,直接给后盖玻璃干碎了。。。又紧急在闲鱼淘一个小米14的原装拆机后壳,晚一会应该快递就到了,再给它换上,这样密封性良好,防水好一点吧。


 这个电池说是小米15移植到小米14和小米13使用的,比原装电池容量大,用了两天确实续航提升不少,又能再战三年,等着换小米20。

  •  

没心思上班了,准备开溜

 春节前的最后一周了,大家都没心思上班了,各种花样摸鱼,这两天云闪付上有个活动,徽动消费 合肥GO,二月份新开的合肥区域公司的发票,上传上去可以抽奖,都是大额优惠券,大的100,小的也得有20,这两天周围的同事也是玩的不亦乐乎,都抽了几百几百的优惠券,应该是政府发优惠券推动消费,利用开发票可以增加企业纳税,真是一箭三雕。
 周日晚上公司例会,通知了放假安排事宜,准备从2月12号放到3月10号,反正中间各种事吧,不好明说,这整的放假时间太长了,这期间绩效是要扣除的,只有基本工资,如果按照这个时间休,差不多要被扣五六千,其实是想放假时间长一点的,但是这也太长了,前后冗余两天都差不多了,反正准备3月初提前一周过来吧,到时候按照每天工作报备,还能少扣一点,公司去年春节还有礼品,今年毛线都没。
 上周还想着奋战到最后一天呢,结果这通知的猝不及防的,也没有买高铁票,正好老丈人丈母娘他们准备这两天回,从金华回来经过合肥带上我,正好我可以开下半程,也省的我再单独找顺风车了,晚会回去准备收拾收拾东西,再去理个发,下次再理发就要等二月份了,虽然不可证实的正月不理发的习俗,但是也保持了29年了,还是继续保持下去吧。

  •  

老同学被优化了

 中午回来吃饭正吃的香呢,老同学发来了个微信,说他被优化了,赶紧扒拉扒拉吃完饭,给他去了个电话问了下啥情况。

 我们是大学同一个宿舍的室友,毕业之后我基本上无缝衔接,去了上海待了两年,然后就来了合肥,他是毕业之后先在他们本地那边待了两年,然后报培训班学习考了个行业内高级证书,应该是23年的时候他入职了郑州那边,我们算是干的一样的工作,一直干到现在,虽然是背靠大厂,但是也从去年11月12月份开始,新出了各种规则,取消加班工时调休和超额加班费,各种降本增效,其中有一项就是人员配置优化,我们这暂时还是一个萝卜一个坑,大刀可能还没砍到头上,今天他给我发消息,我才意识到,要开始了,不知道什么时候优化到我们这里。
 我这老同学那边据他描述,已知的就暂时通知了他自己,让干到三月底,还有一些老资历员工都没被优化,他说别人干的他不一定能搞下来,但是他做的事情别人能接住,所以被优化的只能是他,唉,想想他去年6月份才结的婚,这刚结婚半年结果工作没了,也是比较难顶。
 如今各行业都是比较难过,也别想太大了,现在能有个相对稳定的工作已经很难得了,老家一些在外面打工的也是老早就回家过年了,外面一些厂子里面也是活不多,早早就放假了。

  •  

宽带改公网投诉小记

 老家的两条联通宽带安装的时候就是装的PPPOE公网的,本来家里有网络摄像头做的有DDNS映射,非标端口外面可以直接访问,前几天发现不行了,从联通APP里面查光猫看了下,竟然私自给我改成NAT私网了,小联通竟然在用户毫不知情的情况下私自变更用户业务类型。
 前天晚上越想越气,搞的晚上都没睡好觉,脑海里一直在想第二天怎么怎么投诉,说什么硬气的话。上周三还有个营业厅的固话给我打电话问我最近宽带用的怎么样,我也没多想,纯粹支持他们工作,就给了好评,现在回过来想想,搞不好就是他们私自改了,然后做下回访,看用户有没有感知呢。
 昨天上午一直在忙,中午得空直接打了10010客服进行投诉,倒也算处理的积极,一会省投诉中心,一会市投诉中心,一下午接了几个电话,又挨个给他们解释一边投诉原因,我就说我安装的是公网,为什么在用户不知情的情况下给我改成私网,现在搞的在外地看老家摄像头都延迟很大,挂机游戏老掉线(编的,不打游戏),一路转派投诉单,最后给派到了乡镇营业厅。
 早上营业厅人员给我打电话,还给我说了一顿,她问我,你知道什么是公网吗,只有企业,医院这种才是公网,家里面的都是私网,。。。我寻思我天天搞这些,我不知道我还能投诉到这??还说她干了15年了,也没听说过宽带搞公网的,我让她不知道就给我往上报,找技术支撑去问,也就不到一个小时,就有市公司的技术人员给我打电话,说私网改公网改好了,让我重启光猫试一下,我远程重启了一把,终于又恢复了公网。


 我在想肯定是后台做什么改造,或者批量回收公网的操作,如果用户不投诉,改了就改了,有投诉再改回来,真是让人恼火,以后还想着有公网IP可以搞点家里云自己用呢,这指定是不能再给我改了。

  •  

身份证到期更换

 25年应该有很多人的身份证到期吧,我应该算是赶到了末尾,目前在用的是截止到2026年2月23日到期,还剩下不到一个月的时间了,说是提前两三个月就可以申请办理了,我这本来也没着急,一个月前应该就有各种APP和短信给我推送发消息说身份证即将到期让及时更新,否则影响相关服务的,这周五公司也找过来了,让传新的证件信息,哈哈,我还没办啊。
 其实在两周前就在皖事通上申请了,预约的今天去办理,预约的时候按照提示说是可以自己拍照上传,审核通过后可以直接用自己拍的照片,上周得空让同事帮忙拍个大头照,准备去上传呢,结果提示我非安徽户籍,我申请的时候怎么不提示啊,还引导我可以提前拍照,白期待了,还是等到今天乖乖去线下办理。
 预约的我们这最近的蜀山区政务中心,上午九点过去的,骑车十几分钟,到那问了下工作人员,也挺简单的,基本上去办事的大部分都是办理身份证业务的,人倒也不多,就先排队拍照,也没让看一眼,拍完照接着取号等着窗口喊号办理,等了有两分钟吧,就喊号了,看来人是真的不多,到了办理窗口也就确认一下信息,然后录取指纹,说是做信息比对,确认一下是本人,然后让我看了一眼拍的照片,让我确认一下是不是,就看了一下侧着的电脑屏幕,不说了,真的不是很好看,自己拍出来也不是这样啊,看到时候拿到证是什么效果吧,确认好信息之后让付了20块钱的办理费用,整个过程不到五分钟。
 业务人员说是20天后过来取,我媳妇之前是六七月份吧,来办理的说是可以邮寄,好像是一周多就寄过来了,我也去问了一下办理了邮寄业务,是邮政承办的,扫码把办理的业务号进去,填好收件地址,又付了12块钱的邮寄费用。
 整个过程还是比较顺利,本来以为周末人会多的,结果不到半个小时就搞定了,我这是第三个身份证了,第一次是5年,第二次是10年,这次办理的应该是该20年有效期了,记得10年前办理第二张的时候还要回老家户籍地派出所办理,现在整的是挺方便,跨省也可以直接办理,期待新证,希望不要太丑。。
1月31日更新:
 25号上午去办理,31号上午收到证件,快递送货上门,不得不说还挺快,这次是20年有效期,照片还行,看得过去,除了双下巴有点明显,不过也是真实情况,哈哈。

  •  

姥姥和奶奶

 前几天老家下了大雪,从家里监控看到在下雪的第二天妈妈把姥姥接过来了,说是接过来住几天,难得妈妈在家这么长时间,临近春节了,也是想和她的妈妈待一起照顾几天吧。
 今天看到姥姥和奶奶在院子里面晒太阳,挺温暖的一幕,他们俩的日常生活轨迹完全不同的,也难得待在一起晒太阳。

 姥姥是胖胖的,好几年前还因为胆的问题做过两次手术,也就导致平常也不能干重活,走路干啥的都慢慢的,平常也就待在我的三舅家的小卖部帮忙照看一下,记得我小的时候就喜欢去姥姥家,那时候还是姥爷开的有间小卖铺,就喜欢去里面拿些小玩具啥的,每到春节还都能得到姥姥姥爷给的一把“枪”,那时候有一把玩具枪别提有多开心了,但是我的姥爷好像就停留在了那时候的某一年,就剩下我姥姥一个人了,应该都有快二十年了,我记得姥姥一直是这个样子,一直都是不紧不慢的,也可能是身体原因吧,反正每次有机会回老家都会回去看看姥姥。
 奶奶就不一样了,同样都是七十几岁的人,奶奶还是干劲十足,现在家里都是各种各样的农活,给人家扒红薯、种蒜、种辣椒、摘辣椒、电商打包、打玉米等等,真是啥活都干,一天有时候挣个三四十,有时候整个一百多,比人家年轻人都能干,尽管家里人都各种劝说,不要出去干活了,不要出去干活了,但是没一个人说的动,只要有活,别人打电话给她,肯定去。我的爷爷也是在我订婚的那一年走了,每次回家都盼望着能看到孙媳妇,如果再坚持半个月也就能看见了,最后还是没等到,爷爷的时间也就停留在了22年的春节,好像就是自从那之后,正好也是家里流行起来了日结干农活,专门找家里的老头老太太干些农活,管饭还给钱,也就很多人可以去,奶奶就经常出去干活,我想着,也可能是她自己一个人待在家无聊吧,出去干活好歹能多几个人唠唠嗑,反正各有利弊吧,有啥轻活重活都去干,最后再给身体累出病来还是不划算。
 树欲静而风不止,子欲养而亲不待,如果能有空还是抽出时间陪陪家里人,不要说真等百年之后再去后悔,那其实没有任何意义的,就算陪家里人在太阳底下晒暖,那也无疑不是一种尽孝。

  •  

给博客所有图片加个水印

 此方法为又拍云存储+腾讯云EdgeOne的组合,实现免费10G存储+无限流量的图片外链,理论上适用于所有对象存储和所有CDN的组合。
 正常一些技术博客发的技术文章还是比较有价值的,文章附图最好还是加个水印,也减少一点被盗的风险,我这生活水文博客那就看心情了,也没啥值得盗的,纯属折腾哈,简单记录一下。
 首先是要用云存储作为图床,无论是直接上传或者通过图床程序,在对应的云存储内找到图片处理功能

 接着设置一个间隔标识符,后面会用到,又拍云是有三个可选(! _ –),然后设置一个图片处理规则,这里面就按需配置,如果上传之前已经对图片进行过处理了,这里就推荐不要进行压缩或者更改格式的其他处理了,只开启添加水印一个功能,可以设置文字水印或者图片水印,选好水印放置的位置,设置好对应的参数之后,点击右侧的小喵咪可以进行刷新预览看一下最终呈现的样式,达到想要的效果之后进行保存此规则,注意样式命名,后面会用到。


 到此就可以进行实际图片测试了,比如原来的图片外链是https://img.hxy.cc/2026-ceshitupian.webp,加上前面设置的图片处理参数之后的格式就是https://img.hxy.cc/2026-ceshitupian.webp-shuiyin,这样其实还是没有达到目的,因为访问不带图片处理规则的原链接,还是没有水印效果的,想要最终实现不加参数又能加上水印,这就要去CDN加速那边设置了。
 又拍云的处理规则设置好之后,到腾讯EO的对应图床域名的规则引擎中设置回源URL重写,可以按照如下函数进行配置

正则表达式: ^/(.*\.(jpg|jpeg|png|gif|webp|bmp))$   (这里加上实际使用的图片格式的后缀)
替换为: /$1-shuiyin  (这里的-就是上面设置的间隔标识符,shuiyin就是上面设置的图片处理规则的名称)


 这样配置好之后,把EO这边的图床域名的缓存清除一下,然后再刷新文章内的外链图片,此时看到不用每张图片进行添加后缀,已经成功添加水印,这样设置的好处就是不用每张图片都单独进行添加,而且万一以后有换域名,直接把水印规则里面的水印换一下就行了,原图片不受影响。
 看了下七牛云的云存储,基本上一样的方式,也可以达到上面的效果。

 经常看到用对象存储的博友被刷多少多少流量,以至于产生巨额账单而劝退使用对象存储,我觉得还是没有做好防护措施吧,使用不限量的EO或者ESA,还有可以设置达量拉闸的多吉云,然后用http回源到对象存储,因为https可能会收费,这样基本上不会产生费用啊,我用了好几年的又拍云联盟,一年有61块的券,我25年按照这种方式设置之后才产生不到1块钱的费用。

  •  

下载微信公众号的视频

作为一个专业的程序媛,前端时间折腾龙虾转发公众号的文章到闺蜜圈wiki,之前已经处理了图片和文章的问题,今天转发的时候发现另外一个问题:文章里面的视频无法正常播放。

刚开始的时候想着直接去chrome的缓存里面找,但是试了下chrome://cache发现无效,又不想去找插件来干这件事情。直接去调试工具找对应的视频地址:

然而直接贴到地址栏,直接报403了。

唉,好尴尬,既然有本地缓存文件了。那么直接尝试将接收到的数据流写入到文件呗。找了半天没发现怎么直接把请求到的数据写入到文件,点击开始播放等待缓冲结束。

加载完了右下角的数据也就有了,直接切换成base64,复制粘贴:

然而,尝试decode 之后,播放不了,缺少mp4的头文件,这就挺奇怪的。文件头哪里去了?my_video为通过代码下载的mp4,video为通过base64 处理的图片。

文章测试地址:https://mp.weixin.qq.com/s/heoer_zm4SFwFKsk4tRecQ

看了下是video标签实现的:

<div data-v-c66e8e28="" class="js_inner inner not_fullscreen"><div data-v-c66e8e28="" class="js_video_poster video_poster"><div data-v-c66e8e28="" class="video_mask"></div><video data-v-c66e8e28="" src="https://mpvideo.qpic.cn/0bc3pidsgaahauamxiglsruvo6wden5aoiya.f10002.mp4?dis_k=247900efb8791f0718998ea0813793c9&amp;dis_t=1775118363&amp;play_scene=10120&amp;auth_info=d9/5u/dlYUBWn6qY0Sp2SXM9PUdEOj5CZmQ3H2k2TzNOXXtjTwYQen0+WTMXEzdWIDNuS0hkIHgTMSlENWAcfUpBcQ==&amp;auth_key=ed4a91866522f27b4b89c5e71e04d115&amp;vid=wxv_4453415887525888005&amp;format_id=10002&amp;support_redirect=0&amp;mmversion=false" poster="http://mmbiz.qpic.cn/sz_mmbiz_jpg/GAVxEAgJstytcf0uF3dpdZKia9G96C3loxCNaBrbFLHCiak3GvJDfASC7uYqNjjAZ5e2OHSmHoBQrONRJ8UIq6icJjjFXMfUBtdhy7VWlfb3MM/0?wx_fmt=jpeg&amp;wxfrom=16" webkit-playsinline="isiPhoneShowPlaysinline" playsinline="isiPhoneShowPlaysinline" preload="metadata" crossorigin="anonymous" controlslist="nodownload" class="" style="display: block; width: 655px; height: 492px;"> 您的浏览器不支持 video 标签 </video></div><div data-v-f4ee5450="" data-v-c66e8e28="" class="video_poster__info__play" style="display: none;"><i data-v-f4ee5450="" data-v-c66e8e28="" class=""></i></div><div data-v-f4ee5450="" data-v-c66e8e28="" class="video_poster__info" style="display: none;"><p data-v-f4ee5450="" data-v-c66e8e28="" class="video_poster__info__title" style="font-size: 17px;">继续观看</p><p data-v-f4ee5450="" data-v-c66e8e28="" class="video_poster__info__desc" style="font-size: 12px;"> 孤独症,就是不爱说话吗? </p></div><div data-v-f4ee5450="" data-v-c66e8e28="" class="video_poster__info__mask" style="width: 100%; display: none;"></div></div>

还是说着这个东西还有另外的处理逻辑?哪位大神知道原因还望不吝赐教。

既然decode不行,那就直接上代码吧:

#!/usr/bin/env python3
"""
下载 mpvideo.qpic.cn 等需 Referer 的 MP4(微信视频 CDN)。

Author: obaby
  https://zhongxiaojie.cn
  https://oba.by
"""

import argparse
import sys
import urllib.error
import urllib.request

# 与常见微信内嵌页一致,避免 403
DEFAULT_REFERER = "https://mp.weixin.qq.com/"
DEFAULT_UA = (
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 "
    "MicroMessenger/7.0.20"
)


def main() -> None:
    p = argparse.ArgumentParser(description="带 Referer 下载 mpvideo MP4")
    p.add_argument("url", help="完整 mp4 URL(含查询参数)")
    p.add_argument(
        "-o",
        "--output",
        default="downloaded.mp4",
        help="保存路径(默认 downloaded.mp4)",
    )
    p.add_argument("--referer", default=DEFAULT_REFERER, help="Referer 头")
    p.add_argument("--user-agent", default=DEFAULT_UA, help="User-Agent")
    args = p.parse_args()

    req = urllib.request.Request(
        args.url,
        headers={
            "User-Agent": args.user_agent,
            "Referer": args.referer,
        },
        method="GET",
    )
    try:
        with urllib.request.urlopen(req, timeout=120) as resp:
            data = resp.read()
    except urllib.error.HTTPError as e:
        print(f"HTTP {e.code}: {e.reason}", file=sys.stderr)
        sys.exit(1)

    out = open(args.output, "wb") if args.output != "-" else sys.stdout.buffer
    try:
        out.write(data)
    finally:
        if out is not sys.stdout.buffer:
            out.close()
            print(f"已写入 {args.output},{len(data)} 字节")
            if len(data) >= 8 and data[4:8] == b"ftyp":
                print("魔数检测:疑似标准 MP4(含 ftyp)")


if __name__ == "__main__":
    main()

现在就可以下载之后,上传了,发布的文章地址:

孤独症,就是不爱说话吗?

💾

  •  

弱弱的问一下,我的网站怎么被镜像了嗫?

其实网站被镜像这件事情,本身没什么稀奇的,如果想搭建一个镜像网站,从零开始也不过个吧小时的时间。

之所以写这个东西,是因为最近有看到好几个人被镜像的,这一个(爱娃子),还有 这一个(我是军爸)。

不过,既然还有人有疑惑,那就简单的教一下大家怎么来镜像个网站吧。

为此,我创建了一个开源项目:

OpenResty + OpenCC 反向代理简繁转换


基于 OpenResty 反向代理上游站点,对 HTML 正文 做 OpenCC 简繁转换(默认:简体 → 繁体,配置文件为 s2t.json)。适合在不改源站的情况下,为访客提供另一种字体习惯版本。

功能概览

能力 说明
反向代理 HTTPS 回源(示例站点:zhongxiaojie.cn),客户端走本机证书与域名。
HTML 简繁转换 仅当 Content-Type 含 text/html 时对整页做 OpenCC UTF-8 转换。
gzip 解压 通过 Lua zlib 尝试解压响应体(与去掉 Content-Encoding 的配合视上游行为而定)。
链接与图片 URL 保护 转换前将 href / src / poster / data-src / srcset 及裸 http(s):// 链接替换为占位符,转换后还原,避免路径或查询串中的汉字被改写导致 404
IPv4 优先解析 resolver … ipv6=off + 变量 proxy_pass,减轻云主机无 IPv6 时对 AAAA 连接失败的问题。
静态资源直过 图片、CSS、JS、字体等扩展名单独 location不做 OpenCC,减轻负担、避免误伤二进制。
动态库加载 对 libopencc.so 按常见路径依次尝试 ffi.load,降低找不到共享库的概率。

限制与说明

  • JSON / JS / CSS 内嵌字符串若不在上述保护规则内,仍可能被转换;重要数据建议不要用全文 HTML OpenCC 硬转。
  • 内联样式 style="background:url(...)" 未单独做保护,若遇少数破图可再扩展规则。
  • 转换配置在 nginx/opencc/opencc-filter.lua 中的 OPENCC_CONFIG(默认 /usr/share/opencc/s2t.json);若需 繁体 → 简体 可改为 t2s.json 等(需系统已安装对应 OpenCC 数据文件)。

部署要求

  • OpenResty(带 lua-nginx-module)。
  • OpenCC 运行时:系统安装 libopencc.so 与词典数据(如 /usr/share/opencc/*.json),并保证 worker 进程能加载到 .so(见下文「共享库」)。
  • Lua 可 require('zlib') 的模块(用于 zlib.inflate,若无 gzip 体则 pcall 失败会跳过解压,不影响后续逻辑)。
  • 上游为 HTTPS 时,本机需能解析并访问该域名(已用 resolver 时 VARIABLE 形式 proxy_pass 才会走指定 resolver)。

部署步骤

1. 安装 OpenCC 与数据文件

以 Debian / Ubuntu 为例(包名因发行版略有差异):

sudo apt update 
sudo apt install -y libopencc1.1 opencc # 或 libopencc2 等,以仓库为准 
或者手工复制 lib64目录下的文件到 脚本对应的路径就是这个 /usr/lib64

 

确认存在词典,例如:

ls /usr/share/opencc/s2t.json

 

2. 确保能找到 libopencc.so

若日志出现 libopencc.so: cannot open shared object file

  • 将库放在系统默认搜索路径,例如 Ubuntu amd64:
  • ldconfig -p | grep opencc

     

  • 若库仅在 /usr/lib64 等非默认路径,可执行(与仓库 fix.md 一致):
  • echo '/usr/lib64' | sudo tee /etc/ld.so.conf.d/usr-lib64.conf sudo ldconfig

     

  • 或在 OpenResty 的 systemd 单元 中设置 Environment="LD_LIBRARY_PATH=/usr/lib64:/usr/local/lib"  后重启。

脚本内已对多路径做了 ffi.load 尝试;仍失败时请对照 ldd 与 opencc 包实际安装位置排查。

3. 部署 Lua 脚本

将 nginx/opencc/opencc-filter.lua 复制到服务端约定路径(与 nginx 配置一致),例如:

sudo mkdir -p /usr/local/openresty/lua 
sudo cp nginx/opencc/opencc-filter.lua /usr/local/openresty/lua/opencc-filter.lua

按需修改脚本顶部 OPENCC_CONFIG 指向本机实际的 JSON 配置。

4. 合并 Nginx / OpenResty 配置

  • 将 zero.zhongxiaojie.cn.conf 中的 server 块纳入主配置(include 或粘贴到 nginx.conf 的 http {} 下)。
  • 修改 证书路径日志路径上游域名 zhongxiaojie.cn、以及 body_filter_by_lua_file 的路径,使其与当前环境一致。
  • header_filter_by_lua 中去除 Content-Encoding,便于对明文 HTML 做处理;若上游与解压逻辑不匹配,需自行观察是否需要调整。

5. 校验并重载

sudo /usr/local/openresty/nginx/sbin/nginx -t 
sudo /usr/local/openresty/nginx/sbin/nginx -s reload 
# 或 systemctl reload openresty

 

6. 验证

  • 浏览器访问你的站点,查看页面简繁是否符合预期。
  • 检查 图片与站内链接是否正常(尤其含中文或 % 编码的路径)。
  • error.log 中不应再出现 OpenCC 库加载失败或大量 IPv6 unreachable(在无 IPv6 环境)。

配置项速查

项目 位置
OpenCC 配置 JSON opencc-filter.lua → OPENCC_CONFIG
Lua 脚本路径 zero.zhongxiaojie.cn.conf → body_filter_by_lua_file
上游站点 set $upstream_host … 与 proxy_pass https://$upstream_host$request_uri
DNS / 仅 IPv4 resolver 223.5.5.5 8.8.8.8 valid=300s ipv6=off
不参与转换的静态文件 `location ~* .(gif

故障排查

现象 可能原因
libopencc.so 找不到 未安装包、ldconfig 未包含库目录,或需 LD_LIBRARY_PATH
body_filter 报错、栈指向 ffi.load 同上;或架构不一致(如 32/64 位混用)
上游连接 IPv6 失败 已用 ipv6=off + 变量 proxy_pass;仍失败则检查防火墙与 DNS
图片 404 历史上多为 OpenCC 改了 URL 内汉字;当前脚本对常见属性已做保护,若仍有个别,检查是否来自 CSS url() 或 JS 动态拼接

如需改为其他域名、证书路径或 t2s 转换方向,只需改配置文件与 OPENCC_CONFIG,无需改 OpenResty 核心。

实际效果:

开源项目地址:https://gitee.com/obaby/baby-website-mirroring-tool

参考链接:https://blog.csdn.net/wzj_110/article/details/127758020

https://blog.rexskz.info/support-traditional-chinese-using-openresty-and-opencc.html

  •  

记录一下我因为懒导致六天被入侵三次的经历

感谢订阅陶其的个人博客!

我有一份兼职,目前处于运维状态。

因为我的懒,导致上周被入侵了三次。

第一次

1、入侵警告

事情开始时间是2025年03月18日,周三。

老板(他有阿里云主账号)在群里发,接到阿里云安全警告短信,说是我使用的RAM账号的AccessKey泄露了,检测到对云资源有异常试探访问记录。

我赶紧登录我的RAM账号,因为我的RAM账号被授权几乎所有权限,所以一开始我并没有登录老板的主账号。

我登录之后,看了一眼告警日志,说是我这个RAM账号的一个AccessKey泄露了,对方(后面称为黑客)使用这个AccessKey去试探性访问云资源的一个接口,去获取基本信息,属于中低危警告。

我一时有一点懵,我有点纳闷,并不清楚这个AccessKey怎么突然泄露了。

因为这个AccessKey只在下面几个地方存在:

  • 我自己电脑的项目里;
  • Gitee私有代码仓库;
  • 测试服务器部署的jar里;
  • 生产服务器部署的jar里。

可以说保护的算不错来了,应该并没有泄露的可能,然后我检查了一下:

  • 我电脑里有360,我也扫描了一下,没发现电脑被入侵的记录。
  • 检查了一下Gitee账号,发现没有其他异常IP地址登录和异常动态,我也把密码改了,之前授权但是不用了的Token也被我删除了。
  • 那最大的可能就是从测试服务器或者生产服务器泄露的。

因为这个AccessKey涉及到好几个生产项目的使用,这些项目需要一直保持可运行状态。

被中低危警告的级别迷惑了的我,想着先申请一个新的AccessKey,把所有测试环境项目和生产环境项目的AccessKey都替换之后再去禁用这个泄露的AccessKey。这样那些生产项目只需要经历2-3分钟重启停服,问题不大。正好这个AccessKey也已经使用9个月了,早就该换了。

2、被黑客创建高权限RAM账号

结果正在我按部就班愉快的更换项目代码里Accesskey时,老板又发了一条信息,说阿里云发警告短信,说泄露的AccessKey正在创建新的高权限的RAM账号。

?(此处应该有:黑人问号脸.gif)

发生了什么?这次这么刺激的么?

然后等我从项目代码切回阿里云操作界面时,我发现我的RAM账号被退登了(被踢了)。

我以为是长时间没操作,登录过期了,当我再尝试登录的时候,登录界面弹出提示:当前账号不允许RAM用户登录,请使用主账号登录联系客服。

一开始我没仔细看到“联系客服”,心里卧槽一声,我这个账号不会被黑客删了吧,对方不会已经拿到主账号的权限了吧。

我的RAM账号不能用了,我又想到对方还创建了一个高权限的RAM账号。

我赶紧问老板要他的手机号码和验证码登录主账号。

结果老板在忙,不回我消息,电话也直接挂断。

在遭到我的消息轰炸之后,老板终于把验证码发了过来。

经历好久终于登录上主账号,那时候心里特别忐忑,千万不要动云资源和服务器数据库啥的呀,如果被勒索了,这兼职铁定完了。

此时阿里云的顾问也打来电话,说账号疑似被入侵了,我说我正在处理了,不得不说阿里云的安全嗅探和提醒机制是真🐂🍺。

3、紧急处理

所以等我登录上主账号之后,我第一时间查看RAM列表,然后把对方创建的RAM账号给禁用并删除了,然后我赶紧把我RAM账号的AccessKey给禁用了,防止对方再搞事情。

这个时候已经顾不上线上系统服务会不会崩了,先和对方对完线再说。

等都禁止的差不多了,我才去看操作日志,看一下对方都做了些啥,看看有没有什么损失,赶紧处理或者修复。

然后我就看到,对方一开始在使用我的RAM账号的AccessKey去试探访问各种账号或者云资源的API接口,然后就用这个Accesskey自己创建了一个高权限RAM账号。

因为我当初为了省事,直接给我自己的RAM账号赋予了主账号几乎所有权限,然后在创建AccessKey时,不想一个一个勾选,索性也直接赋予所有权限了。

4、阿里云🐂🍺

但是庆幸的是,当对方创建了高权限RAM账号之后,触发了阿里云安全限制,直接禁止这个主账号下所有RAM账号登录。所以我看到了对方登录自己创建的RAM账号的控制台时,提示操作失败。

因为用一个RAM账号的AccessKey去创建另一个高权限的RAM账号,这个接口不仅是高敏结构,这个操作本身就很反常,因为创建RAM账号基本都是主账号或者在控制台操作,几乎很少会是RAM账号的AccessKey去创建另一个高权限的RAM账号。

主要是此时,我们并没有购买阿里云的安全服务和防火墙之类的安全产品,纯阿里云自带的免费基础安全服务在顶。

此处我只能说:阿里云🐂🍺

我的RAM账号也是被阿里云禁止登录的,直接禁止了所有RAM账号登录控制台,所以黑客没有成功登录自己的RAM账号,但是RAM账号下属的AccessKey还能用,所以我直接把泄露的AccessKey给禁用了。

5、排查处置

然后经过我细细的排查,发现黑客只进行了一些查询操作和创建账号的操作,并没有对云资源和其他东西做出增删改的操作。

黑客是使用徐州的一个代理服务器进行入侵的,IP地址:49.68.57.93。

虽然我这边阿里云限制了常用登录地址是徐州,结果对方的IP地址就是徐州,这个正好放过去了。



此时,各项目服务还处于停服的状态。

所以我赶紧更换项目的AccessKey为自己新申请的AccessKey,先恢复项目服务再说。

我把所有项目的已泄露AccessKey都换成新的AccessKey并恢复运行之后。

6、入侵原因解析并处理

我开始排查泄露原因,不然对方还是能通过泄露渠道再次拿到AccessKey的。

因为之前简单排查过,大概率是从测试服务器或者生产服务器泄露的。

这两个服务器的账号密码,我们保护的也挺严格的,应该不是账号密码问题。

所以我就先从漏洞方面考虑。

果然在排查期间,发现对方就是从测试服务器的Redis的一个漏洞入侵的,然后一步一步提权,然后访问系统里的jar文件,解析到里面的AccessKey,才导致的AccessKey泄露的。

我看了一下,服务器里部署的Redis Server版本是6.2.6,这个版本有一个高危漏洞,可以通过漏洞逐步提权到root账号级别。

果然,我赶紧把测试服务器和生产服务器的redis版本从6.2.6升级到6.2.20(安全版本),之所以没有升级到最新的安全版本,是因为老项目了,能不动就不要大动,小版本升级更保险一些。

然后我在Redis版本升级之后,还检查了一些服务器里有没有新增异常的账号,以及root近期登录日志啥的,以及那个入侵的IP地址在服务器有没有其他操作啥的,结果没有发现异常。

我想着此次危机应该是度过了,就结束了运维。


第二次

本来我以为入侵事件就这么结束了,毕竟我已经把漏洞都给修复了,而且排查了对方没有在服务器里留东西,测试和生产服务器的root的密码我也换了。

结果打脸来的如此之快。

因为我的懒惰,并没有做过多的防御设置,结果就是再次被入侵了。

1、再一次被入侵,破防

时间发生在2026年03月21日,那天是周六。

估计是对方想着,这天是周末,应该不会被发现的那么快,即使发现了也不会处置的那么快。

但是黑客应该没有想到,我主职是上六休一,我那天上班,所以接到老板的消息之后就立即着手处置。

想利用我休息时间偷袭我,没想到我周六也上班吧,哈哈呜呜呜呜~

我特么周六上班,还被黑客入侵。

焯!毁灭吧。

言归正传。

我发现又是通过这个RAM的AccessKey泄露导致的,而这个AccessKey是前两天刚换的新的AccessKey。

我擦,怎么又泄露了。

2、紧急排查

有了上一次经验之后,我在他还在进行查询一些权限接口的时候,第一时间封禁了这个AccessKey。

项目停就停吧,先保命。

我禁掉AccessKey后,就开始着手排查,是不是服务器还存在漏洞,结果发现没有了。

因为我其实对运维,特别是安全方向的运维并不熟悉,所以我让AI给我生成一堆全面检查CentOS系统的检查入侵的命令,我还把黑客的IP地址也给AI了。

然后我使用AI给我的命令一条一条的排查。

果然发现了端倪。

在测试环境的MySQL中,发现了这个IP地址的几条长链接。

淦!

他没在系统里留后门和木马,他在MySQL里留了长连接,然后潜伏几天之后悄悄摸摸进行账号提权,再一次拿到了新部署的jar包,解析到了新的AccessKey。

淦!

上次升级了Redis的版本,检查了服务器层面,修复了漏洞,结果偏偏漏掉了MySQL,其实也不算漏掉,我想着MySQL版本是安全的,所以就没在意。

3、紧急处置

我赶紧把所有异常的MySQL链接全部杀死,然后更换MySQL的密码和端口。

又排查了一遍测试服务器里所有的异常链接、定时任务之类的可能留后门或者木马的地方。

而且这次我直接在测试服务器的防火墙里封禁了这个黑客的IP地址。

我不知道我的RAM账号本身是不是也出现了问题。

我直接问老板要来了主账号,又重新创建一个RAM账号,把之前的RAM账号连带泄露的AccessKey都删除了。

然后用新的RAM账号,重新申请了AccessKey。

这次我留了一个心思,我创建了两个AccessKey,然后分别用在测试环境和生产环境的项目配置文件里。如果万一后面再出现泄露,我好知道是哪个服务器泄露的。

然后在项目打包构建jar的时候,让不同环境仅能把自己环境的配置文件打进去,其他环境配置文件不会打进jar包。之前我都是直接打,一个jar里包含所有环境的配置文件。

4、自我感觉良好

然后我感觉我应该处理完了。

排查了Redis和MySQL、排查了系统层面的链接和日志、更换了RAM账号和AccessKey,并且分环境打包。

于是我结束了这次的处置。

但是世事无常,大肠包小肠。


第三次

时间是2026年03月23日上午,周一。

仅仅过去一天,接到老板的消息,新RAM账号的新AccessKey再次泄露。

MD!

没完了,盯着我打。

我上RAM账号,查了一下,是生产服务器的AccessKey泄露的。

我特么,淦!

他是怎么进到生产服务器的。

1、全面处理

倒不是说生产服务器是内网,而是jar包里就没有生产服务器的IP地址,后面我才想明白,可能是之前黑客通过访问阿里云的API查询接口,拿到了云服务器的信息,所以才被入侵的。

上次我只封禁了测试服务器的防火墙对这个IP的禁止访问,忘了生产了,想着他也没有入侵成功生产,生产服务器的MySQL、Redis、系统日志都没有显示被异常访问过,没有异常链接,就忘记封禁了。淦!真的一点懒都不能偷。

我又第三次的封禁了AccessKey,我担心另一个测试服务器用的AccessKey也泄露了,索性两个都禁用了。

因为,一个RAM账号只能同时有两个AccessKey,我又申请了一个RAM账号,用新RAM账号申请的两个AccessKey重新替换。

在替换之前,我还做了一些操作。

  • 关闭阿里云生产服务器的MySQL的远程连接;
  • 关闭阿里云生产服务器的Redis的远程连接;
  • 在阿里云的安全组里限制测试服务器MySQL的端口访问IP仅为我当前所在的动态IP;
  • 在阿里云的安全组里限制测试服务区Redis的端口访问IP仅为我当前所在的动态IP;
  • 在阿里云的安全组里限制生产服务器和测试服务器的22端口访问IP仅为我当前所在的动态IP;
  • 对最新RAM账号新申请的两个AccessKey做网络访问限制,用于测试的AccessKey只能通过测试服务器IP访问,用于生产的AccessKey只能通过那几台生产服务器的IP访问。
  • 在测试服务器和生产服务器,全面禁止黑客的IP地址访问;
  • 更换测试服务器和生产服务器的MySQL和Redis账号密码;
  • 把测试服务器和生产服务器的root账号的SSH连接登录方式,从账号密码改成公私钥证书登录,然后禁用账号密码登录;
  • 把测试项目和生产项目的配置文件中的AccessKey都做成了环境变量,Key值存放在系统的文件中,项目本身不自带密钥;

这样,直接进行了AccessKey的固定IP访问、服务的指定动态IP访问(我每次想要远程连接这些服务,都要登录阿里云账号进行当前所在IP地址的临时授权)、服务器登录方式的变更。

说实话,第三次的AccessKey的泄露,我并没有查到从哪里泄露的,实在是没有一丁点头绪,但是我把能封禁的或者能限制IP的地方都限制了。

现在即使黑客拿到了AccessKey也无法进行任何操作,因为这些AccessKey只接受来自我允许的服务器IP地址的访问,而服务器层面也做了加固和防御。

然后我还针对我的RAM账号进行了权限收缩,解除了一些不必要的高权限授权。

所以,这次应该能真正的结束了…吧。

真的,心累了,真不能偷懒。

我之前就是想偷懒,不想每次登录服务器或者使用服务都要登录阿里云进行IP授权,才直接放开所有IP都可访问的,大忌啊!


入侵事件后续

今天(2025年04月01日),项目的实际权属方,我们地方的某市政单位。

今天单位人员联系我,说市局技术发现这个系统存在漏洞,可以无权限下载服务器内任意文件。

然后发了一个入侵拿到各个权限的截图。

我擦,感情是主管部门进行的渗透测试。。。

我TM服了。

我这边赶紧搜索了一下,结果发现是若依框架早期版本的一个漏洞:CNVD-2021-01931任意文件下载漏洞。

这个项目的代码都是上古的了,所以这个漏洞是存在的,怪不得每次都被拿到jar包。我还以为是入侵了服务器后一步一步提权拿到的。

不过我第三次的处理方案能杜绝对方在拿到jar之后的进一步入侵,只是这个漏洞我当时没有发现,怪不得对方能一而再再而三的入侵成功。。。

我刚要把问题给修复了,结果老板告诉我,这个系统早就超出了我们运维的合同期,现在已经不需要我们运维了。

额…

我还能说什么呢?

我只能说:

淦!

喜欢记录一下我因为懒导致六天被入侵三次的经历这篇文章吗?您可以点击浏览我的博客主页 发现更多技术分享与生活趣事。

  •  

局部有雨

春天到了,又到了万物复苏的季节。小草也从土里钻出了头,露出了点点的绿色。树上的花也开始绽放,虽然没有绿叶的衬托,倒是也别有一番韵味。

每个周末,都大同小异。跟上班一样,也没有什么特别的,与渐渐生机勃勃的春天比起来,似乎有些过于平淡了。每周依然是带着宝子上课,上课,剩下偶尔有那么一点时间,也不能走的太远,只能周边小范围的溜达溜达。

宝子现在上课不太需要自己去帮忙了,至少能隔网之后,不用自己跟着去抛球了。这样就有点时间可以继续跑步了,围着网球场一圈一圈的跑。

不过相比操场的400米跑道,这一圈一百多米跑起来感觉更累,感觉跟拉磨的驴一样。

周日上午的网球课推迟到了十一点,早上早点起来,宝子嚷嚷着去公园,我嚷嚷着去轮渡。当然,公园是上午去,轮渡是下午去。对象再次征求宝子的意见,得到的答复是两个都去。

急忙吃完东西,开车去公园。上车之后发现前挡风玻璃上面有点雨点,想着是不是要下雨,跟宝子说,下雨了可能。她表示怀疑,没感觉到雨点啊。不过既然没感觉到,那就去公园吧,一路上虽然天依然阴沉,刚停好车,拉开车门的瞬间,一阵大雨落了下来,赶忙重新钻进车里。前后不到五秒中的时间,就让这场公园之旅化为了泡影。

回家之后,宝子在家换上轮滑鞋,来回溜达。想着,既然下雨了,应该网球课也停了吧,然而等到十点半依然没消息。既然如此,那可能市北没有下雨吧。一路上天变得越来越晴朗,到了学校之后依然是一篇艳阳天。

下课去学校餐厅吃点东西,下午就可以去轮渡了。

虽然是中午的饭店,想找个空位置还是蛮方便的,买了两碗麻辣烫,没有小碗。去隔壁的卤肉饭要了个小碗。餐厅门口从外面能感受到吹进来的真真的凉风。吃完饭,到了室外反而变得更加的温暖,与餐厅的温度完全不同。

一路小道辗转到环湾大道,路况还是比较畅通的,半个小时就到了轮渡停车场。怕找不到停车位,在入口找了个空塞了进入。往里走,大约100米发现内部竟然有一个大的停车场,并且还有大量的空位。

候船厅入口,写了一些方言的标语。

虽然购票的时候显示有场次,但是实际买的时候,就是看人数,人够了就发船。感觉到了县城做镇上的客车的感觉,流水车,流水船。检票的时候会给一个油条,这个油条并不是给游客吃的,是给海鸥吃的。

这个码头,在海底隧道和跨海大桥开通之后就已经停了好几年,这两年才又重新开放,用来做海上观光。成人票两个人90,宝子2块,价格还是蛮合适的,海上50分钟的观光,说可以看到跨海大桥。

检票上船,刚找地方坐下,就有工作人员开始喊,大家可以去喂海鸥了。喂海鸥只能在一楼,二楼是观光区域,油条投喂完之后可以上二楼。带着宝子来到船尾,大家都在那里观望,也不知道在等什么,我扔了一块油条出去之后,马山就有无数的海鸥飞过来开始争抢。大家也跟着开始往海里投喂。

发船之后,海鸥依然跟着游船,甚至能感受到海鸥翅膀煽动的时候,甩出来的水滴。

还有跟着游船一直来回盘旋,不断的争抢从游客手里抛出的油条。

当然啦,如果足够大胆,也可以直接用手拿着让海鸥去吃。

不过,这天气稍微微差点了,大约半小时不到,就开始掉头返航了。说好的看跨海大桥呢?海面的能见度并不算高。只能隐约看到一个轮廓,远处的货轮也一样,飘渺。

回港之后,依然能看到很多人在等着检票。如果五点多应该能看到海上的日落,很多人也在等那个最好的时间。

海上的冷风吹来,单穿一条牛仔裤还是冷了点。

好在没有因为家里的一场雨而不出门,毕竟其他的地方是一片艳阳天,不过局部有雨。

  •