- 1. ภาพรวม
- 2. วัตถุประสงค์ของ Workshop
- 3. สิ่งที่ต้องมี
- 4. Architecture
- Part 1: สร้าง API บน Kong
- Part 2: เปิด JWT Authentication
- Part 3: Consumer และ JWT Credential
- Part 4: สร้าง JWT
- Part 5: เรียก API ด้วย JWT
- Part 6: ตรวจสอบ Header ที่ Kong ส่งไป Upstream
- Part 7: Negative Testing
- Part 8: JWT Flow แบบละเอียด
- Part 9: Plugin Scope
- Part 10: HS256 กับ RS256
- Part 11: Declarative Configuration ตัวอย่าง
- Part 12: Security Best Practices
- Part 13: Troubleshooting
- Part 14: Cleanup
- Part 15: แบบฝึกหัด
- Part 16: สรุป
- อ้างอิง
Workshop ภาษาไทยสำหรับเรียนรู้การป้องกัน API ด้วย Kong API Gateway JWT Plugin โดยใช้ Consumer, JWT Credential และการตรวจสอบ
expclaim
#1. ภาพรวม
JWT (JSON Web Token) เป็นมาตรฐานสำหรับส่งข้อมูลที่ผ่านการลงลายมือชื่อดิจิทัลระหว่างระบบ โดย Kong API Gateway สามารถใช้ JWT Plugin เพื่อตรวจสอบ token ก่อนอนุญาตให้ request ผ่านไปยัง upstream API
ลำดับการทำงานโดยทั่วไปคือ
Client
|
| Authorization: Bearer <JWT>
v
Kong API Gateway
|
|-- ตรวจ Route
|-- อ่าน JWT
|-- อ่านค่า iss
|-- หา JWT Credential ของ Consumer
|-- ตรวจ Signature
|-- ตรวจ exp / nbf ตามที่ตั้งค่า
|
+---- Invalid ----> 401 Unauthorized
|
+---- Valid ------> Upstream API
Kong JWT Plugin รองรับการรับ JWT จาก HTTP header, query string และ cookie โดยค่าเริ่มต้นจะตรวจ header Authorization และใช้ claim iss เป็น key สำหรับระบุ JWT credential
ใน production ควรให้ Auth Service หรือ Identity Provider เป็นผู้ออก JWT ส่วน Kong ทำหน้าที่ตรวจสอบ token และบังคับใช้นโยบายก่อนส่ง request ไปยัง backend
#2. วัตถุประสงค์ของ Workshop
หลังจบ Workshop จะสามารถ
- สร้าง Gateway Service และ Route
- เปิดใช้งาน JWT Plugin บน Route
- สร้าง Consumer
- สร้าง JWT Credential แบบ HS256
- สร้าง JWT สำหรับทดสอบ
- ส่ง JWT ผ่าน
Authorization: Bearer - ทดสอบกรณีไม่มี token
- ทดสอบ token ถูกต้อง
- ทดสอบ token หมดอายุ
- เข้าใจแนวทางนำ JWT ไปใช้งานจริง
#3. สิ่งที่ต้องมี
Workshop นี้สมมติว่า Kong Gateway ทำงานอยู่แล้ว โดยใช้
| Component | URL |
|---|---|
| Kong Proxy | http://localhost:8000 |
| Kong Admin API | http://localhost:8001 |
| Upstream | https://httpbin.org |
ตรวจสอบ Kong Admin API
curl http://localhost:8001
ถ้า Kong ทำงานปกติจะได้ข้อมูล version และ configuration กลับมา
#4. Architecture
flowchart LR
C[Client] -->|Authorization: Bearer JWT| K[Kong API Gateway]
K --> R[Route]
R --> J[JWT Plugin]
J -->|Read iss| CR[JWT Credential]
CR --> CO[Consumer]
J -->|Valid| U[Upstream API]
J -->|Invalid / Expired| X[401 Unauthorized]
องค์ประกอบหลักมีดังนี้
- Service แทน upstream API
- Route กำหนด URL ที่ Client ใช้เรียก
- Plugin เพิ่มความสามารถให้ Service/Route
- Consumer แทน client/application/user ที่ Kong รู้จัก
- JWT Credential เก็บข้อมูลที่ใช้ตรวจสอบ JWT ของ Consumer
#Part 1: สร้าง API บน Kong
#5. สร้าง Gateway Service
สร้าง Service ชื่อ demo-api
curl -i -X POST http://localhost:8001/services \
--data name=demo-api \
--data url=https://httpbin.org
ตรวจสอบ Service
curl http://localhost:8001/services/demo-api
#6. สร้าง Route
สร้าง Route ชื่อ demo-route
curl -i -X POST http://localhost:8001/services/demo-api/routes \
--data name=demo-route \
--data 'paths[]=/api' \
--data strip_path=true
เมื่อ strip_path=true
Client:
http://localhost:8000/api/get
Kong ส่งไป:
https://httpbin.org/get
ตรวจสอบ Route
curl http://localhost:8001/routes/demo-route
#7. ทดสอบ API ก่อนเปิด Authentication
curl -i http://localhost:8000/api/get
ควรได้ HTTP status ประมาณ
HTTP/1.1 200 OK
ตอนนี้ API ยังเรียกได้โดยไม่ต้องส่ง JWT
#Part 2: เปิด JWT Authentication
#8. เปิด JWT Plugin บน Route
เราจะเปิด JWT เฉพาะ demo-route
curl -i -X POST http://localhost:8001/routes/demo-route/plugins \
--header "Content-Type: application/json" \
--data '{
"name": "jwt",
"config": {
"header_names": ["authorization"],
"key_claim_name": "iss",
"claims_to_verify": ["exp"]
}
}'
ความหมายของ configuration
{
"header_names": ["authorization"],
"key_claim_name": "iss",
"claims_to_verify": ["exp"]
}
| Option | ความหมาย |
|---|---|
header_names |
Header ที่ Kong ใช้ค้นหา JWT |
key_claim_name |
Claim ที่ใช้ระบุ credential |
claims_to_verify |
Registered claims ที่ต้องการตรวจเพิ่ม |
ใน Workshop นี้กำหนดให้ตรวจ exp เพื่อไม่ยอมรับ token ที่หมดอายุ
Kong JWT Plugin รองรับการตรวจ registered claims exp และ nbf
#9. ทดสอบโดยยังไม่ส่ง JWT
curl -i http://localhost:8000/api/get
ควรได้
HTTP/1.1 401 Unauthorized
ตัวอย่าง response
{
"message": "Unauthorized"
}
แสดงว่า JWT Plugin เริ่มทำงานแล้ว
#Part 3: Consumer และ JWT Credential
#10. สร้าง Consumer
สร้าง Consumer ชื่อ workshop-user
curl -i -X POST http://localhost:8001/consumers \
--data username=workshop-user
ตรวจสอบ
curl http://localhost:8001/consumers/workshop-user
#11. สร้าง JWT Credential
สำหรับ Workshop จะใช้ HS256
curl -i -X POST http://localhost:8001/consumers/workshop-user/jwt \
--data algorithm=HS256 \
--data key=workshop-key \
--data secret=workshop-super-secret-change-me
ค่าที่สำคัญ
algorithm = HS256
key = workshop-key
secret = workshop-super-secret-change-me
#key ไม่ใช่ secret
ใน JWT payload เราจะกำหนด
{
"iss": "workshop-key"
}
Kong จะนำ iss ไปค้นหา JWT Credential ที่มี
key = workshop-key
จากนั้นจึงใช้ secret ของ credential นั้นตรวจ signature
#12. ตรวจสอบ JWT Credential
curl http://localhost:8001/consumers/workshop-user/jwt
อย่าเปิดเผย JWT secret ใน production และไม่ควร hard-code secret ใน source code
#Part 4: สร้าง JWT
#13. ติดตั้ง PyJWT
python -m pip install PyJWT
ตรวจสอบ
python -c "import jwt; print(jwt.__version__)"
#14. สร้างไฟล์ generate_token.py
import time
import jwt
SECRET = "workshop-super-secret-change-me"
payload = {
"iss": "workshop-key",
"sub": "workshop-user",
"iat": int(time.time()),
"exp": int(time.time()) + 3600
}
token = jwt.encode(
payload,
SECRET,
algorithm="HS256"
)
print(token)
สร้าง token
python generate_token.py
จะได้ JWT รูปแบบ
xxxxx.yyyyy.zzzzz
JWT ประกอบด้วย
Header.Payload.Signature
ตัวอย่าง payload
{
"iss": "workshop-key",
"sub": "workshop-user",
"iat": 1760000000,
"exp": 1760003600
}
#Part 5: เรียก API ด้วย JWT
#15. Linux/macOS
เก็บ token ลง environment variable
export JWT_TOKEN=$(python generate_token.py)
เรียก API
curl -i http://localhost:8000/api/get \
-H "Authorization: Bearer $JWT_TOKEN"
หาก token ถูกต้องควรได้
HTTP/1.1 200 OK
#16. Windows PowerShell
$JWT_TOKEN = python generate_token.py
เรียก API
curl.exe -i http://localhost:8000/api/get `
-H "Authorization: Bearer $JWT_TOKEN"
#17. Windows CMD
for /f "delims=" %i in ('python generate_token.py') do set JWT_TOKEN=%i
เรียก API
curl -i http://localhost:8000/api/get -H "Authorization: Bearer %JWT_TOKEN%"
ถ้าใช้คำสั่งในไฟล์
.batให้เปลี่ยน%iเป็น%%i
#Part 6: ตรวจสอบ Header ที่ Kong ส่งไป Upstream
เมื่อ authentication สำเร็จ Kong สามารถส่งข้อมูล Consumer ไปยัง upstream ผ่าน header ภายในของ Kong
ลองเรียก
curl -s http://localhost:8000/api/headers \
-H "Authorization: Bearer $JWT_TOKEN"
Upstream เช่น httpbin จะช่วยให้เห็น request headers ที่ได้รับ
แนวคิดคือ backend สามารถทราบว่า request ถูก authenticate ผ่าน Consumer ใดโดยใช้ header ที่ Kong เพิ่มให้
Backend ไม่ควรเชื่อ header ระบุตัวตนที่ส่งตรงจาก public client โดยไม่มี boundary ที่เหมาะสม ควรออกแบบ network และ header trust model ให้ชัดเจน
#Part 7: Negative Testing
#18. Test Case 1 — ไม่มี JWT
curl -i http://localhost:8000/api/get
Expected
401 Unauthorized
#19. Test Case 2 — Token ถูกต้อง
curl -i http://localhost:8000/api/get \
-H "Authorization: Bearer $JWT_TOKEN"
Expected
200 OK
#20. Test Case 3 — Token ปลอม
curl -i http://localhost:8000/api/get \
-H "Authorization: Bearer abc.def.xyz"
Expected
401 Unauthorized
#21. Test Case 4 — ใช้ secret ผิด
สร้าง generate_invalid_token.py
import time
import jwt
payload = {
"iss": "workshop-key",
"sub": "workshop-user",
"iat": int(time.time()),
"exp": int(time.time()) + 3600
}
print(
jwt.encode(
payload,
"wrong-secret",
algorithm="HS256"
)
)
เรียก
BAD_TOKEN=$(python generate_invalid_token.py)
curl -i http://localhost:8000/api/get \
-H "Authorization: Bearer $BAD_TOKEN"
Expected
401 Unauthorized
#22. Test Case 5 — Token หมดอายุ
สร้าง generate_expired_token.py
import time
import jwt
payload = {
"iss": "workshop-key",
"sub": "workshop-user",
"iat": int(time.time()) - 7200,
"exp": int(time.time()) - 3600
}
print(
jwt.encode(
payload,
"workshop-super-secret-change-me",
algorithm="HS256"
)
)
เรียก
EXPIRED_TOKEN=$(python generate_expired_token.py)
curl -i http://localhost:8000/api/get \
-H "Authorization: Bearer $EXPIRED_TOKEN"
เพราะตั้ง
"claims_to_verify": ["exp"]
Expected
401 Unauthorized
#23. Test Case 6 — iss ไม่ตรงกับ Credential
สร้าง token ที่มี
{
"iss": "unknown-client"
}
Kong จะหา JWT Credential ที่ตรงกับ key ไม่พบ จึงไม่ยอมให้ request ผ่าน
Expected
401 Unauthorized
#Part 8: JWT Flow แบบละเอียด
sequenceDiagram
participant C as Client
participant K as Kong Gateway
participant P as JWT Plugin
participant D as Kong Data Store
participant A as Upstream API
C->>K: GET /api/get + Bearer JWT
K->>P: Execute JWT Plugin
P->>P: Parse Header/Payload
P->>P: Read iss
P->>D: Find JWT Credential by key
D-->>P: key + algorithm + secret/public key
P->>P: Verify Signature
P->>P: Verify exp
alt JWT valid
P-->>K: Consumer authenticated
K->>A: Proxy request
A-->>K: Response
K-->>C: 200 OK
else JWT invalid
P-->>C: 401 Unauthorized
end
#Part 9: Plugin Scope
Kong Plugin สามารถเปิดได้หลายระดับ
#Global
ใช้กับทุก Route/Service ที่เข้าเงื่อนไข
POST /plugins
#Service
ใช้กับทุก Route ของ Service หนึ่ง
POST /services/{service}/plugins
#Route
ใช้เฉพาะ Route
POST /routes/{route}/plugins
Workshop นี้เลือก Route scope เพื่อไม่กระทบ API อื่น
#Part 10: HS256 กับ RS256
#HS256
ใช้ shared secret
Auth Service --- shared secret --- Kong
ข้อดี
- ตั้งค่าง่าย
- เหมาะกับ workshop
- ประสิทธิภาพดี
ข้อควรระวัง
- ทั้งฝั่ง signer และ verifier ต้องรู้ secret
- ถ้า secret รั่ว ผู้ถือ secret สามารถสร้าง token ที่ signature ถูกต้องได้
#RS256
ใช้ asymmetric key pair
Auth Service
|
| Sign ด้วย Private Key
v
JWT
|
| Verify ด้วย Public Key
v
Kong
ข้อดี
- Kong ไม่จำเป็นต้องรู้ private key
- แยกหน้าที่ signer กับ verifier ได้ชัดเจน
- เหมาะกับระบบกระจายตัวมากกว่า
สำหรับ production ที่มี Identity Provider หรือ Auth Service แยกต่างหาก ควรพิจารณา asymmetric signing เช่น RS256 ตาม threat model และ architecture ของระบบ
#Part 11: Declarative Configuration ตัวอย่าง
ตัวอย่างแนวคิดด้วย kong.yaml
_format_version: "3.0"
services:
- name: demo-api
url: https://httpbin.org
routes:
- name: demo-route
paths:
- /api
strip_path: true
plugins:
- name: jwt
route: demo-route
config:
header_names:
- authorization
key_claim_name: iss
claims_to_verify:
- exp
consumers:
- username: workshop-user
jwt_secrets:
- algorithm: HS256
key: workshop-key
secret: workshop-super-secret-change-me
Declarative configuration เหมาะกับแนวทาง GitOps เพราะสามารถ version-control configuration และนำไป apply ผ่าน tooling เช่น decK ได้
#Part 12: Security Best Practices
#1. อย่าใช้ secret ตัวอย่างใน production
ตัวอย่าง
workshop-super-secret-change-me
มีไว้เพื่อการศึกษาเท่านั้น
ใช้ secret ที่มี entropy สูงและเก็บใน Secret Manager/Vault
#2. กำหนด exp
JWT สำหรับ access token ไม่ควรมีอายุยาวโดยไม่จำเป็น
ตัวอย่าง
{
"exp": 1760003600
}
และตั้ง Kong ให้ตรวจ
{
"claims_to_verify": ["exp"]
}
#3. พิจารณา nbf
Kong สามารถตรวจ nbf ได้ด้วย
{
"claims_to_verify": [
"exp",
"nbf"
]
}
#4. ใช้ HTTPS
JWT แม้จะ signed แต่ payload โดยทั่วไปไม่ได้ถูกเข้ารหัส
จึงไม่ควรส่งผ่าน HTTP บน public network
Client
|
HTTPS
|
Kong Gateway
#5. อย่าใส่ข้อมูลลับใน JWT Payload
JWT payload สามารถ decode ได้
หลีกเลี่ยง
{
"password": "...",
"credit_card": "...",
"private_secret": "..."
}
#6. แยก Authentication กับ Authorization
JWT Plugin เน้นการตรวจ identity/token
หากต้องการสิทธิ์ระดับ role/scope เช่น
admin
editor
viewer
ควรออกแบบ authorization policy เพิ่มเติม
#7. ใช้ key rotation
ระบบ production ควรรองรับการหมุน key/secret โดยไม่ทำให้ระบบหยุดทำงาน
แนวทางทั่วไปคือ
Old Key + New Key
|
transition period
|
Remove Old Key
#8. จำกัดการเข้าถึง Admin API
Port
8001
คือ Kong Admin API และไม่ควรเปิดให้ public internet เข้าถึงโดยตรง
ควรจำกัดด้วย
- private network
- firewall
- VPN
- RBAC/secured administration ตาม edition และ deployment model
- reverse proxy/internal management plane ตาม architecture
#Part 13: Troubleshooting
#ปัญหา: 401 Unauthorized
ตรวจสอบ
1. มี Authorization header หรือไม่
2. ใช้ Bearer token หรือไม่
3. iss ตรงกับ credential key หรือไม่
4. secret ตรงหรือไม่
5. algorithm ตรงหรือไม่
6. token หมดอายุหรือไม่
7. plugin ถูกผูกกับ Route/Service ที่ถูกต้องหรือไม่
#ตรวจ plugin
curl http://localhost:8001/routes/demo-route/plugins
#ตรวจ Consumer
curl http://localhost:8001/consumers/workshop-user
#ตรวจ JWT Credential
curl http://localhost:8001/consumers/workshop-user/jwt
#ตรวจ Route
curl http://localhost:8001/routes/demo-route
#ตรวจ Service
curl http://localhost:8001/services/demo-api
#Part 14: Cleanup
ลบ Route
curl -i -X DELETE http://localhost:8001/routes/demo-route
ลบ Service
curl -i -X DELETE http://localhost:8001/services/demo-api
ลบ Consumer
curl -i -X DELETE http://localhost:8001/consumers/workshop-user
#Part 15: แบบฝึกหัด
#Lab 1
เปลี่ยน Route จาก
/api
เป็น
/secure-api
#Lab 2
เพิ่ม claim
{
"department": "software-engineering"
}
และตรวจสอบว่าค่า claim ถูก encode อยู่ใน JWT อย่างไร
#Lab 3
ตั้ง JWT ให้หมดอายุใน 60 วินาที
"exp": int(time.time()) + 60
ทดสอบก่อนและหลังหมดอายุ
#Lab 4
เพิ่ม nbf
"nbf": int(time.time()) + 30
และตั้ง plugin
{
"claims_to_verify": [
"exp",
"nbf"
]
}
สังเกตผลก่อนและหลังเวลาที่กำหนด
#Lab 5
สร้าง Consumer เพิ่มอีกคน
mobile-app
พร้อม JWT Credential คนละ key/secret และตรวจสอบว่า Kong สามารถแยก Consumer ได้
#Part 16: สรุป
Workflow หลักของ Kong JWT Authentication คือ
1. Create Service
2. Create Route
3. Enable JWT Plugin
4. Create Consumer
5. Create JWT Credential
6. Auth Service/Client สร้าง JWT
7. ส่ง Authorization: Bearer <JWT>
8. Kong อ่าน iss
9. Kong หา Credential
10. Kong ตรวจ Signature
11. Kong ตรวจ exp/nbf
12. Valid -> Proxy ไป Upstream
13. Invalid -> 401 Unauthorized
JWT Plugin ช่วยย้ายงานตรวจ authentication ออกจาก backend ไปไว้ที่ gateway ทำให้หลาย API ใช้นโยบาย authentication ที่สอดคล้องกันได้ง่ายขึ้น อย่างไรก็ตาม production architecture ควรพิจารณาเรื่อง key management, token lifetime, TLS, authorization, secret rotation และการป้องกัน Admin API เพิ่มเติม
#อ้างอิง
-
Kong JWT Plugin
https://developer.konghq.com/plugins/jwt/ -
Kong: Authenticate Consumers with the JWT Plugin
https://developer.konghq.com/how-to/authenticate-consumers-jwt/ -
Kong JWT Configuration Reference
https://developer.konghq.com/plugins/jwt/reference/ -
Kong JWT: Verify Registered Claims
https://developer.konghq.com/plugins/jwt/examples/verified-claim/ -
Kong Gateway Service
https://developer.konghq.com/gateway/entities/service/ -
Kong Gateway Route
https://developer.konghq.com/gateway/entities/route/ -
RFC 7519: JSON Web Token
https://www.rfc-editor.org/rfc/rfc7519