Safari 10 for Developer

Safari 10 跟著 macOS 一起出來了,這次更新了不少東西(對於網頁開發者來說),Apple 也依舊放了一份文件在他們的 Developer Library 裡面,以下列出我覺得比較有趣的:

CSP 2.0

CSP 2.0 和之前的版本相比,最主要是多了非常多可以控制的權限,也有幾個名稱有改掉,不過基本上格式是相容的。

Shadow DOM

Shadow DOM 1.0 標準,這也讓 Web Component 的理想又往前賣進一步了。

ES 6

號稱支援度 100%,看起來是依據ECMAScript compatable table的,不過在 module 的面前,還沒有真的 100% 的啊,另外主流瀏覽器其實支援度都蠻高了,之前 Edge 還放話說領先的,沒想到現在就已經被 Safari 10 和 Chrome 超過了,而 Chrome 看來也之差 tail call 而已,接下來應該又要開始效能比拼了吧。

Inline and Auto Video Playback in iOS

這也是等很久的功能,之前就有先開放靜音影片能直接在 iOS Safari 上自動播放,主要的考量是,gif 和 mp4 相比,還是 gif 比較吃資源啊。

ES Internationalization

ECMA-402 支援,這也是希望快點普及的東西啊,不然數字、日期什麼的搞本地化實在很麻煩。

WOFF 2.0 Support

令人意外的有點慢,不過還算很有誠意的把很新的CSS Font Loading Module Level 3的 API 做好了。

#RRGGBBAA

新的 CSS color 格式,也是前陣子才 propose 出來而已,這樣以後就可以讓 CSS 裡的顏色格式統一點了。

Right-to-Left Language Support

主要是 RTL 頁面 scrollbar 的位置終於會換邊了。

Media Query for Wide Color Gamut Support

廣色域的 CSS media query,主要是因為最新的 iMac 和 iPhone 7 都有支援 P3 色域了。

WebDriver Support

主流瀏覽器最後一個支援的...

Apple Pay for the Web

這真的蠻兇狠的,不過 Google 也有在 Android 做類似的就是了。

大概就這些了,其實也列出超過一半的項目了,Safari 這種更新頻率其實比起其它幾家來說吃虧不少,不過還是一直有跟上最新進度,其實也蠻厲害的,更何況現在 Google 都把人拉到 Blink 去,有回到 Webkit 的貢獻似乎比例上不高。


MRT & GIT

今年 COSCUP Lightening Talk 分享的主題是 GIT & MRT,主題就是紀錄我之前用 GIT 線圖畫台北捷運路線圖的過程,投影片在上面,這篇文章是補充一下被省略的部分和講不完的部分,當天我有用 Ricoh Theta 錄影,也丟上 YouTube 了:

閱讀「MRT & GIT」全文

Input Event

Playtime Credit Card,

今天做了一個特殊的 input 欄位,其實目標只是做成類似像輸入信用卡號那樣,輸入1234完,準備要輸入5的時候,會在4後面補上一個-,變成1234-5,不過我預期做的完美一點,所以考慮了很多狀況,例如:

  • 複製貼上沒有-的資料後會自動格式化
  • 已經輸入一部分資料後,游標移到前面插入資料也會正確格式化
  • 直接用DELBackspace來刪除資料,要讓使用者感覺不到-
  • 先選取一些字元然後用DELBackspace甚至是剪下來刪除資料後會重新格式化
  • 以上幾種操作都不會讓游標亂跳

簡單看過目前一些信用卡相關的 library,在卡號輸入的部分是沒有全部達到的,要達成這些目標,幾乎是等於每個使用者的操作都要攔截下來,然後要抓到當欄位內的值,會用到的事件包括了 keyup、keydown、paste 和 input,等,其中本來我對於一般使用者敲打鍵盤輸入的事件是用 keyup,keyup 事件後會判斷游標位置和輸入的內容,如果需要的話就加上-,然後調整游標位置,通常是 +1,弄好後測試一陣,發現如果按鍵輸入很快的話,游標位置會亂掉,應該要 +1 的卻錯過了,深入除錯一陣子之後發現,keyup 事件其實和欄位內的 value 變更是非同步的,所以不能確保 keyup 事件拿到的欄位值是正確的,能確保欄位值正確的,其實是input 事件,不過 input 事件沒有 keyCode,所以只能自己判斷輸入了什麼,另外刪除內容時也不會觸發 input,還好DELBackspace是用 keydown 事件來處理,兩邊剛好錯開了。

