跳到主要内容

新年新气象:网站快一点,再快一点

· 阅读需 3 分钟
林林
在西安读大二的小菜鸡

WordPress 如果没有缓存,就会加载得超级慢。此前,我在网站上装了 Jetpack Boost、WP Super Cache 等插件。昨晚看到 此前发的文章,发现还能装一个 Redis Object Cache。

插件装得越来越多,我问 ChatGPT 有没有必要装这么多插件,它向我推荐了一种方案。如今,网站的访问速度基本得到了改善。秒开估计还是有些困难。

测速全绿(全部深绿还有点难)

Fastcgi Cache​

WordPress 上的缓存插件都是在 PHP 运行的过程中起作用的,Fastcgi 缓存可以将服务器的缓存前置到 Nginx,如果 Nginx 匹配到缓存文件直接返回,请求不会给到 PHP。

在站点的 Nginx 配置文件伪静态部分后面加上如下片段,其中加粗部分为缓存的有效时间。

set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($http_cookie ~* "wordpress_logged_in_") { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/wp-login.php|/xmlrpc.php") { set $skip_cache 1; }
if ($args != "") { set $skip_cache 1; }

location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/tmp/php-cgi-85.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

fastcgi_cache WORDPRESS;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_valid 200 301 302 30m;
fastcgi_cache_valid 404 1m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;

add_header X-Cache $upstream_cache_status;
}

在 Nginx 的全局配置文件中加上如下片段。

fastcgi_cache_path /www/wwwroot/fastcgi_cache levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

Gzip 和 Brotli 压缩​

知道创宇的加速乐本就有 Gzip 自动压缩的功能,默认开启且无法关闭。皓子的 静态资源预压缩:零运行时开销,极致节省带宽 这篇文章给我提供了一种可能的优化路径。因为原先安装的 Nginx 并没有带 Brotli 模块,所以我卸载掉重新编译安装了一个。

然后 apt 安装 Brotli,在网站目录里搜索 js、css 文件并生成其对应的 gz、br 文件。

随后,在 Nginx 站点配置文件中添加关于 Gzip 和 Brotli 压缩的设置。

brotli on; brotli_static on; brotli_comp_level 5;
brotli_types text/plain text/css application/javascript application/json application/xml text/html image/svg+xml;

gzip on; gzip_static on; gzip_vary on; gzip_comp_level 5;
gzip_types text/plain text/css application/javascript application/json application/xml text/html image/svg+xml;

但可能是因为 CDN,我并没能测试到服务器返回 br encoded 的内容。

WordPress的插件​

弄好上面的缓存和压缩之后,把 WP Super Cache 插件移除掉网站访问起来也不会太卡。目前网站正在用这些优化速度的插件:

插件名用途
Advanced Database Cleaner清理修订版本等垃圾数据
CompressX提供图片的webp和avif版本
Redis Object Cache网站目前使用的唯一缓存插件

2026.4.28更新:CompressX压缩图片之后使用WordPress自带的图片灯箱功能会出现异常,放大的图标能显示出来,但放大不了。Fastcgi在一些情况下会出现异常缓存,而且用宝塔服务器面板的环境下改Nginx配置文件会改乱,这可能是我个人能力的问题。

HGAME 2026 WriteUp

· 阅读需 6 分钟
林林
在西安读大二的小菜鸡

很多题不会做,等题解出来在这里一并记录自己当时做题的思路和照着题解做题的过程。

一周目​

魔理沙的魔法目录​

观察网络控制台
看控制台发现网页每过一段时间向/record发请求(通过Authorization Header 来鉴权),再通过/check检查是否达到足够的时间。tracker.js被混淆了,就是一个黑盒。用 Postman 试试看。

通过 Postman 给接口发请求
接口并没有限制 time 的大小,那我们填大一点,发完请求在浏览器等一会 flag 就出来了。

答案自己就出来了
但如果挂机等一个小时的话应该也能做出来。

Vidarshop​

我自己没做出来这一题。

什么你这卖的东西这么贵 什么为什么都用uid了用户名还要抢啊, 什么凭啥admin可以管我们所有人的钱啊

题目的题干

update接口直接改的好像是User类的balance属性欸,但是User属性中balance似乎并非。。。该怎么修改balance呢

