Google 在 Gemini 服務上把 API key 拿來當作 credential 用的問題

上個禮拜發表出來的災難,Google 的 Gemini 服務上把 API key 當作 credential 用:「Google API Keys Weren't Secrets. But then Gemini Changed the Rules. (via)」。

首先 API key 一直都被視為一般性的 identifier,不是 secret,這個也是目前常見的 pattern,需要帶權限操作時會再加上 secret key 才能存取。這點在 Google 很多文件都可以看到:

問題在於 API key 生成的設計中,預設開了所有的權限:

再加上 Gemini 只需要 API key 就可以對 Gemini 的 endpoint 存取資料,也就是文章裡面提到的,如果 endpoint 傳回 200 就代表大事不妙了,可以直接用 Gemini 的功能用到爽:

curl "https://generativelanguage.googleapis.com/v1beta/files?key=$API_KEY"

然後文章去撈了 Common Crawl 裡面的資料,發現有 2863 個 key 可以開心玩:

To understand the scale of this issue, we scanned the November 2025 Common Crawl dataset, a massive (~700 TiB) archive of publicly scraped webpages containing HTML, JavaScript, and CSS from across the internet. We identified 2,863 live Google API keys vulnerable to this privilege-escalation vector.

更開心的是他們發現 Google 自家的 API key 也有這個問題:

Proof of Concept: Google's Own Keys

We provided Google with concrete examples from their own infrastructure to demonstrate the issue. One of the keys we tested was embedded in the page source of a Google product's public-facing website. By checking the Internet Archive, we confirmed this key had been publicly deployed since at least February 2023, well before the Gemini API existed. There was no client-side logic on the page attempting to access any Gen AI endpoints. It was used solely as a public project identifier, which is standard for Google services.

當初這 design 怎麼過的 XDDD

llama.cpp 支援 Anthropic Messages API

在 X (Twitter) 上看到「New in llama.cpp: Anthropic Messages API」這篇,llama.cpp 支援 Anthropic Messages API 了,相容於 Claude Code 用的 API,官方的範例可以參考:

# Start server with a capable model
llama-server -hf unsloth/Qwen3-Next-80B-A3B-Instruct-GGUF:Q4_K_M

# Run Claude Code with local endpoint
ANTHROPIC_BASE_URL=http://127.0.0.1:8080 claude

先前測試 OpenCode 發現用起來問題還是不少,加上前幾天又被找出不少 security issue,可以先用這個接官方的 claude 起來玩看看...

Bose 把要 EoL 產品的 API 放了出來

Bose 本來在去年十月的時候宣佈今年二月要終止 SoundTouch 產品線:「Bose SoundTouch support discontinued」,到時候會有一堆功能都不能用 (因為各種「雲端」),只能用最基本的 speaker 類的功能:「Bose SoundTouch home theater systems regress into dumb speakers Feb. 18」。

當時看到本來想到的是「又是個變成電子垃圾的常態」,不過昨天 Bose 宣佈放 API 出來,並且會推一版更新讓大多數功能可以在本地端使用:「Bose open-sources its SoundTouch home theater smart speakers ahead of end-of-life」。

On May 6, 2026, the app will update to a version that supports the functions that can operate locally without the cloud. No action will be required on your part. Opening the app will apply the update automatically.

官方用 open source 這個詞,不過看了 license 並不是 open source,這又是另外一個最近被吵的話題了... (包括 LLM 那邊)

對 SoundTouch 的消費者最直接的改變就是很多技術上不需要雲的功能可以繼續用了,包括 AirPlay 以及 Spotify Connect,然後本來的 app 也會不會下架,會繼續留在平台上面讓人下載,算是有個善終 (?)。

至於 API 可以讓 3rd-party software 支援我覺得倒是還好,反倒是這套模式上了新聞,留下了一套硬體的 EoL 範本讓其他硬體廠商可以考慮。

Anthropic 的 Claude 要支援 structured output 了

AnthropicCluade 要支援 structured output 了:「Structured outputs on the Claude Developer Platform (via)」。

目前是支援在 Opus 4.1 與 Sonnet 4.5 上跑 (public beta),後續也會在 Haiku 4.5 上提供:

