Logo

site iconyzqzss | 一座桥在水

RSS和博客研究,倡导 声冻计划、Save The Web Project(STWP)。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

yzqzss | 一座桥在水 RSS 预览

CSSWAF: Browser Detection Using CSS (No JavaScript Needed)

2025-03-24 19:26:00

Inspired by Anubis last week, I came up with an idea to detect if a client is a browser using CSS animations and image lazy loading. I spent a few hours writing a simple PoC, and it works!

How it works?

CSSWAF places random hidden empty.gif images in CSS animation progress, allowing the browser to play these images one by one in order.

...
<style>
@keyframes csswaf-load {
  ` + func(expectedSequence []string) string {
        lines := []string{}
        for i, img := range expectedSequence {
            f := float64(i) / float64(len(expectedSequence))
            lines = append(lines, strconv.Itoa(int(f*100))+`% { content: url('/_csswaf/img/`+img+`?sid=`+sessionID+`'); }`)
        }
        lines = shuffle(lines)
        return strings.Join(lines, "\n")
    }(expectedSequence) + `
}
.csswaf-hidden {
width: 1px;
height: 1px;
position: absolute;
top: 0px;
left: 0px;
animation: csswaf-load ` + strconv.FormatFloat(cssAnimationTS, 'f', -1, 64) + `s linear infinite;
}
</style>
...
<div class="csswaf-hidden"></div>

The backend measures the loading order. If the loading order is correct, it passes the request to the target server. Otherwise, 🙅.