题目的提示

一开始看题目和登陆页面看感觉像是 SQLi,要直接登录到 admin 账号去管钱。注入试了两次之后发现能登录,登陆进去发现鉴权用的是 Bearer Token,token 为 JWT,用空密钥试了一下不行。然后对着/api/update接口调了半天,发现没用,睡觉。

第二天醒来接着看这道题,注册了一个用户名和 uid 都是 111 的账号,对着 JWT 解析出来的结果发呆。不知道想到什么了,试着把 111 输到密钥里,验证成功…是哪个地方有问题吗?为什么就验证成功了?我将 token 拿到工具里爆破了一下,工具给出结果:密钥还真是 111。

此时的我欣喜若狂
我猜想密钥应该和 uid 是一样的,因为 uid 也被放在 header 传给接口了。拿其他账号一试,我猜错了:是所有账号生成的 JWT 密钥都是 111 啊。这样就可以构造一个 admin 的 token 了。

然后用这个管理员的 token 在题目环境里试了试,知道了这些信息:

  • balance 貌似是所有用户的共有属性,前端的接口是假的(貌似就算用 admin 权限也无法修改,还没找到题目说的 admin 管钱是怎么管的)
  • 应用好像用 uid 来识别是否为管理员,ctf-token 来识别用户名(并且没有检验用户是否存在的逻辑)可以通过在接口请求的 body 里指定 username 来显示某个用户的 balance(虽然大家都共用同一个)
  • buy 接口能使 balance 实实在在地变少,但好像利用不了(没办法指定花的钱数,自然无法改为负数)

我又试了试,还是没有试出什么名堂来。题目归档之后,CopperKoi 同学把题解发给我,是我没见过的原型链污染。大概就是用前面伪造的 token 发请求到 /update 接口,payload 如下:

{"__init__": {"__globals__": {"balance": 2000000}}}

看样子题目确实是这么解的。

二周目​

easyuu​

很早以前玩虚拟空间的时候有部署过一个 PHP 文件就实现文件上传、查看、登录等功能,给我当时幼小的心灵带来一些震撼。

貌似发现了一个可以利用的接口
“uu是什么意思,很简单吗,分开来想想你就明白啦”,题干是这样说的。从同学口中得知,uu 是 upload & update 的意思。他还说,做题首先应该尝试拿到源码。我在题目环境的/app/update里发现了一个压缩包,像是源码,想着用download_file接口进行目录穿越下载下来又不行,浏览器好像会把../和链接的上一级进行“消消乐”。

要不要encode一下试试?
然后我就把源码的压缩包下载下来了。打开一看,Rust Cargo!

我将代码喂给 GitHub Copilot 和 Deepseek,Deepseek 给出一种可能的攻击方案:可以先在本地加恶意代码并编译,再通过上传接口将文件上传到/app/update(又是一个目录穿越),等代码自动更新之后拿到 flag。

import requests

url = "http://环境地址/api/upload_file"
files = {
'file': ('../../../../app/update/easyuu',
open('target/x86_64-unknown-linux-musl/release/easyuu', 'rb'),
'application/octet-stream')
}
response = requests.post(url, files=files)
print(response.status_code)
print(response.text)

但是没成功。上传文件这个接口的目录穿越可行。但自动更新这个机制能否利用不清楚,本地编译了几次,一直cargo clean cargo build --release --target x86_64-unknown-linux-musl,文件大小都是553928。估计编译出来的一直是老代码。

baby-web?​

读附件的代码之后发现应用并没有限制 php 文件的上传,所以上传了一个一句话木马上去,然后用蚁剑连接。结果发现上传附件的文件夹只有我上传的文件,flag也不在文件系统当中。

HarmonyOS 6初体验

· 阅读需 3 分钟
林林
在西安读大二的小菜鸡

晚上,咖啡豆子突然问我:“你手机用的是华为吗?有升级 HarmonyOS 6 吗?”起初注意过设置里有升级鸿蒙 6 公测版的选项,但没关注。如今被问到,才在网上看看,群里问问。去年春节买的手机如今也可以用上鸿蒙操作系统了。

从安卓的鸿蒙 4 升级到鸿蒙 6,升级前我有一些顾虑:

  • 应用数据会丢吗?
  • 学校要求的软件能否兼容?
  • 没有备案的软件可以使用吗?
  • ……

