惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MyScale Blog
MyScale Blog
F
Fortinet All Blogs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
爱范儿
爱范儿
V
Visual Studio Blog
Last Week in AI
Last Week in AI
WordPress大学
WordPress大学
aimingoo的专栏
aimingoo的专栏
小众软件
小众软件
L
LangChain Blog
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
IT之家
IT之家
P
Proofpoint News Feed
博客园_首页
D
DataBreaches.Net
T
The Blog of Author Tim Ferriss
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Check Point Blog
Engineering at Meta
Engineering at Meta
Microsoft Azure Blog
Microsoft Azure Blog

yzqzss|一座桥在水上’s Blog

运行多用户 FreshRSS 实例的一些经验 便利店的一元——回忆之8 轮胎作玩具,铁环的消失——回忆之6~7 盐——回忆之5 FM广播、站起来及汶川地震——回忆之1~4 部分录音数据再次丢失 「农村合作基金会」——改革开放时期的“县乡银行” 别人说俺是英雄——记小时候一次造永动机的尝试 中文独立博客的年化“腐烂率”——8%
CSSWAF: Browser Detection Using CSS (No JavaScript Needed)
2025-03-24 · via yzqzss|一座桥在水上’s Blog

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>")