雖然 input 事件似乎很好用,不過其實它在早期的時候支援度是不太好的,算是比較新的事件,有類似狀況的還有一個是 change 事件,我的印象中是某些瀏覽器的行為會不太正確,所以其實我一直都還不太使用,至於 input 事件,我則是需要在不支援的瀏覽器中 fallback 到 keyup 事件,所以就會需要偵測,找了一下在 Modernizr 有支援,仔細看一下內容其實可以發現不是很好偵測,然後我也不是很喜歡 Modernizr 的介面,所以目前用的是在 ModernizrIssue 210裡面 AndyE 提供的版本,稍微精簡一些:

var inputSupport = "oninput" in document.body || checkEvent(document.body);
/*
   The following function tests an element for oninput support in Firefox.  Many thanks to
        http://blog.danielfriesen.name/2010/02/16/html5-browser-maze-oninput-support/
*/
function checkEvent(el) {
    // First check, for if Firefox fixes its issue with el.oninput = function
    el.setAttribute("oninput", "return");
    if (typeof el.oninput == "function")
        return true;

    // Second check, because Firefox doesn't map oninput attribute to oninput property
    try {
        var e  = document.createEvent("KeyboardEvent"),
            ok = false,
            tester = function(e) {
                ok = true;
                e.preventDefault();
                e.stopPropagation();
            }
        e.initKeyEvent("keypress", true, true, window, false, false, false, false, 0, "e".charCodeAt(0));
        document.body.appendChild(el);
        el.addEventListener("input", tester, false);
        el.focus();
        el.dispatchEvent(e);
        el.removeEventListener("input", tester, false);
        document.body.removeChild(el);
        return ok;
    } catch(e) {}
}

測試困難的主因是 Firefox 4 有 bug,所以需要真的建立一個 input 元件,然後用完整模擬 input 事件。然後雖然這個版本的比較精簡好懂,不過之後還是會因為 license 的關係改用 Modernizr 的版本吧。至於我做的 input field 呢,現在當然還是公司資產,大概要等我有空在假日重寫一個 Credit Card 的版本才會放出來吧。


Modern HTML Email Development

今天在 Modern Web 分享的主題

其實最主要是想介紹MJML這個工具,不過最後介紹的篇幅有些不夠,有些可惜,話說今天設備也有些狀況,一是投影機解析度和預期的不一樣,二是無線麥克風聲音會延遲,對於講者來說還蠻干擾的,最後時間還剩的比預期多,覺得愧疚啊。

最後附上這次介紹的一些資源的連結,方便取用:


JSON Web Token

大丸百貨

之前的JSON Universe那篇文章在寫的時候,還沒發現到有這東西,直到上個月才發現到JSON Web Token(JWT) 這個標準,研究過後覺得要單獨介紹一下,不過由於相關的標準有好幾個,花了些時間才搞清楚各個標準之間的關係;這一系列標準是由JOSE(JSON Object Signing and Encryption) Working Group所制訂的 RFC 標準,目前包括了:

共五個 RFC 標準,事實上,JSON Web Token 是要最後談到的;這一系列標準的目的是提供一個標準的協定,用在傳輸 JSON 資料時提供可靠性(簽章、signature)和安全性(加密、encryption),眼尖的人可能發現了,怎麼沒有 JOSE 的文件呢?事實上是真的沒有,而且也沒官方文件清楚解釋 JOSE 到底是什麼,最常看到的詞就是 JOSE Header 了,思考許久後才理解,JOSE 其實包括了兩種格式,分別是 JSON Web Signature(JWS) 和 JSON Web Encryption(JWE),JWS 只是加上驗證用的簽章,其實內容是明碼的,JWE 才是真的有把傳輸的資料加密過,至於簽章和加密用的演算法則是用 JWA 格式來紀錄,然後需要用到的 key,例如用非對稱加密保護加密內容的 key 給收信端,這時則是用 JWK 格式來記錄要使用的 public key,而這些資訊就是 JOSE Header 的內容了,JWK 和 JWA 都是很簡單的格式,基本上就是一個物件,然後有定義好的屬性:

{
  "kty":"EC",
  "crv":"P-256",
  "x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
  "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0",
  "kid":"Public key used in JWS spec Appendix A.3 example"
}

例如這個 JWK 文件範例中的kty代表的是 Key Type;crvxy則是橢圓曲線加密(ECC)類演算法會用到的參數;kid則是自訂的 Key ID,用來在一堆 JWK 當中尋找所要的 key 使用;其它還有像是 X.509 憑證驗證會需要的資訊,各種加密演算法會用到的 Initialization Vector、Salt 等,都有定義好的屬性名稱。

JWS 和 JWK 就比較複雜些了,以 JWS 來說,你會先有要傳輸的資料 payload,然後一組 meta data,又稱為 JOSE Header,內容基本上就是 JWA + JWK + 一些基本的屬性,像是ctytyp

{
  "typ":"JWT",
  "alg":"HS256"
}

這就是一個最簡單的 JOSE Header,它說明傳輸的資料內容和簽名用的 HMAC 演算法,然後這個 JOSE Header 和 payload 要分別轉成base64url編碼,其實和 base64 沒差很多,就先把 JSON String 轉成 base64 encoding string 後,把 padding 的=都拿掉,然後+-取代,/_取代。例如ab?ab這個 ASCII 字串,用 base64 encoding 就會變成:

YWI/YmE=

用 base64url 的話就變成:

YWI_YmE

然後現在我們有一個 JOSE Header 和一個要傳輸的 JSON payload,以 base64url 編碼呈現並且用.接起來:

BASE64URL(UTF8(JOSE Header)) || '.' || 
BASE64URL(JWS Payload))

接著把這個字串拿去用 JOSE Header 裡面指定的 HMAC 演算法搭配一組 key 來算出簽章(signature),至此我們就有了 JWS 三樣必須的元素了:

  • JOSE Header
  • Payload
  • Signature

JWS 文件中定義了兩種格式可以用來傳輸這三個元素,第一種是精簡格式(Compact Serialization Syntax),格式很簡單,就和上面算 signature 用的格式一樣,只是現在多加了 signature 在後面,一樣用 base64url 形式:

BASE64URL(UTF8(JWS Protected Header)) || '.' ||
BASE64URL(JWS Payload) || '.' ||
BASE64URL(JWS Signature)

長的會看起來像是:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

另外一種則是 JSON 格式(JSON Serialization Syntax):

{
  "payload":"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9",
  "signatures":[
     {"protected":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
      "signature":"TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"}]
}

這種形式其實是最完整的版本,還可以加上 public header (沒 signature 驗證)和多個 signature;另外也有 flatten 版,只能放一個 signature:

{
  "payload":"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9",
  "protected":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
  "signature":"TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ"
}

雖然有 JSON 格式的 JWS,不過 精簡格式目前應該是最廣為通行的,一來是它資料量比較小,二是它比較方便在不同環境下傳輸使用,例如後面會提到的,放在 HTTP Header 內,如果沒有特殊需求要多個 signature,實在很沒有用 JSON 格式的需求。

最後,JWS 還有一個特殊的 case,就是它其實允許不加上簽章的,使用這組 JOSE Header:

{"alg":"none"}

然後 signature 是空字串,所以精簡格式的就會變成:

eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.

最後是.結尾。

JWE 和 JWS 的狀態其實也很像,只是三個元素變成五個,包括了:

  • JOSE Header
  • Encrypted Key
  • Initialization Vector
  • Ciphertext
  • Authentication Tag

