SHA-256 的 Length extension attack

Hacker News 上看到「Breaking SHA256: length extension attacks in practice (kerkour.com)」,在講不當使用 SHA-256 會導致 Length extension attack 類的安全漏洞,主要是因為 MD5SHA-1 以及 SHA-2 類的 hash function 最後生出 hash 值時會暴露出 hash function 的內部狀態而導致的問題。

這邊講的不當使用是指你沒有使用標準的 MAC,而是自己用字串組合實作造成的問題,通常是 S = H(secret || message) 這樣的形式,這邊的 || 是指字串相接。

拿 MD5 為例子,在維基百科上面可以看到 MD5 演算法對應的 pseudo code,最後輸出的部分可以看到是把 a0a1a2a3 這四個 32-bit variable 接起來,也就是把內部的狀態丟出來了:

// Process the message in successive 512-bit chunks:
for each 512-bit chunk of padded message do
    // ...

    // Add this chunk's hash to result so far:
    a0 := a0 + A
    b0 := b0 + B
    c0 := c0 + C
    d0 := d0 + D
end for

var char digest[16] := a0 append b0 append c0 append d0 // (Output is in little-endian)

於是你在可以反推 padding 的結構之後 (會需要知道 secret 的長度),就可以往後接東西繼續算下去,這就是被稱作 length extension attack。

本來只有 S = H(secret || message),你在不知道 secret 的情況下就可以疊字串到後面而且算出對應的 hash 值,變成 S' = H(secret || message || evildata)

維基百科給的例子也示範了怎麼「用」,這是原始的資料以及 server 端簽出來的 hash 值:

Original Data: count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo
Original Signature: 6d5f807e23db210bc254a28be2d6759a0f5f5d99

於是我們想要蓋 waffle 參數,就變成:

Desired New Data: count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo&waffle=liege

攻擊者則可以不斷的嘗試,去猜測 padding 的結構,把計算出來對應的 hash 值丟到 server 看反應,直到看到 200 OK 的回應:

New Data: count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x28&waffle=liege
New Signature: 0e41270260895979317fff3898ab85668953aaa2

如同前面提到的,這是 hash function 在最後把內部狀態直接暴露出來造成的問題,在 MD5、SHA-1、SHA-2 (SHA-256、SHA-384、SHA-512) 都有類似的問題,而比較新的 hash function 在設計時就已經有考慮到了,不會出現這個問題,像是 SHA-3

另外一方面,不要自己發明演算法,使用標準的 MAC 演算法通常是比較好的選擇。這邊用的比較廣泛的應該就是 HMAC,超過 25 年了。

結論是 SHA-256 還是堪用,儘量拿現成的演算法套,不要自己搞。

用 Fly.io 跑 RSS-Bridge,再把現有的 twitter2facebook 與 twitter2plurk 改寫

Twitter 把我本來 read-only 的兩個應用程式停用掉了,加上這陣子的新聞,就改用其他方式來處理。

用的是先前在「用 RSS-Bridge 接服務」提到的 RSS-Bridge,可以將 Twitter 的資料轉成 JSON Feed

其中 RSS-Bridge 是 PHP 寫的,剛好就拿先前在「在 Fly.io 上面跑 PHP」這邊提到的方法丟上 Fly.io,不需要自己架主機跑了。

然後把 twitter2facebooktwitter2plurk 這兩個專案裡面本來抓 Twitter API 的程式碼改成抓 JSON Feed。

先這樣子弄,之後再看看要不要搬...

nginx 開始嘗試支援 HTTP/3

Hacker News 上看到「Nginx 1.25.0: experimental HTTP/3 support (nginx.org)」這則消息,從 nginx 1.25.0 開始可以用 HTTP/3

HTTP/2 最大的差異就是從以往的 TCP 改到 UDP 上了,這是基於 QUIC 的經驗弄出來的東西...

nginx 的支援算是等了一陣子了,不過沒有當初 HTTP/1.1SPDY 的進步這麼明顯,我自己就沒有跟的那麼緊了。

這樣以後 office firewall 預設應該會再開 443/udp?

雲端上面的 GPU 資源費用,以及地端的 GPU 決策圖

Hacker News 上面看到「Cloud GPU Resources and Pricing (fullstackdeeplearning.com)」這篇,原網頁是「Cloud GPUs - The Full Stack」,裡面有些有用的資源可以拉出來獨立看。