...
// Check if sequence matches expected sequence
expectedSeqTTL := st.expected.Get(sessionID)
var expectedSeq []string
if expectedSeqTTL != nil {
    expectedSeq = expectedSeqTTL.Value()
}
if expectedSeq != nil && len(sequence) == len(expectedSeq) {
    match := true
    for i := range sequence {
        if (sequence)[i] != (expectedSeq)[i] {
            match = false
            break
        }
    }
    st.validated.Set(sessionID, match, ttlcache.DefaultTTL)
...

HoneyPot

CSSWAF places some honeypot empty.gif files in HTML <img> tags but instructs the browser not to load them. If someone loads the honeypot GIFs, 🙅.

lines = append(lines, `<img src="/_csswaf/img/`+img+`?sid=`+sessionID+`" style="width: 0px; height: 0px; position: absolute; top: -9999px; left: -9999px;" loading="lazy">`)

CSSWAF also places some unvisible <a> tags in HTML, if someone clicks the honeypot links, 🙅.

.honeya {
    display: none;
    width: 0px;
    height: 0px;
    position: absolute;
    top: -9898px;
    left: -9898px;
}

lines = append(lines, "<a href='/_csswaf/img/"+img+"?sid="+sessionID+"' class='honeya'>View Content</a>")

运行多用户 FreshRSS 实例的一些经验

2024-03-10 06:45:00

运行多用户 FreshRSS 实例的一些经验

一般项

常规的优化做做就可以了,另外 PHP 越新越好。

另外由于 FreshRSS 为每个用户创了两张数据库表(一张 article 一张 feed),所以数据库的 opened tables 会比较高,但也不用太关心这个指标,毕竟我们不可能有这么高的并发。

  • 推荐开启 WebSub。
  • 记得订阅 FreshRSS 的 GitHub Release
  • 将 ./p 作为网站根目录,不要把 FreshRSS 的根目录设为网站根目录

FreshRSS 升级

FreshRSS 升级时会把 ./data 复制一份到 ./data.bak ,这本身没啥问题。但是在我们的多用户实例上,./data/cache 轻轻松松就会积累起几万十几万个缓存文件,毕竟订阅源数量非常多。建议在升级前 cd ./data/cache && rm *.spc *.html 清理缓存。以免升级时给 ./data 打备份过久而 PHP 超时。

如果你有备份 crontab,记得排除 ./data/cache./data.bak

新用户默认订阅源

./opml.default.xml 默认是 FreshRSS 的 GitHub Release。

新用户默认配置

创一个 ./data/config-user.custom.php ,内容如下:

<?php
return array (
        'ttl_default' => 14400, # 最小 4h 自动刷新一次
        'dynamic_opml_ttl_default' => 43200, # dynamic opml 的刷新频率
);

可用参数见 config-user.default.php

让用户不能把更新频率改得太高

app/views/configure/archiving.phtml 里的几十分钟刷新一次的选项删掉,我是只留下了最快 4h 一次的选项。注意更新 FreshRSS 后要重新删掉。

禁用不活跃用户的自动更新

调整 ./data/config.phpmax_inactivity 值。

并行刷新订阅源优化

在 1.22.0 后,FreshRSS 给 app/actualize_script.php 加了进程互斥锁。这玩意极大拖慢了刷新一轮订阅源的效率。虽然单个刷新线程会5并发刷新 feed,但还是不够,而且一些 feed 死链,默认 20s 才会 timeout,又拖慢了全局刷新速度。

建议加下这个 patch,让你的 app/actualize_script.php 可以并行运行。

42c42
< $mutexTtl = 900; // seconds (refreshed before each new feed)
---
> $mutexTtl = 10; // seconds (refreshed before each new feed)
49c49,51
<       die();
---
>       // die();
> } else {
>       fclose($handle);
51d52
< fclose($handle);

不用担心并行刷新会导致冲突啥的,因为 feed 级别上也有锁,所以多个进程不会傻乎乎地同时去刷新同一个 feed 。

我是每 3 分钟起 9 个刷新进程,速度飕飕地。

for i in {1..9}; do sudo -u www-data /usr/bin/php /var/www/rss.othing.xyz/app/actualize_script.php & done

另外还可以启用 AutoTTL 插件,根据每个 feed 的实际更新间隔来自适应调整更新频率。不过你需要自己 patch 一下,把它改成系统级插件,而不是用户级插件。

管理插件

FershRSS 的插件手动安装和更新很麻烦。

为了方便,我做了个“FreshRSS 插件包管理器”——yzqzss/freshrss-ext-manager,妈妈再也不用担心我装的 FreshRSS 插件过时了。

备份

不推荐用 FreshRSS 自带的 cli 工具导出 sqlite dump,因为它会重排序 feed_id。另外,FreshRSS 的 Mysql/PostgreSQL 里存的 RSS 文章内容是压缩了的,导出成 sqlite dump 会解压,所以 dump 极大。

总结

经过一波优化下来,估计至少容纳 1k 用户应该没啥问题。

便利店的一元——回忆之8

2023-09-06 00:02:00

9/10 年前(具体时间记不清了)我还在读小学的时候,有次回家路上发现没带钱坐公交车,而同行的小伙伴们也正好没有多余的一块钱。于是我们合计着可以找公交站旁的一家便利店老板借借钱。

我也确实以“忘带钱要坐车”从老板手中借到一元。(那时候重庆还存在 1 元钱的公交车路线)

而后……可能出于害羞的心态??我不懂我自己,这一块钱一直到毕业也没还给便利店。那之后同行的同学其实有问过我有没有还,我撒谎说“还了”。此后我是路过就绕着这便利店走,不敢进去买东西,时间拖得越久,我越不敢进去还了。

是时候给这个故事画上个句号了,我打开地图,便利店还在,找到电话,添加微信。老板没同意好友申请,于是我在申请上留言说明了这件事。

“你不用记得,往前走越来越好就行”
“不用还了哈”
“有老人和孩子,生活中的这些普通的事早就记不住了”(由于微信只保留最近三条留言,这条我没截图到,但大概是这个意思)
  • 我:“嗯,谢谢。XX小学也改名了。”
  • 我:“祝好!”
“同祝安好”

#回忆 #小卖部没有还钱 8/n

轮胎作玩具,铁环的消失——回忆之6~7

2023-09-06 00:00:00

我在小区的废楼里找到一个摩托车轮胎。我把这个轮胎当作玩具,天天在小区里外滚上滚下,掌握了一番手刹、侧转等技巧。连找小伙伴玩我都带上这个轮胎。🌚

然后某天,因超速失去控制,这轮胎一头扎进沟里,Game Over,之后我就没轮胎玩了。🌚🌚

#回忆 #轮胎作为玩具 6/n


轮胎没了之后,我想玩玩正经的铁环。又过了好久,那时候淘宝开始普遍了,于是我让我爸上淘宝买了个。

说起来真悲催,到现在我在城市里也没看见哪个小孩/大人玩铁环。倒是有很多大人看到我在玩,就借过去滚两下过过瘾。

现在那铁环已经找不到了,不知道放哪儿了。

我觉得这项娱乐有复兴的潜力。🙈

#回忆 #铁环 7/n

盐——回忆之5

2023-09-05 23:58:30

我发现了个神奇的事情,把厨房里那种白色的粉洒在阳台的铁栏杆上,然后舌头舔,它有咸味的!

说起来我好像当时还给表姐分享了这个发现?以及,为什么当时我要把盐放在铁栏杆上再舔?

#回忆 #味道 5/n