咖啡豆子晚上因为忍不住好奇就先更新了,我早上找了一些回答,问咖啡豆子,明确了一些信息:

  • 在更新之前系统会进行数据备份,开机后使用微信会自动回复聊天记录,软件数据不会丢。
  • 暂不支持鸿蒙的应用会变成“卓易通”版的应用,软件数据也不会丢,功能也都能正常使用,再也不怕把 2Fa 软件里的数据丢掉了。
  • 鸿蒙操作系统的应用商店有我们学校使用的定制款学习通,至于能不能签到就要开学之后试。

早上,带着一些忐忑,我点下那个更新按钮。手机自己进行下载、备份、安装,整个流程用时不短,备份时还不能切屏,好在我没有什么要用手机处理的事情。接近中午,手机才向我展现出“鸿蒙版”的新面目。

新系统,微信、QQ、支付宝…几乎所有应用都需要重新登录。鸿蒙最开始惊艳我的一点是:输入法在将短信收到的验证码填进去之后会随即把短信标为已读并划去通知。这个体验点真的很小,虽然我手动把消息划掉也用不了多少时间,但真方便不少。

临近中午用网易云音乐听歌,虽然网易云音乐还是安卓版,但体验上优化不少。我不需要进入网易云音乐就可以实现音乐的操控。比起此前将音乐控制放在控制中心,这样我连下划调出控制中心的步骤都省了。

感觉像是苹果的灵动岛?
听说 QQ 和微信少了很多功能,如今用了一天,并没有感觉到什么不方便。微信的消息通知还是跟安卓时的一样,估计是我禁止自启动了。欸,鸿蒙 6 好像连应用启动管理的设置都没有了(都是系统来管的嘛)。

下拉呼出通知栏,解锁手机,滑动退出这些操作都很丝滑。在鸿蒙用安卓应用(比如 Chrome)感觉有些问题,我在编辑文章的时候经常触发黑屏。

目前总的来说,鸿蒙系统惊艳了我,给不时要输验证码的我方便不少,流畅的使用体验也让我舒心不少。

云上

· 阅读需 2 分钟
林林
在西安读大二的小菜鸡

建议配合听蒼鈺Danielle翻唱的《归途有风》
晚上从飞机上看
此前来西安坐的是傍晚的飞机,飞在空中只能看到地面上的灯光,灯光连成一条一条路。有些城市像是依山而建,有些城市则从中心向四周放射状伸展。

起初一片黑,什么都看不见。突然出现地上发出的灯光,我们便伸向舷窗看。有时看得清楚,还能看到路上的车灯。

白天,白白的天。从西安回来的飞机是白天的。云多,天又亮,我能从舷窗看到清晰又细腻的云层。云层望不到边。小时候说云像棉花糖一样,看上去好像真的和棉花糖一样软。云多是好,我能看看此前没见过的云层;云多不好,我还没在白天看过高空下的城市呢。回到家之后,我将我在飞机上拍的照片和视频分享给我的父母。他们坐飞机回来时是深夜,看到这些照片时颇为震撼。

回来坐飞机用不到三小时,却隔五个月。

短聚,长离。

西电信安协会招新系统 Golang 后端开发小记

· 阅读需 6 分钟
林林
在西安读大二的小菜鸡

大概是,今年 1 月份与协会的 CopperKoi 同学把协会的招新系统重新做了一版。CopperKoi 用 Vite 写前端,他和我用 Golang 写后端。后端用 gin 处理请求,用 gorm 对接数据库。文章到这里就结束了。

招新系统公测截图

接下来是废话时间。

CopperKoi 真的是一个很厉害的大佬。

吹,接着吹

迎新页面(一般每年由大一同学建设)

引自某个文档

大概就是这样,今年我们在使用的招新系统有一些功能需要改进,但我找不到代码仓库,协会服务器里的站点文件夹里赫然堆着 Flask 大代码,明显不是今年用的这个。又听学长说历来都有大一新生写招新页面的传统,就来了。

CopperKoi 此前跟我聊过前端。当我问他愿不愿意带飞我做一个招新页面时,他欣然答应了。此前写过一些关于流程和数据库的设计图,CopperKoi 也写了接口文档,开整!

