就在團隊完成了HMAC
認證實作,以符合金融公司的高安全性要求後,又一間金融公司希望使用這個新的API
,但這次卻出了另一個認證的難題給工程團隊。因此,團隊又聚在一起討論,該如何完成這次的任務。
Lala:這次安全性要求更高了,為確保資訊安全,所提供之服務須經安全性連線,認證授權機制之機敏資訊除可供定期輪替,金鑰亦需採用非對稱加密
,並確保逾時後授權失效之機制....。
Ariel:非對稱加密
,這樣HMAC
好像又不符合要求了....
Sam:沒錯,雖說HMAC
採用的是每一次發送請求時,都會變更認證內容,但是的確是使用對稱金鑰的形式進行的。
Ariel:我記得我閱讀過文件,主流是不是要使用OIDC
(Open ID Connection)協議,那是不是就是非對稱金鑰?
Sam:對,那是最經典而且最主流的認證授權方式,而且OIDC
又有多種認證流程可以使用,的確是符合需求....但,那個plugin
....要企業版才有。
Lala:那這個客戶的需求,是不是就沒辦法承接了?
Sam:這倒也不是,其實OIDC
底層是透過JWT
(JSON Web Token)實作而成的,而JWT
的 plugin
是不用錢的,應該也是可以符合這次客戶的需求。而且也剛好藉這次接觸JWT
的機會,讓我們一起了解OIDC
的其中一種 flow。
Ariel:聽起來頗有趣的,那就開始吧。
圖 27-1 JWT
JWT
(JSON Web Token)是一種 基於 JSON 格式的權杖(token),常用來在不同系統或服務間攜帶身份認證或授權資訊。
它由三個部分組成(以 . 分隔):
header.payload.signature
Header:
說明演算法與 token 類型,例如 { "alg": "HS256", "typ": "JWT" }。Payload:
使用者相關資訊與權限聲明(claims),例如 sub, exp, role, iss等。Signature:
根據 header 與 payload 經過金鑰簽章,確保內容未被竄改。補充:
圖27-2 token.dev
為了讓讀者更能了解JWT的組成,推薦 token.dev這個網站,可以快速的產生一個範例的token
讓讀者可以學習,並且會將token
的各個組成解碼後以 JSON的形式觀看,是個學習與實驗的好網站。
網路上如果去翻找程式碼範例,其實也不乏各網站自己實作JWT的發行與管理,大多都透過 API endpoint 進行。然後在 API 請求時,帶在 http header 的 authorization
欄位,以 bearer header.payload.signature
形式被帶入。
這種做法最大的優點就是,token 的 payload
中就有足夠資訊能夠識別登入者以及相對應的權限,不需查詢資料庫。這種被稱之為無狀態 (stateless)的身分認證與授權方式,可以讓伺服器不用維護 session。
這也是前面在談到與撰寫網頁系統最大的認證授權的不同之處,但也因為無狀態(stateless)的特性,代表可以在多個服務之間跳轉,服務依舊可以繼續提供。相較過往使用 session的方式,當如果伺服器有多台時,通常會需要跟網管去協調網路設備的Session Persistence
,不然只要跳台就會需要使用者重新登入,體感就會變得很不好。
另外,進入了微服務的架構下,除非在所有微服務的南北向入口統一管理session
,不然要個別處理session
幾乎是不可能的任務。因為微服務的容器設計是需要具備自我修復的能力,這表示容器隨時都有可能重新啟動。如果個別管理session
卻又隨時會被 kill,那應該會是一場大災難。
在密碼學中,除了各式各樣不同的演算法外,主要還是分成了對稱金鑰
與非對稱金鑰
兩種大類型。用簡單的比喻敘述,並稍微闡明各自有缺點:
對稱金鑰
:雙方就像共用一把鎖鑰匙,都必須知道同一把鑰匙。例如前面示範的apikey
就是一種經典類型,或是hamc-auth
同樣也是,這都是雙方共同知道同一把金鑰,進行溝通的加解密與驗證。token
來進行存取。非對稱金鑰
:比較像去銀行領款時,取款人用印章蓋了取款單上面的章,而銀行櫃台則是用曾經被留存的印鑑單核對,核對正確就能確定印章沒被偽造。但銀行人員卻無法偽造取款的印章後,跑去銀行領錢。
圖27-3 非對稱與對稱金鑰認證差異
在kong
的認證方式中,JWT
同時支援了對稱與非對稱的認證方式,與其他認證方式的設定方式類似,都可以彈性的在route
或service
實作認證的要求,另外在Consumer
(使用者/應用程式)綁定,藉由 Consumer
的 secret
或 public key
進行驗證。
- name: case_ELK_JPG_AuthZN_weather
url: http://case_ELK_JPG_AuthZN_weather:8080
routes:
- name: case_ELK_JPG_AuthZN_weather
paths:
- /case_ELK_JPG_AuthZN_weather/
plugins:
- name: key-auth
...內容省略...
- name: acl
...內容省略...
- name: TestCase_HMAC
paths:
- /FinancalHMAC/
plugins:
- config:
name: hmac-auth
...內容省略...
- name: acl
...內容省略...
- name: TestCase_JWT
paths:
- /FinancalJWT/
plugins:
- config:
anonymous: null
claims_to_verify:
- exp
cookie_names: []
header_names:
- authorization
key_claim_name: name
maximum_expiration: 4800
run_on_preflight: true
secret_is_base64: false
uri_param_names:
- jwt
enabled: true
name: jwt
protocols:
- grpc
- grpcs
- http
- https
- name: acl
config:
allow:
- FinancalJWT
...acl 內容省略...
這次以
ironman2025\case_ELK_JPG_Route_JWT\
這的資料夾做為示範的範例標的。
前面的故事說到,是共用同一個service
,繼續建立route
但是使用不同的認證授權方式。所以請讀者注意到上方的kong.yml
設定內容(ironman2025\case_ELK_JPG_Route_JWT\1.Kong_declarative\declarative\kong.yml
),可以注意到同樣是基於 case_ELK_JPG_AuthZN_weather 這個 service下,又建立了一個名為 TestCase_JWT的 route,而且在其之下建立了兩個 plugin,分別是JWT
與acl
。
簡單說明一些值得注意的設定項目:
claims_to_verify: - exp
exp
(過期時間),表示 Kong 會驗證 token 是否過期。nbf
(not before)。header_names: - authorization
JWT
token 可以從 HTTP Header 的 Authorization
讀取。Authorization: Bearer <jwt_token>
。key_claim_name: name
"name": "HS256_Sam_Key"
,Kong 就會拿 HS256_Sam_Key 去對應到 consumer
在jwt_secrets所設定的key,以此來辨識是哪一位consumer
。maximum_expiration: 4800
exp - iat
超過 4800 秒,Kong 會拒絕,避免有人簽發「超長效期 token」。ACL
的 group
:FinancalJWT
。如同其他的認證設定方式一樣,在 route
被設定好認證方式之後,接著就是在 consumer
部分要進行設定,同樣來看kong.yml
。
- acls:
- group: FinancalJWT
username: sam@samnchiuhotmail.onmicrosoft.com
custom_id: sam
jwt_secrets:
- algorithm: RS256
key: RS256_Sam_Key
rsa_public_key: |-
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6S7asUuzq5Q/3U9rbs+P
kDVIdjgmtgWreG5qWPsC9xXZKiMV1AiV9LXyqQsAYpCqEDM3XbfmZqGb48yLhb/X
qZaKgSYaC/h2DjM7lgrIQAp9902Rr8fUmLN2ivr5tnLxUUOnMOc2SQtr9dgzTONY
W5Zu3PwyvAWk5D6ueIUhLtYzpcB+etoNdL3Ir2746KIy/VUsDwAM7dhrqSK8U2xF
CGlau4ikOTtvzDownAMHMrfE7q1B6WZQDAQlBmxRQsyKln5DIsKv6xauNsHRgBAK
ctUxZG8M4QJIx3S6Aughd3RZC4Ca5Ae9fd8L8mlNYBCrQhOZ7dS0f4at4arlLcaj
twIDAQAB
-----END PUBLIC KEY-----
secret: xxx
- algorithm: HS256
key: HS256_Sam_Key
secret: HS256_Sam_Secret_HS256_Key_minimum_size_of_256
acls
group: FinancalJWT
Consumer
加入到 FinancalJWT 這個 ACL
群組。方便在 Kong
裡面用 ACL
Plugin 控制哪些群組可以存取 API。username
Consumer
的「顯示名稱」,這裡是sam@samnchiuhotmail.onmicrosoft.com
。onmicrosoft.com
的帳號,容筆者先賣個關子。custom_id
sam
,代表這個 consumer
在外部系統的唯一識別。jwt_secrets
JWT
金鑰驗證方式,讓同一個 Consumer
支援 RS256
和 HS256
。RS256
algorithm: RS256
這裡指定使用 RSA 非對稱演算法驗證。key : RS256_Sam_Key
這是 Kong
用來識別這組金鑰的名稱。rsa_public_key
: RSA 公鑰。
RS256
簽署時,Kong
會用這個公鑰來驗證。secret: xxx
→ 雖然有 secret 欄位,但在 RS256 模式下實際上不會用到(主要還是依賴公鑰,而且這欄位不保留格式檢查器會一直提示錯誤)。HS256
algorithm: HS256
指定 HMAC-SHA256 對稱演算法。key: HS256_Sam_Secret_HS256_Key_minimum_size_of_256
Kong 用來識別這組金鑰的名稱(建議最小長度要超過256 bit)。secret: HS256_Sam_Secret
簽署與驗證都會使用這組密鑰。非對稱金鑰示範檔案:
ironman2025\case_ELK_JPG_Route_JWT\1.Kong_declarative\declarative\example_JWT.yml
這個範例筆者準備了非對稱金鑰,放在示範專案上面的路徑中,供讀者參閱。但切記,如果讀者自己要在公司中使用,請針對每一位
consumer
建立不同的金鑰,可別使用筆者的示範金鑰,然後廣發到廣大使用者中。這樣不僅無法識別真正的使用者,同時風險也極高。至於產生非對稱金鑰的方式,讀者可以去找找網路上有一些實作範例,或是自行撰寫程式產生也可以。
今天筆者用了一些篇幅來說明關於JWT
設定中,對稱與非對稱金鑰的特點。同樣的也將這兩種類型在Kong
上面的設定都說明。接下來明天將進入實作的階段,來確認JWT
驗證方式在Kong
的consumer
中的實作方式。
讓我們明天見~