其中 JOSE Header 和 JWS 的內容差不多,Encrypted Key 和 Initialization Vector(IV) 是加密時的輸入,這邊的 Encrypted Key 是一把加密過的 Key,被加密保護的 Key 又稱為 Content Encryption Key(CEK),是實際上用來加密保護內容時所使用的 Key,這把 CEK 和 IV 都是亂數產生的,那又有一個問題是,用什麼 Key 加密 CEK 來產生 Encrypted Key 呢?這邊建議的是用非對稱加密,拿收信方的 public key 來加密,當然 JOSE Header 裡面也可以塞進 x.509 的相關資訊用來確保 public key 的正確性;最後兩個,Ciphertext 和 Authentication Tag 則是加密的輸出,Authentication Tag 是 authentication encryption 會產生的,用來驗證內容正確性的資訊,就像是 JWS 的 signature 一樣用途,主要也是避免 Ciphertext 被用中間人攻擊替換掉,不過我還不太清楚如果可以偷到 key 偽造出 Ciphertext,是怎樣會沒法同時有另外一組 Authentication Tag 就是了。

然後一樣有精簡格式和 JSON 格式,精簡格式看起來就如下:

eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je81860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIjf7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZdvTiFvSkQ

注意找的話就可以發現四個.把資料切成五段。

最後終於要來介紹 JSON Web Token(JWT)了,JWT 是什麼呢,它其實就是一個 JOSE 的應用,一句話來說,就是使用 JWS 或 JWE 來做現在網路服務的身分認證協定中常見的 token(權杖)的傳遞。所以 JWT 其實就是規範了一組 JSON 資料的屬性(claim),和身分認證相關,然後要求這個 JSON 資料要用 JWS 或 JWE 來傳輸提供保護,這些預先定義好的屬性有:

  • iss, Issuer
  • sub, Subject
  • aud, Audience
  • exp, Expiration Time
  • nbf, Not Before
  • iat, Issued At
  • jti, JWT ID

都是非常 meta 的 token 屬性,這些名稱基本上是從OpenID Connect那邊來的,除了這些定義好的屬性之外,還可以加上其它自訂的資料,只是這些已經被定義且註冊好的名稱不能另做他用,OpenID Connect 也有不少個人資料的屬性已經註冊上 IANA 了,像是first_namecountry之類的 profile 資訊,有一種使用 JWT 的方法就是直接把個人 profile 存在客戶端,server 只要驗證簽名是否正確,這樣一個好處是 server 不用保存 session 資訊,減少很多資源的需求,實做起來其實複雜度也比較低。另外由於是用作 token 之用,自然也可以當成 OAuth 的 token 使用,這部分資訊在RFC-7523這份文件有說明,至於要如何使用 OAuth token 則是在RFC-6750有介紹,比較常見的是放在 HTTP Auth Header 裡面:

Authorization: Bearer eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.

之前鴨七也有整理過中文的說明,看起來比較輕鬆,而且說明很完整(不過我承認我沒有從頭看到尾)。

目前搜尋 JWT 一定會看到一個網站jwt.io,這個網站用淺顯易懂的方式來介紹JWT,把比較複雜的關係,像是 JWS、JWE 等等都隱藏起來幫助瞭解,還有一個線上除錯工具和不同語言的 library 整理,不過除錯工具只有支援 JWS,另外也有和一些其它類似標準做比較,還蠻值得看一看的,這個網站是由Auth0提供的,他們其實就是一家專門提供身分認證服務的公司,似乎都已經轉到使用 JWT 了,在 jwt.io 有提到他們似乎是把他用作 stateless 的 token 來用,我對這間公司之前印象是還不錯,一來是 API 文件看起來還蠻不錯的,另外有不少的開放原碼專案,當然有一些是串接他們家服務用的 library 啦。

PS. 對密碼學還沒很熟悉,有誤歡迎指正~


➡ 看看其它文章