雲端的選擇上,因為 H100 看起來還沒普及,所以用上一代的 A100 (80GB) 來看,可以看到大的雲端跟其他家的差異還是蠻大的:

不過這邊好像沒把 vast.ai 放進來。

地端的資訊主要是直接購買顯示卡時的選擇,可以看到如果除了各系列的旗艦卡外 (4090 & 3090 & 2080),3060 是一張會在考慮到「便宜」而上榜的卡,應該是因為他是一張入門價位的顯卡,卻有 12GB VRAM 的關係:

在接下來七月要推出的 4060 會出 16GB VRAM 版本,應該會取代現在 3060 12GB VRAM 的地位...

KeyDB:使用 Multithreading 改善 Redis 的效能

Hacker News 上看到有支援 Multithreading 的 Redis fork:「KeyDB – A Multithreaded Fork of Redis (keydb.dev)」,官網在「KeyDB - The Faster Redis Alternative」這邊。

不過這篇是要記錄從 Hacker News 看到的雷點,這樣以後自己再找資料的時候會比較容找到。

36022425 這篇是跳下去用發現不太行,最後在 application 端實作需要的 feature,後端還是用原廠的 Redis:

To counter what the other active business said, we tried using KeyDB for about 6 months and every fear you concern you stated came true. Numerous outages, obscure bugs, etc. Its not that the devs aren’t good, its just a complex problem and they went wide with a variety of enhancements. We changed client architecture to work better with tradition Redis. Combined with with recent Redis updates, its rock solid and back to being an after-thought rather than a pain point. Its only worth the high price if it solves problems without creating worse ones. I wish those guys luck but I wont try it again anytime soon.

* its been around 2 years since our last use as a paying customer. YMMV.

另外是在專案裡搜尋「is:open is:issue label:"Priority 1"」的結果可以看到不太妙,在 36021108 這邊有提到的問題:

Filed July, eventually marked priority 1 in early December, not a single comment or signs of fix on it since. That doesn't look good at all.

然後 36020184 有提到 Snap 買進去後沒有什麼在管 open source project 的部分了:

I think I'll stay far away from this thing anyway. Numerous show-stopper bug reports open and there hasn't been a substantial commit on the main branch in at least a few weeks, and possibly months. I'll be surprised if Snap is actually paying anybody to work on this.

XFCE (Xubuntu) 下的手動 lock + screensaver + DPMS 組合

桌機換成 Xubuntu 22.04 後會遇到手動鎖定螢幕時,螢幕不會進入省電模式,遇到的情況有點像是「DPMS Suspend on Screen Lock」這邊提到的情境。

這類問題如果用 search engine 一時間沒有找到解法的話,最好的方法都是直接去讀 source code,然後就發現透過 Ctrl+Alt+L 觸發的 /usr/bin/xflock4 其實是個 shell script,從 code 讀可以發現裡面只負責 lock 的部分,本來就跟 DPMS 無關。

我在下面提供的方案就是自己處理螢幕的部分,也就是自己先跑 DPMS 指令,然後再回頭呼叫 /usr/bin/xflock4

#!/bin/sh
/usr/bin/xset dpms force off
exec /usr/bin/xflock4

Ctrl+Alt+L 的觸發程式掛上來,這樣就會正常處理了...

用 Atom feed 訂閱 crt.sh 的 Certificate Transparency

本來打算自己寫個程式訂閱 Certificate Transparency 的資料,結果發現 crt.sh 本身就有提供 Atom feed

舉我的例子「crt.sh | gslin.org」來說,對應的 Atom feed 就在 https://crt.sh/atom?q=gslin.org 這邊。

不過 crt.sh 的速度不快,我在使用 Slack 內建的 /feed add 加了很多次才成功,但設定好之後放著收更新就可以了,看起來還算方便。

官方應該是可以把 feed 的筆數壓低,降低對後端 PostgreSQL 的壓力才對,像是只丟最近 30 天的量就好,現在看起來是所有的 entry 都轉成 Atom feed 丟出來...

關於 LLM 的數字

Hacker News Daily 上看到的文章,講 LLM 的各種數字 (大多都是費用):「Numbers every LLM developer should know (github.com/ray-project)」,原文在「Numbers every LLM Developer should know」這邊。