Structured outputs is now available in public beta for Sonnet 4.5 and Opus 4.1 on the Claude Developer Platform, with support for Haiku 4.5 coming soon.

話說 structured output 算是 LLM API 服務這塊蠻重要的功能,沒有的話得透過 prompt 要求 LLM 給 JSON output,然後再從裡面透過 regex 拉出來跑 validate。

查了一下競爭對手 OpenAI 是去年八月發表的,所以是晚了 15 個月:「Introducing Structured Outputs in the API」。

不曉得為什麼 priority 排的這麼低,也有可能是想把主力部隊放在不同的戰場,想避免跟 OpenAI 硬碰硬?

Claude Code 的 Pro Plan 也可以設定 Extra usage,在超量使用時另外付費避免中斷

之前有用過 Max 20x ($200/mo 的版本),後來發現用不太到先就降到 Pro ($20/mo),結果接著推出幾個還蠻強的中小模型,sonnet-4.5 與 haiku-4.5,測過發現其實就很好用了,尤其是反應速度比旗艦模型 opus-4.1 快很多,互動感覺好很多:

之前在用 Max 20x 的時候會有一個 Extra usage 的選項,讓你在 quota 用完的時候 (通常是 session quota 會先用完) 可以自動切到 API 版本扣預付的 credit (也就是用多少算多少);不過降到 Pro 就沒看到這個選項,所以遇到的時候通常是先去做其他事情,等 session 的 time window 過去後再繼續用。

今天在用 Claude Code 的時候發現 Usage 頁面冒出來了,就把他開起來,然後儲個 US$5 進去:

再用下去就會扣到了:

時間到了又會回到 session quota 的使用量。

GitHub notification 消不掉的問題

前幾天 GitHub 上有 scammer 假冒 Y Combinator 的名義 tag 了一大堆人 (所以會有 notification),然後 scammer 的 repository 被幹掉後,一堆人的 notification 就一直卡著未讀:

看起來就很像是官方砍掉後沒有清 notification 的關係,加上前端又沒有 Mark all as read 之類的功能,就卡在那邊了...

用搜尋引擎找看看有沒有其他人有討論,找到「Bug: unread notification is stuck #174602」,這篇的作者跟我一樣也是遇到 Y Combinator 的詐騙。

然後有人在討論下面指點在 https://github.com/orgs/community/discussions/174310#discussioncomment-14474599 這邊有 workaround 可以用,前陣子有其他的詐騙也有類似的狀況,有人找到可以透過 GitHub CLI 的 gh 指令砍掉。

平常就有裝好 gh,所以直接照表操課,可以看到對應的 id:

gslin@home [~] [17:49/W4] gh api notifications
[
  {
    "id": "19143713982",
    "unread": true,
    "reason": "mention",
    "updated_at": "2025-09-23T22:32:32Z",
    "last_read_at": null,
    "subject": {
      "title": "Y Combinator | $15M W2026 with GitHub [2025]",
      "url": "https://api.github.com/repos/ycombiinator/ycombinator-co/issues/226",
      "latest_comment_url": "https://api.github.com/repos/ycombiinator/ycombinator-co/issues/226",
      "type": "Issue"
    },
    "repository": {
      "id": 1062901816,
      "node_id": "R_kgDOP1qYOA",
      "name": "ycombinator-co",
      "full_name": "ycombiinator/ycombinator-co",
      "private": false,
      "owner": {
        "login": "ycombiinator",
        "id": 233630326,
        "node_id": "U_kgDODezqdg",
        "avatar_url": "https://avatars.githubusercontent.com/u/233630326?v=4",
        "gravatar_id": "",
        "url": "https://api.github.com/users/ycombiinator",

然後打下去後收工:

gslin@home [~] [17:50/W4] gh api -X DELETE notifications/threads/19143713982

算是有趣的經驗 (?)

使用 OpenAI 的語音轉文字 API 的省錢方法:加速語音

OpenAI 的語音轉文字 API 是照語音的長度算錢的,然後昨天還是前天在 Lobsters 上看到的省法,把語音的部分加速就可以了:「OpenAI Charges by the Minute, So Make the Minutes Shorter」。

作者測試發現跑 2x 或是 3x 都還 OK,但 4x 就不行了,然後也有提到這跟原始的語速有關 (很容易推論):

More helpfully, I didn’t compare word-for-word, but spot checks on the 2x and 3x versions looked solid. 4x speed was too fast—the transcription started getting hilariously weird. So, 2x and 3x seem to be the sweet spot between efficiency and fidelity, though it will obviously depend on how fast the people are speaking in the first place.

工具都有現成的 (像是 FFmpeg),主要是這個點子,之後有遇到類似的境可以拿來用... 另外要考慮到我們會處理 CJKV,如果要用這招的話,語速與辨識力都得再確認。

xfce4 的天氣套件需要更新 (xfce4-weather-plugin)

這兩天發現天氣套件的輸出變成 No Data 了,看了一下 ~/.xsession-errors 裡面的 error log,發現是被 403 了:

(wrapper-2.0:4334): weather-WARNING **: 21:56:32.850: Download of sun astronomical data failed with HTTP Status Code 403, Reason phrase: Forbidden

本來以為是 IP 的問題,但重開機或是其他方式弄了幾次以後發現都不行,才用錯誤訊息去找資料看看發生什麼事情,然後發現是用到的 API 改版了,需要更新套件:「Download of sun astronomical data failed with HTTP Status Code 403, Reason phrase: Forbidden」。

下面 Pariksheet Nanda 很好心的提供了自己 compile 的方式,這樣就不用等上游套件更新了。

把 rsyslog 訊息串到 Slack 與 Pushover 上

把之前想弄的東西弄出來了,直接在 rsyslog 上設定條件,然後串到 Slack 以及 Pushover 上。

rsyslog 這邊有不少眉眉角角要處理,本來查到 omhttp,想直接透過 omhttp 打到 HTTPS endpoint,但發現 omhttp 沒有也沒打算包進標準套件裡面 (因為不是由官方開發的),但文件上面有... 在 2018 年的「rsyslogd: could not load module 'omhttp' #3302」這邊就有提到這個問題了:

Sadly, the omhttp module is currently not part of the default packages, so you'll have to build it from source (github).

所以還是回來用 omprog,但這邊會遇到預設的 AppArmor 設定會擋掉外部執行權限的問題,這邊有兩個地方要改:

  • /etc/apparmor.d/usr.sbin.rsyslogdprofile rsyslogd /usr/sbin/rsyslogd { 這邊要加上 flags=(unconfined),變成 profile rsyslogd /usr/sbin/rsyslogd flags=(unconfined)
  • 新增 /etc/apparmor.d/rsyslog.d/omprog 這個檔案,裡面只放 /usr/local/libexec/** Ux,

接著回頭設定 rsyslog,首先是 /etc/rsyslog.conf 裡面增加 module(load="omprog");然後是 /etc/rsyslog.d/99-ocserv.conf 裡面設定 webhook,這邊我是這樣寫:

#
if ($programname == "ocserv") then {
    if (re_match($msg, "main\\[")) then {
        action(type="omprog" binary="/usr/local/libexec/logs-to-slack.sh https://hooks.slack.com/services/x/x/x")
        action(type="omprog" binary="/usr/local/libexec/logs-to-pushover.sh x x")
    }
}

這兩個呼叫 webhook 的 script 都是吃 stdin 的資料,然後丟給 jq 產生出 JSON object,再用 curl 打 API 出去:

#!/bin/sh

WEBHOOK="$1"

while IFS= read -r LINE; do
  J=$(echo "${LINE}" | jq -Rs '{"text":.,"type":"plain_text"}')
  curl -s -X POST -H "Content-Type: application/json" --data "${J}" "${WEBHOOK}" > /dev/null 2>&1
done
#!/bin/sh

USER="$1"
TOKEN="$2"

while IFS= read -r LINE; do
  J=$(echo "${LINE}" | jq -Rs "{\"message\":.,\"token\":\"${TOKEN}\",\"user\":\"${USER}\"}")
  curl -s -X POST -H "Content-Type: application/json" --data "${J}" "https://api.pushover.net/1/messages.json" > /dev/null 2>&1
done

然後重跑 rsyslog 讓他生效,接著登入 VPN 看看有沒有收到訊息...