# คู่มือการเปิดใช้งานระบบยืนยันตัวตนสำหรับวิดีโอที่เข้ารหัสแบบ ClearKey
การปกป้องเนื้อหาวิดีโอจากการเข้าถึงโดยไม่ได้รับอนุญาตถือเป็นสิ่งที่มีความสำคัญอย่างยิ่ง โดยเฉพาะอย่างยิ่งสำหรับผู้ให้บริการสตรีมมิ่งซึ่งต้องการรักษาคุณค่าของเนื้อหาและป้องกันการละเมิดลิขสิทธิ์
ClearKey เป็นระบบ DRM ที่มีความซับซ้อนน้อยกว่า Widevine หรือ FairPlay แต่ยังคงให้การป้องกันพื้นฐานที่เพียงพอ
เอกสารฉบับนี้จะอธิบายถึงจุดอ่อนของระบบ ClearKey ที่มีอยู่ และแนวทางการเพิ่มกลไกการยืนยันตัวตน (Authentication) เพื่อเสริมสร้างความปลอดภัยให้กับระบบ
# ภาพรวมของระบบ ClearKey ที่มีอยู่
# หลักการทำงานพื้นฐาน
ระบบ ByteArk Stream รองรับการประมวลผลวิดีโอที่เข้ารหัสแบบ Clear Key ด้วยอัลกอริทึม AES-128 สำหรับวิดีโอในรูปแบบ HLS (HTTP Live Streaming) ผู้ชมวิดีโอที่ถูกเข้ารหัสจะไม่สามารถรับชมได้หากไม่ได้รับกุญแจ (Key) ที่ใช้ในการเข้ารหัสเพื่อใช้ในการถอดรหัสเสียก่อน ถึงแม้ว่าผู้ชมจะดาวน์โหลดวิดีโอดังกล่าวมาด้วยวิธีใดก็ตาม
# โครงสร้าง HLS กับการเข้ารหัส
ในรูปแบบ HLS วิดีโอจะถูกแบ่งออกเป็นส่วนย่อย ๆ เรียกว่า "Segment" และไฟล์ Manifest จะมีข้อมูลอ้างอิงถึงตำแหน่งของกุญแจที่ใช้ในการถอดรหัส
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-KEY:METHOD=AES-128,URI="https://key-server.example.com/key",IV=0x...
#EXTINF:10.0,
segment1.ts
#EXTINF:10.0,
segment2.ts
2
3
4
5
6
7
8
จากตัวอย่าง #EXT-X-KEY ระบุว่าวิดีโอถูกเข้ารหัสด้วย AES-128 และกุญแจสามารถขอได้จาก URL ที่ระบุ
# จุดอ่อนของระบบปัจจุบัน
# การขาดกลไกการยืนยันตัวตน
แม้ว่าระบบ ClearKey จะให้การป้องกันพื้นฐานผ่านการเข้ารหัส แต่ก็ยังมีจุดอ่อนสำคัญ คือ การขอกุญแจไม่มีการตรวจสอบสิทธิ์การเข้าถึง
# ปัญหาที่เกิดขึ้น
- การเข้าถึงกุญแจโดยไม่ได้รับอนุญาต - ผู้ไม่หวังดีสามารถขอกุญแจถอดรหัสได้โดยตรงหากทราบ URL
- การแบ่งปันกุญแจ - ผู้ใช้ที่ได้รับสิทธิ์สามารถนำกุญแจไปแบ่งปันให้ผู้อื่นได้
- ขาดการควบคุมการเข้าถึง - ไม่สามารถจำกัดสิทธิ์ตามระยะเวลา จำนวนครั้ง หรือเพิกถอนสิทธิ์ได้
# แผนภาพลำดับขั้น: ระบบเดิม
# ขั้นตอนการทำงานของระบบ ClearKey เดิม
# คำอธิบายขั้นตอนการทำงานของระบบเดิม
# ขั้นตอนที่ 1: การดึงไฟล์ Manifest
ผู้ชมจะดำเนินการขอไฟล์ Manifest จาก Manifest Server เพื่อรับข้อมูลเกี่ยวกับวิดีโอที่ต้องการรับชม
# ขั้นตอนที่ 2: การดึงกุญแจ (จุดอ่อนหลัก)
นี่คือจุดอ่อนสำคัญของระบบ กุญแจจะถูกส่งให้ทันทีโดยไม่มีการตรวจสอบว่าผู้ขอเป็นผู้ใช้ที่มีสิทธิ์หรือไม่
# ขั้นตอนที่ 3: การเล่นวิดีโอ
หลังจากได้รับกุญแจแล้ว ผู้ชมสามารถถอดรหัสและเล่นวิดีโอได้ทันที
# แผนภาพลำดับขั้น: ระบบใหม่ที่มีการยืนยันตัวตน
# แนวทางการแก้ไขจุดอ่อน
เพื่อแก้ไขจุดอ่อนดังกล่าว ByteArk ได้พัฒนาระบบใหม่ที่เพิ่มกลไกการยืนยันตัวตน (Authentication) เข้าไปในกระบวนการหลัก โดยมีหลักการสำคัญดังนี้:
- การสร้าง Key Pair: สร้างคู่กุญแจ (Public/Private Key) เพื่อใช้ในการลงนามดิจิทัล
- การยืนยันตัวตน: ตรวจสอบสิทธิ์ผู้ใช้ก่อนอนุญาตให้เข้าถึงเนื้อหา
- การจัดการ Session: ควบคุมระยะเวลาและขอบเขตการเข้าถึง
- การตรวจสอบโดเมน: จำกัดการเข้าถึงจากแหล่งที่ได้รับอนุญาตเท่านั้น
# แผนภาพลำดับขั้นของระบบใหม่
# คำอธิบายขั้นตอนการทำงานของระบบใหม่
# ขั้นตอนที่ 1: การตั้งค่าคู่กุญแจ (Setup Key Pair)
ในขั้นตอนนี้ ลูกค้าจะดำเนินการสร้าง Key Pair สำหรับการยืนยันตัวตน:
- ส่งคำขอสร้าง Key Pair ไปยัง Stream API พร้อมกับ PAT และ Playback Channel ID
- รับ Private Key และ Keypair ID กลับมา
- เก็บ Private Key ไว้อย่างปลอดภัยเพื่อใช้ในการลงนาม JWT Token
# ขั้นตอนที่ 2: การยืนยันตัวตน (Authentication)
ขั้นตอนนี้เป็นหัวใจสำคัญของระบบใหม่ โดยมีการตรวจสอบสิทธิ์ผู้ใช้ก่อนอนุญาตให้เข้าถึงเนื้อหา:
- ผู้ชมส่งคำขอยืนยันตัวตนไปยัง Backend ของลูกค้า (ใช้ OAuth, Username/Password หรือวิธีการอื่นตามที่ลูกค้ากำหนด)
- Backend ตรวจสอบข้อมูลผู้ใช้ และสร้าง JWT Token โดยลงนามด้วย Private Key
- ผู้ชมส่ง JWT Token ไปยัง Key Server ผ่าน SDK ด้วยฟังก์ชัน
initAuth() - หากถูกต้อง จะได้รับ Session และ Refresh Token
# ขั้นตอนที่ 3: การดึงข้อมูล Manifest
หลังจากยืนยันตัวตนสำเร็จแล้ว ผู้ชมสามารถขอข้อมูลวิดีโอได้:
- ขอไฟล์ Playlist จาก Manifest Server
- รับ Playlist กลับมา พร้อม Key Server URL
- ขอ prog_index.m3u8 โดยระบุ
x_ark_origin(หรือใช้ค่า Default)
# ขั้นตอนที่ 4: การดึงกุญแจ (Key Fetch)
ในขั้นตอนสุดท้าย ผู้ชมสามารถขอกุญแจถอดรหัสได้:
- ส่งคำขอไปยัง Key Server โดยแนบ Session ที่ได้รับ
- ได้รับกุญแจถอดรหัสกลับมา
- เมื่อ Session ใกล้หมดอายุ เรียก
renewSession()เพื่อต่ออายุ
# API สำหรับ Passport Key Server
# การยืนยันตัวตนและการอนุญาต
ทุก Endpoint ต้องส่ง Request Headers ดังนี้:
| Header | ค่า | จำเป็น | คำอธิบาย |
|---|---|---|---|
Authorization | Bearer <access_token> | ใช่ | Access token ของผู้ใช้ที่ผ่านการยืนยันตัวตนแล้ว |
x-byteark-team | <team_identifier> | ใช่ | Team identifier ของผู้เรียก |
# 1. การสร้าง Key Pair สำหรับ Playback Channel
สร้างคู่กุญแจ ECDSA สำหรับ Playback Channel เพื่อใช้ยืนยันตัวตน
# Endpoint
POST /playback-channels/:playbackChannelId/key-pair
# Path Parameters
| พารามิเตอร์ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
passportKeyServerId | string | ใช่ | ID ของ Passport Key Server |
# Request Body
Content-Type: application/json
| ฟิลด์ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
enabledCookieAuth | boolean | ใช่ | เปิด (true) หรือปิด (false) Cookie Authentication |
# ตัวอย่าง
{
"enabledCookieAuth": true
}
2
3
# Response
# 200 OK
| ฟิลด์ | ชนิด | คำอธิบาย |
|---|---|---|
id | string | ID ของ record |
playbackChannelId | string | ID ของ Playback Channel |
allowDomains | string[] | รายการโดเมนที่อนุญาต |
defaultDomain | string | โดเมนเริ่มต้น |
passportClearKeyServerId | string | Passport Clear Key Server ID |
enabledCookieAuth | boolean | สถานะการเปิด/ปิด Cookie Authentication ปัจจุบัน |
# ตัวอย่าง
{
"id": "65a1b2c3d4e5f67890abcdef",
"playbackChannelId": "pc-abc123",
"allowDomains": ["example.com", "cdn.example.com"],
"defaultDomain": "example.com",
"passportClearKeyServerId": "pcks-def456",
"enabledCookieAuth": true
}
2
3
4
5
6
7
8
# 3. การอัปเดตโดเมนที่อนุญาต
อัปเดตรายการโดเมนที่อนุญาตสำหรับ Passport Key Server ที่ระบุ
# Endpoint
PUT /passport-keyservers/:passportKeyServerId/allow-domains
# Path Parameters
| พารามิเตอร์ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
passportKeyServerId | string | ใช่ | ID ของ Passport Key Server |
# Request Body
Content-Type: application/json
| ฟิลด์ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
allowDomains | string[] | ใช่ | รายการโดเมนที่อนุญาต |
# ตัวอย่าง
{
"allowDomains": ["example.com", "cdn.example.com"]
}
2
3
# Response
# 200 OK
| ฟิลด์ | ชนิด | คำอธิบาย |
|---|---|---|
id | string | ID ของ record |
defaultDomain | string | โดเมนเริ่มต้น |
allowDomains | string[] | รายการโดเมนที่อนุญาต (อัปเดตแล้ว) |
enabledCookieAuth | boolean | สถานะการเปิด/ปิด Cookie Authentication ปัจจุบัน |
# ตัวอย่าง
{
"id": "65a1b2c3d4e5f67890abcdef",
"defaultDomain": "example.com",
"allowDomains": ["example.com", "cdn.example.com"],
"enabledCookieAuth": true
}
2
3
4
5
6
# 4. การเพิกถอน/ยกเลิกการเพิกถอน Key Pair
เพิกถอนการใช้งานของ Key Pair บน Passport Key Server
# Endpoint
PUT /passport-keyservers/key-pairs/:keyPairId/revoked
# Path Parameters
| พารามิเตอร์ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
keyPairId | string | ใช่ | ID ของ Key Pair |
# Request Body
Content-Type: application/json
| ฟิลด์ | ชนิด | จำเป็น | คำอธิบาย |
|---|---|---|---|
revoked | boolean | ใช่ | เพิกถอน (true) หรือยกเลิกการเพิกถอน (false) Key Pair |
# ตัวอย่าง
{
"revoked": true
}
2
3
# Response
# 200 OK
| ฟิลด์ | ชนิด | คำอธิบาย |
|---|---|---|
keyPairId | string | ID ของ Key Pair |
revoked | boolean | สถานะการเพิกถอนปัจจุบัน |
# ตัวอย่าง
{
"keyPairId": "65a1b2c3d4e5f67890abcdef",
"revoked": true
}
2
3
4
# 404 Not Found
ส่งกลับเมื่อไม่พบ Key Pair ที่ระบุ
| ฟิลด์ | ชนิด | คำอธิบาย |
|---|---|---|
message | string | ข้อความแสดงข้อผิดพลาด |
# ข้อกำหนดการตั้งค่าโดเมนสำหรับ API Session
[!IMPORTANT] หากต้องการใช้งาน API Session (Cookie-based Authentication) โดเมนของ Key Server และโดเมนของ Client จะต้องอยู่ภายใต้ Top-Level Domain เดียวกัน
# เหตุผลที่ต้องตั้งค่าโดเมนให้ตรงกัน
ระบบ Session ใช้ HTTP Cookie (session_token) ในการเก็บ Token โดย Cookie จะถูกตั้งค่าโดเมนเป็น Root Domain ของ Key Server เช่น:
| Hostname ของ Key Server | Cookie Domain ที่ถูกตั้งค่า |
|---|---|
api.example.com | .example.com |
key-server.byteark.com | .byteark.com |
localhost | localhost |
เนื่องจาก Browser จะส่ง Cookie กลับไปยัง Server เฉพาะโดเมนที่ตรงกันเท่านั้น ดังนั้นหาก Client อยู่คนละ Top-Level Domain กับ Key Server, Cookie จะไม่ถูกส่งไปด้วย ทำให้ Session ใช้งานไม่ได้
# ตัวอย่าง
# ใช้งานได้ (โดเมนตรงกัน)
Key Server: https://api.example.com
Client App: https://player.example.com
→ Cookie Domain = .example.com → ใช้ร่วมกันได้
2
3
# ใช้งานไม่ได้ (โดเมนไม่ตรงกัน)
Key Server: https://api.example.com
Client App: https://player.another-site.com
→ Cookie Domain = .example.com → Browser ไม่ส่ง Cookie ให้กับ another-site.com
2
3
# วิธีแก้ไข
หากโดเมนของ Client ไม่ตรงกับ Key Server ให้ดำเนินการ Alias (CNAME) โดเมนของ Key Server ให้อยู่ภายใต้โดเมนเดียวกับ Client เช่น:
Client App: https://player.mysite.com
Key Server: https://api.example.com
→ สร้าง CNAME: key-server.mysite.com → api.example.com
→ เรียก API ผ่าน: https://key-server.mysite.com
→ Cookie Domain = .mysite.com → ใช้ร่วมกันได้
2
3
4
5
6
[!WARNING] หากไม่ดำเนินการ Alias โดเมน ระบบ Session จะไม่สามารถทำงานได้ เนื่องจาก Browser จะไม่ส่ง Cookie ข้ามโดเมน และการเรียก API ที่ต้องการ Session จะได้รับ HTTP 401 Unauthorized ทุกครั้ง
# สรุป
| สิ่งที่ต้องดำเนินการ | รายละเอียด |
|---|---|
| ตรวจสอบโดเมน | ให้แน่ใจว่า Client และ Key Server อยู่ภายใต้ Top-Level Domain เดียวกัน |
| สร้าง CNAME (ถ้าจำเป็น) | Alias โดเมนของ Key Server ให้อยู่ภายใต้โดเมนของ Client |
| ตั้งค่า SSL | ให้แน่ใจว่าโดเมนที่ Alias มี SSL Certificate ที่ถูกต้อง |
| ทดสอบ Cookie | ตรวจสอบว่า Cookie session_token ถูกส่งไปกับ Request ได้อย่างถูกต้อง |