Golang 写完之后可以直接部署成二进制文件,这一点比 Python 方便许多。虽然能做到这一点的语言很多,Python 也有 Pyinstaller,不过没用过。

对面试者的面试流程

对面试官的面试流程

数据库模型,很古早的流程和数据库设计

我们在开发的过程中有一些迷,这里记录一下。

前后端分离了怎么用 X-CSRF-Token 来防 csrf:好像光想着要放 csrf 了,但我们不是前后端分离了吗?😅后来 CopperKoi 想到前端把 JWT 放在 Authorization Header 不依赖 Cookies 就可以让 csrf 几乎不可能实现。

腾讯的 CodeBuddy 写代码是真的快。我刚开始边翻文档边写代码速度好慢,把文档交给 ai 没过多久路由就都写好了。目前公测没有报告出 ai 代码的问题(只有我灵机一动犯的错)。但是 ai 写代码和我一样有时会出现想当然的错误。

ai 的幻觉(幸好我略微熟悉代码)

密码怎么传输? 我原本以为在后端 bcrypt 的基础上前端用 sha256 加密之后传输会安全一点,并且将这个设计补充到文档里。但当我问 Deepseek 时,它给出了不同的意见(聊天链接)。“sha256 碰撞都来了。”CopperKoi 经过一番研究之后采用了 Deepseek 的建议,传输明文密码,传输过程中的安全由 https 来保障。

没过几天,CopperKoi 的前端也写好了。测试的时候公告的置顶接口出现了一个很奇妙的问题。

接口原本用来绑定 body 的模型

接口通过 Params 获取要置顶 / 取消置顶的公告的 uuid,通过 ShouldBindJSON 函数将 body 绑定到模型上。Pinned 布尔值为真则置顶公告,为假则取消置顶。

但是我在测试的时候发现:置顶公告的功能可以跑通,但取消置顶都会报告“参数校验失败”的错误。欸,明明 body 的结构符合要求啊。然后,就出现了惊天大瓜。

看到这个回答我脑子都宕机了

六百六十六。我和 CopperKoi 都看呆了。当我们把这个发给学长时,他给我们分享了这个。

When I send a JSON request with the value true it works, but when I send the same request with false I get the following error.

In summary you need a pointer or custom type to know if the value exists due to Go's static nature.

Bool binding required Bug · Issue #814 · gin-gonic/gin

然后学长跟我们介绍了一件事情。

对于一些可以 nil 的字段而言,静态检查十分重要。GORM 的默认主键自增是从 0 开始的,不是从 1 开始,而你初始化系统用的第一个账户大概率是 admin 账户,账户 id 大概率也是 0。如果因为某些 bug,鉴权中间件没绑上 id……所有人都是 admin 了,而且 go 不会有任何错误汇报。

寄。但我们主键用 uuid 好像把这个坑绕过去了。(不然高低也要踩一回)

给 CopperKoi 提建议的时候我可得劲啦!我给 CopperKoi 提了:将性别从 input 改为单选框、textarea 没有限制宽度可以拉出容器边框、简历显示不出来等等…我们改、测得差不多之后就把代码部署上去让协会里的人公测。

在公测之前,我灵机一动:要不把里面能提的东西都提到 env 里面吧,把参数硬写在代码里不太优雅。然后我看到了 JWT 的 secret…让程序从 env 里面获取 secret。我真是个甜菜!(操作没错,但我的环境变量是用 .env 设置的)

想当然地让程序从 env 里获取密钥

在公测的那天晚上,服务就被学长攻破了。

学长通过修改 JWT 越权访问面试官才能访问的接口

学长通过 gpt 发现的。

jwtSecret =[]byte(os.Getenv("secretKey")在包初始化阶段执行,而 env 是在 main.go 里godotenv.Load()才加载;结果 jwtSecret 可能为空字符串。攻击者可用空密钥自行签发 JWT,伪造 role=interviewer 直接接管所有受保护接口。

gpt如是说

把 secret 硬编码在代码里就把这个漏洞 fix 了。

CopperKoi/XDSEC-Recruitment-System:协会2026招新系统前端
Xiaozonglin/xdsec-join-2026: 西电信安协会2026招新系统后端