HTTP 103 Early Hints

前幾天晚上前同事陶百貼了個 Tweet,說到 Chrome 要移除 HTTP/2 Server Push 了:

仔細看一下,發現原來大家用 Server Push 都還是為了提升網頁第一屏的速度,但是 Server Push 一直有一些難解的問題,像是不知道 client 端有沒有 cache,實作和支援比較麻煩,而 Chrome 要移除 Server Push 前,其實先實作了 RFC-8279 的 HTTP 103: Early Hints,為的就要讓 Server Push 現在作的事情先有替代方案。

Early Hints 應該算是 Fastly 提出的,RFC 文件作者是 Kazuho Oku,實際上應該也有其他 Fastly 的人參與構思和試驗,支援 Early Hints 的環境下,一個 HTTP request 看起來就像是下面這樣:

Client request:

  GET / HTTP/1.1
  Host: example.com


Server response:

  HTTP/1.1 103 Early Hints
  Link: </style.css>; rel=preload; as=style
  Link: </script.js>; rel=preload; as=script

  HTTP/1.1 200 OK
  Date: Fri, 26 May 2017 10:02:11 GMT
  Content-Length: 1234
  Content-Type: text/html; charset=utf-8
  Link: </style.css>; rel=preload; as=style
  Link: </script.js>; rel=preload; as=script

  <!doctype html>
  [... rest of the response body is omitted from the example ...]

很特別的,就是在於有兩段 response,第一段就是 103 的 status code,然後內容就是 Link headers 了,接著才是常見的 200 回應,看到這邊,自然的出現第一個問題:現有的瀏覽器能相容嗎?

這個問題在 Stack Overflow 也有人問,結果回答在 RFC 文件內其實就有,只不過是放在第三章的安全性那邊,我一開始也因為先跳過這章而沒發現,總之關於這個問題,就是如果是 HTTP/2 的話,就比較沒問題,HTTP/1.1 的話,理論上應該要可以相容(沒功能但是也不出錯),但是無法保證現在有在用的 HTTP/1.1 client 都有正確的處理 1xx response,所以比較建議是 HTTP/2 才回 103。

過了兩天後,我更仔細的研究一下,發現其實早在 HTTP/1.1 時,就有把 1xx 的處理需求定義好了:

A client MUST be able to parse one or more 1xx responses received prior to a final response, even if the client does not expect one. A user agent MAY ignore unexpected 1xx responses.

就是說早在 HTTP/1.1 時的設計,就允許 1xx 接 200 的回應,而且還應該要支援多個 1xx 回應,而最後的那個 200(其實是 2xx 到 5xx 都可以),則是稱為 final response,至於這處理的方式,在 WHATWG 的 fetch 的 4.7 章則有清楚的寫下流程,在該章節的第九項裡面的第五子項目,寫成程式碼大概長成:

while (true) {
  const response = await networkTransmit();
  const status = response.statusCode;
    
  if (status >= 100 && status <= 199) {
    // handle 1xx response
    continue;
  } else {
    break;
  }
}

// handle final response

所以理論上,Early Hints 的設計在正確支援 HTTP/1.1 但是還沒有支援 Early Hints 的瀏覽器就應該要可以正常的略過,而不會把它當成是 final response。

解決完第一個問題後,接著來仔細的看看剛剛範例的 server response:

HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

HTTP/1.1 200 OK
Date: Fri, 26 May 2017 10:02:11 GMT
Content-Length: 1234
Content-Type: text/html; charset=utf-8
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script

<!doctype html>
[... rest of the response body is omitted from the example ...]

不知道會不會有人疑惑,為什麼不直接用 200 response 裡面回應的 Link header 就好了?其實我一開始也是這樣想,不過這完全是因為這個問題落入身為前端工程師的我的盲點之中,因為現在前端開發主流是 SPA,通常 HTTP server 回的就是一個靜態的 HTML 檔案,所以回應速度超快。不過,如果回應的 HTML 文件,是由程式語言動態生成的,或許還需要查詢一下資料庫之類的,那這個回應時間就會變慢了,而 HTTP 103 Early Hints 就是在這種狀態下用的,在你的 server 端程式開始處理 request 之前,就先丟 103 的 status code 和 Early Hints 的內容回給瀏覽器,然後才接著處理資料和生成 HTML 文件,這種情境下,Early Hints 就顯得比較有差異了。Nitropack 的文章就解釋的很清楚,還有附上詳細的說明圖。

相較於 Server Push,其實 Early Hints 的設計簡單很多,所有的傳輸還是從 client 端看有沒有 cache ,決定要不要發 request,而這種操作已經非常成熟(相較於 server push),相信很多地方可以直接使用現有的程式碼來實作,最大的隱憂,就只是不相容 HTTP/1.0,然後會擔心有 HTTP/1.1 的 client 端沒正確實作吧,畢竟 1xx 的處理機制雖然早早就設計好,但是實際上 1xx 有被廣泛使用也是這幾年的事。