其中第一條就蠻重要的,如果你是用 API 依照 token 收費的話,叫 API 長話短說會省不少錢 XD

40-90: Amount saved by appending “Be Concise” to your prompt

第二條是給個感覺,換算 word 與 token,不過這邊講的應該是英文的:

1.3:1 -- Average tokens per word

後面也有蠻多數字的,都是讓你有個感覺。都讀過後就可以把 cheatsheet 留下來:

Meta (Facebook) 把 MySQL replication 丟上自製的 Raft 系統

看到「Building and deploying MySQL Raft at Meta」這篇,在講 Meta (Facebook) 把 MySQL 的 replication 架構換成自己用 Raft 的系統。

舊的系統是走 MySQL 的 semisync replication:

Previously, our replication solution used the MySQL semisynchronous (semisync) replication protocol.

其中 semisync replication 是在 MySQL 5.5 加入的功能,在至少一個遠端收到 replication log 後才傳回成功 (可以設定數量):「Semisynchronous Replication」。

Semisynchronous replication falls between asynchronous and fully synchronous replication. The source waits until at least one replica has received and logged the events (the required number of replicas is configurable), and then commits the transaction.

然後舊的系統是透過一包 Python 軟體在管理這些機器的各種 failover 操作:

The control plane operations (e.g., promotions, failover, and membership change) would be the responsibility of a set of Python daemons (henceforth called automation).

這個方法常遇到的問題是切換 primary server (以前叫做 master server) 時有可能會因為 binlog position 接不起來而失敗。

所以後來 MySQL 導入了 GTID,可以緩解這個問題,但還是有可能會發生不同的 secondary server (以前叫做 slave server) 會有不一樣的資料。

而在 Meta 改出來的架構裡面,把 replication data 直接寫到一個用 Raft 同步的系統,同步到其他的 secondary server 上面:

In MySQL Raft:

  • Primary writes to binlog via Raft, and Raft sends binlog to followers/replicas.
  • Replicas/followers receive in binlog and apply the transactions to the engine. An apply log is created during apply.
  • Binlog is the replicated log from the Raft point of view.

是個一般單位不太會遇到的架構,而且可以預期其他公司的人遇到類似問題應該也不會用這個方法解...

在 Fly.io 上面跑 PHP

Heroku 把 free tier 拔的差不多後 (「Heroku 公佈了廢止免費方案的時間表」、「Heroku 的替代方案」),大家手上的小專案都往其他的服務跑,目前看起來做的比較有規模的就是 Fly.io 了,一個人可以建很多個 organization,而每個 organization 都有 free quota 可以用...

Fly.io 官方的文件 FLy.io Docs 裡面可以看到說明,介紹怎麼把 Laravel 站台跑起來,不過沒介紹怎麼跑純 PHP 站台,所以就看了文件研究看看,發現可以用 Docker container 跑,那就簡單了。

專案放在 GitHubgslin/fly-vanilla-php 上面。

一開始我的 Fly App 用的是 V1 的版本,是跑起來了,但後來還是換到 V2 跑,雖然兩者用起來沒有太大區別 (參考「Fly Apps」這邊的說明),但畢竟官方打算都把 V1 掛上 legacy 了,新的專案就儘量別用了...

另外一開始用 buildpacks 編 Docker image,但發現太慢了,就還是去 Docker Hub 上找個大戶人家包好的 image 來用。

首先是 fly.toml 這個檔案,這邊就直接指定用 Dockerfile 來編。

然後是 Dockerfile 這個檔案,這邊用的 image 是 richarvey/nginx-php-fpm 這包,預設會開在 port 80,所以 fly.toml 裡面就把 internal_port 指定在 80

另外就是指定了 WEBROOT,我把 root 放在 public/ 下面。

跑起來以後就可以用他提供的網址測試了,我這個專案在 https://hidden-river-325.fly.dev/ 這邊可以看到,另外我有設定自己的 domain,在 https://test-fly.gslin.com/ 也可以看到一樣的 phpinfo(); 資訊。

這邊有個小插曲,我想要掛自己的 domain 上去跑 HTTPS,但如果機器沒有掛 IPv6 address 的話,Fly.io 的系統不會認定設定已經完成,也就不會往下去申請 Let's Encrypt 的憑證,我放了一天覺得奇怪,摸了辦天才發現這個 "feature"。