目前 Chrome 是從 103 開始支援 Early Hints 的,並且預計在 106 正式移除 Server Push,至於其他瀏覽器則是都還沒有支援, Firefox 是有計畫要支援,進度有點緩慢就是。

最後,Fastly 其實有提供一個測試用的網站:https://early-hints.fastlylabs.com/,不過這個網站不是用來測試你的瀏覽器支不支援 Early Hints 的,而是用來測試先 103 然後接 200 的 response 會不會有非預期的問題(也就是相容性的測試),如果想要直接看看來回的內容,也可以直接用 curl:

curl -v https://early-hints.fastlylabs.com

Shopify App

之前開發 Shopify App 時,為了搞定他的安裝搞了蠻久,所以決定來紀錄一下踩到的坑,這篇文章適合已經開始在開發 Shopify App 的人閱讀,有些 Shopify App 的基本知識就不會提到,以下內文幾個名詞先定義清楚一下:

  • App 指的是我們開發的 Shopify 第三方 app
  • Merchant 指的是在 Shopify 上開店的商家
  • 安裝 app 指的是 merchant 在他們的 Shopify 商店上安裝我們開發的第三方 app

首先就是,我踩的很多坑有一部分原因是因為我用 NodeJS 作為 server 端的語言,選的是 Express,但是官方的 Express 架構的 app 範例已經停止維護了,取而代之的,是 Koa 版本的 @shopify/koa-shopify-auth,只有負責驗證相關的 middleware,不過其實我也就剛好是需要 auth 相關的部分,只是差在不是 Express 版,我也還可以研究看看要怎樣自己實作了。

大概看一下,發現其實還有另外一個 @shopify/shopify-api 是底層負責處理跟 Shopify 相關的邏輯,所以理論上我也可以使用它來搭配 Express,不過這裡首先就有一個坑了,初始化的範例是長這樣:

Shopify.Context.initialize({
  API_KEY: process.env.SHOPIFY_API_KEY,
  API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
  SCOPES: process.env.SHOPIFY_APP_SCOPES,
  HOST_NAME: process.env.SHOPIFY_APP_URL.replace(/^https:\/\//, ''),
  API_VERSION: ApiVersion.October20,
  IS_EMBEDDED_APP: true,
  // More information at https://github.com/Shopify/shopify-node-api/blob/main/docs/issues.md#notes-on-session-handling
  SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});

可以看到,最後有一個SESSION_STORAGE,這是個處理 merchant 在安裝 app 時,我們的 app 拿到的 access token 的儲存方式的 adapter,不過官方的範例是用 Memory Storage,這個 adpater 是只有存在記憶體內,其實只適用於開發用,只要你的 server 一重開,所有的 merchant 就都要重新安裝你的 app,不然你的 app 會沒有 access token 跟 Shopify 溝通,實際上你應該要參考 Custom Session Storage 這份文件,挑選適用的 adapter,我則是參考範例寫了一個 GCP FireStore 的版本,當然另外沒特別提到的就是,因為是儲存 access token,最好要考慮一下 DB 的加密。

第二個坑,就是要怎樣做 Shopify 的 authentication 以及 identification,先來說如何驗證 request 是可信的,在 Shopify API 的設計,就是要靠 query string parameter 裡面的 hmac,他是根據你的 App 的 secret 來計算出來的,然後,這裡的坑就是官方套件@shopify/shopify-api內有個validateHmac可以用,但是它的計算其實是不正確的,它是用白名單只有取部分的 query string parameter 來計算,結果和 Shopify 給的就會有出入,所以我是參考 GitHub issue 討論串內 Muhammad Kamal 給的範例來使用。

第三個坑,則是安裝 App 用的 route 了,Shopify 的設計有點特別,所有的初始 request (不論是第一次安裝、還是從 Shopify 後台進入 App 的設定畫面),都長的很接近,所以你就要根據各種狀況來決定該做什麼事情,以下是所有可能的狀況:

  • 第一次來安裝
  • 安裝後進到設定畫面
  • 曾經安裝過,但是需要重新授權,可能的原因:
    • App 需要的權限有變動
    • App 端的 access token 失效了
  • Shopify 認為已經安裝了,但是 app 端沒資料

扣除需要的權限有變動之外,其實就是排列組合,Shopify 端認為有沒有安裝過,和 App 端認為有沒有安裝過,二乘二共四種可能性,不過實際上只有三種處理方式:初次安裝、重新授權、安裝沒問題的快樂路線(happy path)整理成程式流程大概是:

  1. 驗證 hmac,沒過可以直接回 400
  2. 判斷 shop 是否有在資料庫中
  3. 2 有的話驗證資料庫中的 access token
  4. 3 驗證通過的話,狀態就是 happy path,Shopify 認為 app 有裝,app 端檢查也沒問題,我把這狀態命名為valid
  5. 3 驗證沒通過的話,判斷有沒有session這個 query string 參數
  6. 5 有的話,狀態就是 app 端的 access token 不能用了,需要走重新授權的流程,我把這狀態命名為invalid
  7. 5 沒有的話,就是第一次安裝的流程,我把這狀態命名為not_found
  8. 最後就是 2 沒有的話也是走初次安裝的授權流程,同樣也可以叫not_found

然後 app 需要的權限變動的話,理論上是每次進來,驗證 access token 的時候,可以去打 API 問目前 token 的 access scope,不過這部份我沒實做,因為目前我還沒有相關需求。

網路上可能可以找到X-Shopify-API-Request-Failure-Reauthorize這個 header,不過這個其實不是 Shopify API 的回應,而是 Shopify 的 app-template 裡面設計的機制,它們的 app template 裡面,server 端在轉發 Ajax API request 時,如果收到 Shopify 端的錯誤後,就加上這個 header 回給 app 前端,app 前端收到這個 header 後就可以透過 Shopify app-bridge 進入重新授權的流程。

講到這邊,或許有人會好奇,為什麼需要把安裝 app 和重新授權兩個流程的處理方式分開?其實這可以算是第四個坑,也是和使用者體驗有關係,狀況就是,Shopify 認為是初次安裝時,是直接進入 OAuth 的流程,所以是瀏覽器的最上層視窗直接轉址到 auth 頁面,但是如果是需要重新授權的情形,則是 Shopify 端認為已經安裝好,但是 app 這邊認為需要重新跑一次 OAuth,而這時候,連到 app server 的瀏覽器視窗是在 Shopify 商店後台的 iframe 內,在 iframe 內也無法正確的完成 OAuth 授權,所以需要用 Shopify 現在一套叫 app-bridge 的工具幫忙,讓 OAuth 流程從最上層視窗開始,所以需要回一個 HTML 頁面,引入 app-bridge 的 script,然後執行以下的的 JS:

const AppBridge = window['app-bridge'];
const createApp = AppBridge.default;
const Redirect = AppBridge.actions.Redirect;
const app = createApp({
	apiKey: '{{API_KEY}}',
	host: '{{HOST}}',
});
const redirect = Redirect.create(app);

redirect.dispatch(
	Redirect.Action.REMOTE,
	'/url/to/your/auth?shop={{SHOP}}'
);

當然記得要把該替換的東西替換上去,然後就可以看到正確的從最上層視窗開始進入 OAuth 授權的流程了。

最後一個坑,其實就是 merchant 反安裝 app 後,Shopify 和 app 端的狀態就會不一致的問題,Shopify 端認為沒安裝,但是 app 端認為有安裝,雖然我上面設計的程式流程已經可以處理這種狀況(驗證 access token 會失敗,然後沒有session參數,所以會進入初次安裝),但是這種情形還是應該要能避免就避免,而解法就是要支援 webhook,要作的事情就是:

  1. 安裝完成的 callback 去訂閱APP_UNINSTALLED這個 webhook event
  2. 然後在收到這個事件後,把資料庫中的對應資料刪除

這邊我是用@shopify/shopify-api提供的工具像是Shopify.Webhooks.Registry.registerShopify.Utils.deleteOfflineSession,真的想要自己作也不是辦不到,不過我記得 Shopify 的 webhook 處理起來有點麻煩。

這些細節就是官方文件沒有好好寫清楚,雖然官方文件內容已經很多,有努力整理了,但是實際上要自己接就還是遇到了不少問題,所以特別寫一篇文章紀錄,雖然不知道會不會有其他中文圈的人需要自己來做 Shopify app 就是了,可以直接用他們的 app template 還是比較簡單啦。


Safari 3rd-Party Cookie

Apple ITP

Apple 之前有宣告要完全阻擋 3rd-party cookie,iThome 也有相關的報導,iOS 和 iPadOS 應該是已經上線了,然後最近 Mac 版 Safari 也快要上線了,所以這篇來記錄一下要怎樣因應還有一些參考資料。

其實真的會寫到第三方 cookie 的服務是沒想像多的,如果不是開發給其它網站用的第三方服務的話(不是掛 script 而已),那其實沒那麼常見,舉例來說:很多人可能會覺得 Google Analytic 會受影響,但是其實並沒有,一般網站掛 Google Analytic 算是掛上 3rd-party script,但是它寫的 cookie 是 1st party cookie,也就是寫在你的網站的 domain 下,Google 的文件也有很詳細的說明他的每個 cookie 的用途,然後仔細看就會找到還有寫如何跨網域追蹤,而這其實是需要帶一些參數過去的,如果 GA 是用 3rd-party cookie 寫在 Google 自己的 domain 的話,要跨網域追蹤就不需要這樣帶參數了,我是覺得 Apple 的 ITP 比較是針對廣告和 Facebook,早幾年前 Facebook 可以用 like button 來簡單的做到跨站追蹤,現在那些 iframe 都會被認為是 3rd-party,cookie 會和 1st-party 放不同區(partition),甚至本來如果有先去看過 facebook.com 之後,會有 24 小時可以存取該網域 3rd-party cookie 的能力也在 ITP 2.0 移除,facebook 後來加上了fbclid這個參數來追蹤連出去的連結,然後 ITP 2.2 就又針對這種連結裝飾(link decoration)也設了 cookie 的存取限制(剛好同時也影響到 Google Analytic)。

如果真的是需要作為 3rd-party 端提供服務的話怎麼辦呢?其實一開始 Apple 那篇文章,有列了幾個方案,其中正規的兩個方案:

  1. 用 OAuth 2.0 作為 user auth 的方案,然後第一方網站拿到 token 後自己存好(作為 1st-party cookie 或是其它儲存方法)。
  2. Storage Access API,這是 Apple 所提出的 Web API,在被視為第三方的 context 中(例如 iframe),可以透過 Storage Access API 來取得 1st-party cookie 的存取權限,不過一般人直接用這個 API 要權限,可能會覺得奇怪怎麼 Safari 都沒有問使用者要不要給,權限就拿到了,其實這是因為 Apple 那邊的想法是這個 API 要盡可能的不干擾使用者,所以只有被歸類(classified)為有追蹤能力的域名才會跳出視窗跟使用者詢問,至於這個歸類的方法是在 ITP 1.0 中提出的,Apple 考慮到隱私問題,所以這個機制是用機器學習的,每台電腦/裝置都維護自己的清單,沒有中心化的黑名單(Firefox 應該是用這種方法),而如果想要親自驗證自己的 domain 要是被歸類為追蹤網站的話,會發生什麼事的話,也有篇文章介紹,我自己有測試過也確實看到了那個詢問視窗。

然後如果要用 Storage Access API,其實還有些限制,Safari 從 1.0 開始,就有個針對 3rd-party cookie 的限制,就是使用者要曾經直接訪問過該網域,並且寫入過 1st-party cookie,之後該網域才能對 3rd-party cookie 做存取,而這項限制也延伸到 Storage Access API 這邊,一樣要先作為 1st-party 寫入過 cookie,之後才能夠透過 Storage Access API 取得 1st-party cookie 的存取權限,Apple 負責 ITP 的 John Wilander 最近正在寫相關的文件,裡面就有提到,然後這個限制 Firefox 也有,不過 Firefox 似乎是 30 天內有訪問過該網域就可以。

寫到這邊,其實有件事情忘記先提,就是網路上你去搜尋 Safari 3rd Party Cookie 會找到一些方法說可以成功讀寫 3rd-party cookie 的,那些全部都已經失效了,而且不只是 cookie,所有可以寫入的東西像是 DOM Storage 也是有受到一樣的限制保護的(然後 Storage Access API 現在只能拿到 cookie 的權限),目前也沒有出現其它的繞過方式,而且就算有人找到,Apple 都會修掉的,所以如果有這需求還是趕快用 Storage Access API 實做吧(別忘了 feature detection)。

然後或許有人會覺得 ITP 沒檔到 Google Analytic 好像沒什麼意義,其實 John Wilander 早在 2017 年就有在 WebAppSec 稍微提過 Single Trust 這件事,提的就是網頁內掛的 3rd-party script 其實是安全性隱憂,應該只有同 domain 的東西可以信任,在 cookie 這邊來說就是 3rd-party script 不應該有存取網站 1st-party cookie 的權限(不過後來發生的是某航空公司的信用卡資訊輸入頁面放的第三方 script 會做 key log),如果真的進行,這個改變可以想像的到影響非常的巨大,舉例來說,以前的 Performance Practice 其中一項是把 static file 放到 CDN 並且用不同 domain host,但是這樣其實就會被當成是 3rd-party script 了,雖然他在我們的認知下是可信任的,然後目前也有非常大量的現存網站是這樣做。目前 Apple 也有在做一些相關的研究,其中一個已經廣為人知(?)的就是 Safari 現在有在紀錄 3rd-party script 的數量,另外就是我之前在 SameSite Cookie 這篇文章有提到的,Mike West 起草的 First-Party Sets,透過/.well-known/下的檔案定義可以被認為是 1st-party 的 domain 清單,假設未來真的要做到 single trust 的程度,要處理 CDN 之類的問題,像是 First-Party Sets 的機制就不可少。

最後附上一些延伸的參考資料:


Scroll to Text

Scroll To Text

Chrome 最近有個新功能叫做 Scroll to Text Fragment,雖然在 Chrome Platform Status 網站那邊寫 M80 會可以用,不過我實際上測試正式版的 Chrome 80 還沒有,但是 Chrome Canary 已經是預設啟用了。這個功能讓你可以在網址內的 Fragment 段(#後面那段),用新定義的語法來讓瀏覽器直接捲動到指定的文字位置,語法如下:

#:~:text=[prefix-,]textStart[,textEnd][,-suffix]
          context  |-------match-----|  context

如果有在用 Chrome Canary 的可以直接試試看這個連結。這個語法其實還蠻靈活的,可以和以前 id identifier 並用:

#targetID:~:text=theText

這樣如果找不到文字,瀏覽器還可以改用 targetID 定位;如果網頁內有多個一樣的文字,可以用prefix--suffix給出前後文來讓瀏覽器找到正確的目標;再來就是如果要標註的文字很長,也可以用textStart,textEnd來標註,這樣就不用在網址內放一大串文字了;然後也可以標註多段文字,用&切開,和給參數的格式一樣:

#:~:text=firstText&text=secondText

正式的文件現在是放在 WICG 那邊,GitHub 那邊的 Proposal repo 則是有一段解釋為什麼選擇用:~: 當分割符號的段落,我覺得這種脈絡的紀錄是很重要的,這邊簡單說一下,一開始有考慮過##這種比較容易想到形式,但是有些 URL parser 是由右到左的可能會有錯誤,再來他們列出一堆不太會有人去用的組合來當候選,然後去看 Google 那邊過去五年的紀錄,結果就是:~:最沒人用,只有 0.0000039% 的比例,所以目前是選擇這個分割符號。

我是蠻喜歡這新功能(標準?)的,Mozilla 也覺得還可以考慮看看,不過目前這個功能似乎還沒在 stable channel 啟用,應該是因為隱私問題,Chrome 負責的團隊也有整理相關的資訊,大概簡單說就是有可能透過頁面的讀取時間或是scrollTop的值來判斷你開啟的頁面內有沒有特定字串,然後就會有外洩的疑慮。另外還有一個讓人擔心的問題是這個功能可能會讓那些本來用 fragment 當成 route 的 SPA 壞掉,W3C TAG design review 那邊他們自己也有提到。

其實這個 scroll to text 的功能在之前還有一套提案,不過不是由瀏覽器開發商所提,而是 indieweb 提的,叫做 Fragmention,這組提案功能就比較陽春而且不成熟了:

#some%20text

重點在裡面那個%20,也就是空白字元,Freagmention 的提案是你的目標文字要有空白,因為 HTML 的 id 不能包含空白字元,所以如果有空白字元就表示不是要找 id,這個提案由我來看就是很明顯沒搞過 i18n 的啊~


W3C TAG

去年我在規劃 COSCUP 的 Open Web Technologies 的時候,有準備了一個備用的演講,題目是關於到哪裡可以追蹤到新的網路標準發展,其中的一個可以關注的資源,就是 W3C TAG(Technical Architecture Group)design-reviews,我一開始其實是誤打誤撞發現這個 issue list 的,有點像是發現寶庫一樣,幾乎所有的新標準都會到這邊提出審查請求,而除了標准之外,Web 相關的比較重要的修改提案也會發到這邊來,像是最近要進行的 SameSite=Lax by default,還有剛提出的 Partial freezing of the User-Agent string

W3C 有一份 Consortium Process Document 的文件(簡稱為 Process),內容包括了 W3C 的一些基本構成,規範如何建立 Working/Interest Group 以及這些小組如何發展技術報告(Technical Report),這邊說的技術報告包括了草稿到 W3C Rec(推薦標準),除此之外,這份文件還有制訂了兩個獨立組別的構成方法,這兩個組分別為:Advisory Board(顧問團) 和 Technical Architecture Group(TAG、技術架構組),兩者都有負責解決跨技術報告(aka 標準)之間的問題,前者是負責非技術的問題,後者則是負責技術問題。W3C TAG 的成員結構現在是:

  • Tim Bernes Lee 為終生成員
  • W3C 總監
  • 總監可以提名三位成員
  • 另外有六名成員則是由 Advisory Committee(諮詢委員會,其實就是所有 W3C 會員代表人)選出

其實看他們的審查意見都覺得真的很厲害,不過這些成員的名字其實曝光都很少。然後我一直很好奇,為什麼幾乎所有的技術報告都來這邊提審查,應該是有在技術報告開發流程上寫到才會這麼多,結果那份 Process 文件找來找去就是沒找到,花了幾個小時最後終於在其中一分外連的「如何建立一個工作/興趣小組」的文件中找到,其實找 TAG 做審查是 Horizontal Review(橫向審查) 的一部分,Horizontal Review 指的是技術報告在發展的過程中,找各個相關/相依的小組一起來做審查,而這些組別間的關係其實是寫在小組各自的章程裡面的,該份文件還有列出一些比較關鍵的組別:

這五個確實是非常跨技術的主題,尤其是 TAG,技術的議題都跑不掉,我也循線去其它四個小組看了一下,發現真的也有相關的審查請求,不過不同小組申請審查的管道不一樣,有的還是用 W3C 傳統的 mailing-list,TAG 已經用 GitHub 算是非常先進而且方便訂閱了,從開發者的角度如果要關心網路標準的新發展可以以這邊為主,明顯比較缺的就是 CSS 的新東西不會發到這邊,我自己是還有訂 CSS Working Group 的 issue list。

最後就是現在台灣的 W3C 會員似乎只剩下 台灣數位出版聯盟,日本現在倒是很多組織有加入了,像是不知道為什麼加入的 DWANGO,看起來也沒參加 Chinese Interest Group 的 Danmaku


DNS CAA record

過年前因為工作關係第一次注意到 CAA record (Certification Authority Authorization) 這個東西,簡單說明就是透過 DNS record 來設定你的 SSL cert 的簽發單位白名單,一開始的規範是 RFC6844,其實原理也不複雜,不過我就在遇到用 AWS ACM 簽發憑證時說檢查不過的狀況,有趣的是該 domain 沒有設定任何 CAA,搜尋研究一陣子後發現可能是因為該 domain 是 CNAME 去到別的第三方 domain 才會這樣,然後果然發現這個問題其實很久了。

不過其實原始版本的 RFC6844 其實沒有要求檢查 CNAME 目標的 CAA,而是在 2017 年的勘誤 5065 中才加入的,不過這個修改造成很多問題,所以 letsencrypt 在同一年就又換回舊的實做了。CAA record 看起來也因此放棄這個修改了,在用來取代 RFC6844 的 RFC8659 中,就完全沒有提到 CNAME 的檢查,甚至在與舊版相異的附錄也是特別提到這點差異,不過 RFC8659 還很新,是 2019 年 11 月的,看起來 AWS 還沒修正也情有可原(?)。


Public Suffix List

最近因為花了很多時間研究 Safari 和第三方 Cookie,常常看到一個專有名詞 eTLD+1,之前只知道和 domain name 及 TLD 相關,不清楚確切的定義,所以又去查了一下,結果找到解釋最清楚的竟然是 Go 的 publicsuffix 套件的說明文件,總之簡單比較不明確的解釋,eTLD 指的是 effective TLD,像是netnet.tw這類,域名註冊商可以提供的網址後綴,依此類推,eTLD+1 就是 eTLD 再多一段,也就是一般人可以註冊到的網域名稱,像是我這邊用的othree.net,至於部落格的子網域blog.othree.net就不是 eTLD+1 了。

其中的 eTLD 又稱為 Public Suffix,然後 Mozilla 有維護一份 Public Suffix List,給瀏覽器用的,主要用途就是避免寫入太高權限的 cookie,例如我要是把 cookie 寫到.net層的話,所有的.net域名的網站都會讀的到它,就會有安全性問題,這份清單現在已經是主要瀏覽器開發商都有在使用了,它的內容除了那些 eTLD 之外,其實還有私人公司提交的,像是 blogspot 列了一大串,還有 github 有列github.iogithubusercontent.comgithub.io是 GitHub Pages 的預設 domain,像我的 Github Page 就會用 othree.github.io,GitHub 提交這筆記錄,在現代瀏覽器就會限制我在othree.github.io不能寫 cookie 到github.io,這樣可以確保所有使用者的頁面不會互相影響。

我還順便亂瀏覽一下內容,發現 Amazon 手上好多的 gTLD,像是booksong,然後他們的cloudfront.net也有提交,還有一堆其它的 aws 網域名稱;另外就是 DynDNS 和 no-ip 兩個類似服務都提交超多的;然後還發現一間叫 nymnom.com 的域名註冊商,專門提供一堆nomnym結尾的域名,搞不清楚這兩個單字的意思啊。


Robots Exclusion Protocol

Google Webmaster Central Blog 昨天發表了 Formalizing the Robots Exclusion Protocol Specification 這篇文章,介紹到 Robots Exclusion Protocol (REP) 這個正在標準化的草案,REP 其實就是已經被廣泛使用的 robots.txt 檔案,robots.txt 誕生至今已經 25 年了,當初是由 Martijn Koster 所設計,早期網路的東西基本上就是先做,設計的不錯大家就跟著抄,不一定會有什麼標準的文件,robots.txt 就是這樣其實一直都沒正式的標準文件,我以前還真的有懷疑過怎麼找不到,直到 Google 這篇文章才確定了,真的一直以來是沒標準的,雖然 Google 衝網路標準太快讓人有不少意見,不過這次我倒是覺得樂觀其成,而且他們也還公開了他們的 robots.txt 的 parser matcher lib

消息來源


SameSite Cookie

Cookie Time

Cookie 的規格是 RFC 文件所定義的,其實一直以來都有在演化,目前為止已經有三個版本,照順序分別是 RFC2109RFC2965 和最新的 RFC6265,像是HttpOnly就是 RFC6265 才出現的,而最近最新的屬性,就是SameStie了,其實它和HttpOnly的起源很接近,都是近年來比較被人重視的安全性和隱私的原因,Google 的 web.dev 有一篇圖文並茂的文章介紹的很詳細- SameSite cookies explained,建議還不清楚什麼是 SameSite cookie 的可以先去看一下。

SameSite Cookie 的標準文件其實還未正式定稿,目前還算是草稿 RFC6265bis(bis 在 The Tao of IETF 有解釋),不過主流瀏覽器都已經支援了,然後其實這篇文章我想說的是最近在 W3C TAG 看到的 Issue 373:SameSite=Lax by default,是由 Google 的 Mike West 提案要把 SameSite 的預設值改為 Lax,現在 Google Chrome 已經有這個實驗選項了,而且除了 SameSite 預設值的改變之外,其實還有一個修改目標是SameSite要在Secure的時候才能設為None,這項改變相對而言是影響比較大的,所以提案的文件(Incrementally Better Cookies)也有提到可以分步進行,另外就是 Firefox 也表示有意願來實做,看起來至少 SameSite 預設改為 Lax 這件事應該是不會太久之後就會發生了。

在花時間看一些文件內的參考資料後,發現 Mike West 還有其它幾份相關的草案:

  • first-party-sets WICG/first-party-sets 是用/.well-known/URL 來跟客戶端溝通,可以提供 first party 的域名清單;
  • First-Party Sets and SameSite Cookies 利用上面的 first-party-set 資訊,然後提供兩種新的 SameSite 值:FirstPartyLaxFirstPartyStrict
  • HTTP State Tokens 定義了個標準化的 session token,是由瀏覽器端產生的 token,而不是 Web API,至於怎麼傳遞到 server 端,怎樣溝通有效期等都有寫在規範內,Incrementally Better Cookies 的想法也是從這份草案中的特性而來。

這些草案都還蠻有趣的,至於會不會定稿成為規範甚至大家都開始實做,目前就還很難斷定了。


瀏覽器多樣性 Browser Diversity

前陣子大事就是微軟要放棄自家的 EdgeHTML 引擎,轉用 Chromium 專案為基礎來開發新版的 Edge Browser 了,風聲剛出來的時候,我注意到微軟官方完全沒做出回應,也沒有任何微軟員工出來講話,加上有些媒體早就發現到有 Edge 的開發成員在貢獻 Chromium,我就覺得是真的了,後來十二月六日微軟正式回應,還有一份比較長的聲明,Mozilla 也有回應,其實這件事情對於網路生態算是很大的衝擊,不過一般使用者可能沒什麼感覺,加上都沒看到中文的文章寫這件事情的影響,所以只好我來寫一下了。

首先,我講可能沒什麼公信力,所以可以直接來看一下 Google(?) 其中一集 HTTP 203 短片,標題是 Browser Monoculture,Monoculture 剛好是 Diversity 的相對,mono 是單一,單聲道或是黑白影像都是用 mono,culture 就是文化,Monoculture 的意思自然可以明白:

事實上,當微軟放棄 EdgeHTML 引擎之後,現在整個生態圈只剩下 Firefox 的 Gecko 引擎和 WebKit 家族(Safari 的 WebKit 和 Chrome 的 Blink ),而 WebKit 家族現在的市佔率已經是超過八成的獨大局面,行動裝置領域更是嚴重,如果扣除 iOS 的 Mobile Safari 則幾乎都是 Chrome 的天下了,幾乎是回到 IE 壟斷的時光,不過其實我覺得現在狀況又比那時候更險峻一點,有兩個問題:第一個是現在的 Web Platform 已經太複雜了,HTML 本身還算是單純的部分,但是各種 CSS、Web API 的推陳出新,再加上安全性、親和力、網路連線、Extensible Web 等等,到底有多少東西呢?可以參考我之前貼過的 Web Platform 那篇文章中 Google Chrome 的 Platform Status,光這邊登記在案的,現在就有 1255 個功能,還有不少面向的東西不會列在這邊,像是效能、開發工具、WebDriver 等,其實我已經不認為現在有什麼其他第三方勢力還有辦法維護一個獨自的瀏覽器引擎了,就算是 Google 一開始也是從 WebKit,Mozilla 也是從 Gecko 來發展,微軟今天放棄繼續開發 EdgeHTML 之後,可能過一兩年就會讓它難以再次跟上標準的發展,其實微軟當年能從把 IE 重構成現在的 Edge 我覺得實在很厲害了,不過未來這種事情難度只會越來越高。

第二個是如果 Chrome 已經佔有率這麼高,它是不是可以自己開始亂搞加功能呢?就像是以前的 IE。答案其實是可以,只是現在手法已經進化了,以下舉個例子,不過先消毒,我不是指控 Google Chrome 團隊這件事是 be evil,而是假設要 be evil,這已經是可行的方法,或是換個角度,他們其實不覺得自己在 be evil,只是結果就是這樣了。

我要舉的例子是前兩週 Chrome 發表了他們支援 Background Fetch 這個新標準的消息,我第一時間反應其實是,WTF 我怎麼完全沒聽過這東西,然後我就去查查怎麼回事,然後了解到:

  1. 看介紹大概就了解這個需求確實是有的
  2. Chrome 外其他家都還沒有說要支援(根據 Chrome Platform Status
  3. 標準文件的兩位編輯都是 Google 的人,主要應該是 Jake Archibald
  4. WICG 那邊是去年二月也是由 Jake Archibald 提的,然後 W3C 那邊根據 blink-dev 的正向回饋就決定接收提案了

這狀況有點讓我聯想到「進化的獨裁者」,一切該有的過程其實都有,但是就都是他們的人,自己提案說有這需求,有寫好文件了,在自己家的討論區得到正向回饋,然後實做起來馬上就有市場 80% 的支援度了,這樣要不要直接算正式的網路標準了,其他家(aka Firefox)又情何以堪。事實上 Chrome 這樣衝網路標準的狀況也好一陣子了,早在 2015 年 ppk 就已經有提出對於標準發展太快的疑慮而發了一篇 Stop pushing the web forward

而除了快速的發展新功能之外,還有一種狀況是擱置他們覺得不重要的 Web API 開發,然後因為已經獨大了,所以開發者就算很想要這個新的 Web API 也是無能為力,這其實也就是 HTTP 203 短片有提到的,多樣性意味著開發者有選擇的權力,而有這個力量才能讓兩邊對等。

而除了這兩個問題之外,也有人提出我之前沒想過的安全性問題,剛好就在微軟發佈消息之後沒多久就爆發出 SQLite 漏洞,ZDNet 的標題提到影響到所有以 Chromium 為基礎的瀏覽器,這也是一個我之前沒想到過的問題,如果獨大的軟體有嚴重的漏洞,那一下就直接影響超多人,不過其實這次的漏洞連 Firefox 也有受影響,然後也還好不是直接可以遠端就下手的漏洞,其實佔有率高的軟體或服務都一直是駭客的目標,想必 Chromium 的 Blink 核心之後勢必會更加受到駭客關注吧。

這陣子這個圈子很多人都已經發表過看法了,像是 ppkZeldman 都出來發表意見,如果有人不知道這兩位是誰,趁機介紹一下,Jeffery Zeldman 是 Designing with Web Standards 的作者,A List Apart(A Book Apart, An Event Apart 等)和 Web Standards Project 的發起人,也是當年推動瀏覽器實做應該回歸網路標準的意見領袖,ppk 也是那個時期蠻活躍的,做了很多相容性測試,著有 ppk on JavaScript,當年是很棒的入門書。大部分的人其實都是針對 monoculture 論述,然後建議大家現在就開始行動,包括確保你的網站支援 Firefox、開始使用 Firefox 等等,不過 Lea Verou 有則評論則是針對那些覺得少一個瀏覽器要測試很高興的開發者,講的比較重:

至於事主之一的 Google 則就裝死當沒他的事。總之,現在雖然 Firefox 還有個 10% 左右的佔有率,光看數字還比 IE 那時候好,但是我卻覺得情勢更加險峻,很難再有新的競爭者出來,只能希望 Google don't be evil,還有 Mozilla 能夠堅持下去,真是有點想念還有五大主流瀏覽器的時候啊。


此類別所有文章