VOD সিকিউরিটি
কে কোন ভিডিও, কতক্ষণ, কোথা থেকে দেখতে পারবে — প্লেব্যাক টোকেন, সাইনড ম্যানিফেস্ট, AES-128 বনাম DRM, কী ম্যানেজমেন্ট, ফরেনসিক ওয়াটারমার্ক আর অডিট।
গল্পে বুঝি
বলাকা সিনেমা হলের গেটে টিকিট চেক করে কিন্দি। সন্ধ্যা ছয়টার শো, হল ভর্তি। কিন্দির হাতে তিনটা প্রশ্ন, প্রতিটা টিকিটের জন্য: এটা কি এই হলেরই টিকিট, নাকি পাশের মধুমিতার? এটা কি আজকের ছয়টার শোর, নাকি গত সপ্তাহের? আর সিট নম্বর কোনটা? টিকিটের গায়ে একটা ছাপ আছে যা কাউন্টারের মেশিন ছাড়া কেউ বানাতে পারে না, তাই রাস্তার দোকানে ছাপানো নকল টিকিট এক নজরেই ধরা পড়ে। কিন্দি টিকিট ছিঁড়ে অর্ধেক রেখে দেয়, যেন একই টিকিট দিয়ে দুইজন না ঢোকে।
কিন্তু কিন্দি জানে, গেট যতই কড়া হোক, ভেতরে কেউ মোবাইল দিয়ে পর্দার ভিডিও করতে পারে। গেট সেটা ঠেকাতে পারে না। তাই হল মালিক রুশদ আরেকটা কাজ করেন: প্রতিটা শোর প্রিন্টে চোখে-না-পড়া একটা ছোট দাগ থাকে, শো আর তারিখ অনুযায়ী আলাদা। বাজারে পাইরেট কপি এলে সেই দাগ দেখে রুশদ বলে দিতে পারেন কোন হলের কোন শো থেকে রেকর্ড হয়েছে। আর ক্যাশ কাউন্টারের খাতায় লেখা থাকে কে কখন কয়টা টিকিট কাটল; কেউ একসাথে পঞ্চাশটা কাটতে চাইলে কাউন্টারের ফাতিমা থামিয়ে দেয়, কারণ ওটা কালোবাজারির লক্ষণ। হলের প্রজেক্টর রুমের চাবি থাকে শুধু ম্যানেজারের কাছে, আর প্রতি মাসে তালা বদলানো হয়।
মিলিয়ে নিই: টিকিট হলো প্লেব্যাক টোকেন, কাউন্টারের মেশিনের ছাপ হলো টোকেনের সিগনেচার (HMAC বা JWT), হল আর শো মেলানো হলো টোকেনের ভেতরের asset ID আর expiry, আর সিট নম্বর হলো সেশন বাইন্ডিং। টিকিট ছিঁড়ে রাখা হলো একবার-ব্যবহারযোগ্য বা কনকারেন্সি লিমিট। ভেতরে মোবাইলে রেকর্ড করা হলো স্ক্রিন রেকর্ডিং, যা কোনো গেট ঠেকাতে পারে না, আর প্রিন্টের গোপন দাগ হলো ফরেনসিক ওয়াটারমার্ক। ক্যাশ কাউন্টারের খাতা হলো অডিট লগ, পঞ্চাশ টিকিট থামানো হলো লাইসেন্স এন্ডপয়েন্টে রেট লিমিট, আর প্রজেক্টর রুমের মাসিক তালা বদল হলো কী রোটেশন। বাস্তবে প্রতিটা পেইড VOD প্ল্যাটফর্ম ঠিক এই স্তরগুলো একসাথে চালায়, কারণ কোনো একটা স্তর একা যথেষ্ট নয়।
প্রথমে থ্রেট মডেল
“ভিডিও সিকিউর করো” কোনো রিকোয়ারমেন্ট নয়। কার হাত থেকে, কী ঠেকাতে, কত খরচে, সেটা আগে লিখতে হয়। VOD-এ বাস্তব হুমকিগুলো প্রায় সবসময় এই কয়েকটা:
| হুমকি | দেখতে কেমন | কোন স্তর ঠেকায় |
|---|---|---|
| লিঙ্ক স্ক্র্যাপিং ও শেয়ার | কেউ .m3u8 URL কপি করে টেলিগ্রাম গ্রুপে দিল | এক্সপায়ারিং টোকেন, সাইনড URL |
| হটলিংকিং | অন্য সাইট আপনার প্লেয়ার বা ম্যানিফেস্ট এম্বেড করে চালাচ্ছে | টোকেন, origin/referrer যাচাই |
| অ্যাকাউন্ট শেয়ারিং | এক সাবস্ক্রিপশনে বিশজন দেখছে | কনকারেন্ট স্ট্রিম লিমিট, ডিভাইস লিমিট |
| ডাউনলোডার টুল | সাধারণ টুল ম্যানিফেস্ট পড়ে সব সেগমেন্ট নামিয়ে জোড়া দেয় | এনক্রিপশন (AES-128 বা DRM) |
| স্ক্রিন রেকর্ডিং | OBS বা ক্যাপচার কার্ড দিয়ে পর্দা রেকর্ড | শুধু আংশিক: হার্ডওয়্যার DRM, ওয়াটারমার্ক |
| ইনসাইডার লিক | স্টাফ বা পার্টনার সোর্স ফাইল নিয়ে গেল | অ্যাক্সেস কন্ট্রোল, অডিট, ওয়াটারমার্ক |
| লাইসেন্স/কী এন্ডপয়েন্ট অপব্যবহার | চুরি করা টোকেন দিয়ে লাখো কী রিকোয়েস্ট | রেট লিমিট, টোকেন বাইন্ডিং, অ্যালার্ট |
দুটো সত্য মেনে নিয়ে শুরু করুন। এক, যে ভিডিও পর্দায় দেখা যায়, তা রেকর্ড করা যায়। কোনো প্রযুক্তি এটাকে শূন্যে নামায় না; লক্ষ্য হলো খরচ বাড়ানো আর লিক হলে উৎস খুঁজে পাওয়া। দুই, প্রতিটা স্তরের একটা খরচ আছে: স্টার্টআপ টাইম, ডিভাইস সাপোর্ট, ইঞ্জিনিয়ারিং সময়, আর লাইসেন্স ফি। একটা কোর্স প্ল্যাটফর্মের লেকচার আর একটা স্টুডিওর নতুন সিনেমার জন্য উত্তর আলাদা।
এই চ্যাপ্টার প্ল্যাটফর্মের দিক দেখায়: টোকেন কে বানায়, কী কোথায় থাকে, অডিট কীভাবে হয়। CENC, PSSH আর লাইসেন্স রিকোয়েস্টের বাইট-লেভেলের মেকানিক্স আছে ভিডিও ট্র্যাকের প্যাকেজিং চ্যাপ্টারে, আর এজে সাইনড URL যাচাইয়ের কোড আছে CDN এজ সিকিউরিটি চ্যাপ্টারে। এখানে সেগুলো আবার লেখা হয়নি।
স্তরগুলো একসাথে
প্রতিটা স্তর আলাদা প্রশ্নের উত্তর দেয়। প্লেব্যাক API বলে “এই মানুষের দেখার অধিকার আছে কি না”। এজ বলে “এই রিকোয়েস্টে বৈধ টিকিট আছে কি না”, ডেটাবেসে না গিয়ে, শুধু সিগনেচার মিলিয়ে। কী সার্ভিস বলে “এই টিকিটে কী দেওয়া যায় কি না”। অডিট লগ পরে বলে “কে কখন কী পেয়েছিল”।
গুরুত্বপূর্ণ ডিজাইন সিদ্ধান্ত: entitlement যাচাই হয় একবার, প্লেব্যাক API-তে। এজ আর কী সার্ভিস শুধু টোকেনের সিগনেচার আর ক্লেইম দেখে। এতে এজে ডেটাবেস কল লাগে না, আর সাবস্ক্রিপশনের নিয়ম বদলালে শুধু এক জায়গায় কোড বদলাতে হয়।
প্লেব্যাক টোকেন ডিজাইন
টোকেন হলো একটা ছোট, সাইন করা দাবি: “এই সেশন এই asset-এর এই অংশ এই সময় পর্যন্ত দেখতে পারবে।” দুটো প্রচলিত ফরম্যাট: সরল HMAC কোয়েরি প্যারামিটার (এজে যাচাই সহজ), অথবা JWT (বেশি ক্লেইম রাখা যায়, লাইব্রেরি সব ভাষায় আছে)। JWT-এর খুঁটিনাটি আছে JWT ডিপ ডাইভে।
একটা VOD টোকেনের ক্লেইম:
{
"sub": "playback_7Hq2",
"aid": "asset_123",
"tid": "tenant_baytalhikma",
"sid": "sess_9f3c",
"pfx": "/v/asset_123/",
"exp": 1793001600,
"geo": ["BD", "MY"],
"kid": "play-2026-10"
} প্রতিটা ফিল্ডের কারণ:
aidআরpfx— টোকেন শুধু এই asset-এর পাথের নিচে কাজ করবে।pfxনা থাকলে একটা কোর্সের টোকেন দিয়ে অন্য কোর্সের সেগমেন্ট চাওয়া যায়।exp— কতক্ষণ। ভিডিওর দৈর্ঘ্য আর একটা বাফার মিলিয়ে ঠিক করুন। দুই ঘণ্টার সিনেমার জন্য পনেরো মিনিটের টোকেন মানে মাঝপথে প্লেব্যাক ভাঙবে, যদি না প্লেয়ার টোকেন রিফ্রেশ করে।sid— কোন প্লেব্যাক সেশন। কনকারেন্সি গোনা, রেট লিমিট আর অডিট এটা দিয়েই হয়।kid— কোন সাইনিং কী দিয়ে সাইন হয়েছে, যেন রোটেশনের সময় পুরনো আর নতুন দুটোই যাচাই করা যায়।geo— অনুমোদিত দেশ। এজ ক্লায়েন্টের দেশ জানে, তাই সেখানেই যাচাই করা সস্তা।
IP বাইন্ডিং: লোভনীয়, কিন্তু সাবধান
টোকেনে ক্লায়েন্টের IP ঢুকিয়ে দিলে শেয়ার করা লিঙ্ক অন্য কারো কাছে কাজ করবে না। শুনতে নিখুঁত, বাস্তবে ভাঙে:
- মোবাইল ডেটায় IP মাঝপথে বদলায়, বিশেষ করে Wi-Fi থেকে 4G-তে সরলে।
- বাংলাদেশের মোবাইল অপারেটররা ব্যাপকভাবে carrier-grade NAT ব্যবহার করে, তাই হাজারো ইউজার একই পাবলিক IP শেয়ার করে। IP বাইন্ডিং তখন শেয়ারিং ঠেকায় না।
- IPv4 আর IPv6 দুটোই থাকলে ম্যানিফেস্ট এক অ্যাড্রেস থেকে আর সেগমেন্ট আরেক অ্যাড্রেস থেকে আসতে পারে।
মাঝামাঝি পথ: IP দিয়ে ব্লক করবেন না, লগ করবেন। একটা sid থেকে একই সময়ে পাঁচটা দেশের IP এলে সেটা অ্যালার্ট, প্লেব্যাক ভাঙার কারণ নয়। কড়া বাইন্ডিং দরকার হলে /24 বা ASN-লেভেলে বাঁধুন, পুরো IP-তে নয়।
টোকেন ইস্যু করার হ্যান্ডলার
// POST /v1/playback/{assetID}/token — entitlement is checked here and only here.
func (h *Handler) IssuePlaybackToken(w http.ResponseWriter, r *http.Request) {
user := auth.UserFrom(r.Context())
assetID := chi.URLParam(r, "assetID")
asset, err := h.q.GetPlayableAsset(r.Context(), assetID)
if errors.Is(err, pgx.ErrNoRows) {
apierr.Write(w, http.StatusNotFound, "asset_not_found")
return
}
if err != nil {
apierr.Write(w, http.StatusInternalServerError, "internal")
return
}
if !h.entitled(r.Context(), user, asset) {
apierr.Write(w, http.StatusForbidden, "not_entitled")
return
}
n, err := h.streams.Active(r.Context(), user.ID)
if err != nil {
apierr.Write(w, http.StatusInternalServerError, "internal")
return
}
if n >= user.Plan.MaxStreams {
apierr.Write(w, http.StatusConflict, "too_many_streams")
return
}
sid := newSessionID()
exp := time.Now().Add(asset.Duration + 30*time.Minute)
tok := h.signer.Sign(Claims{
AssetID: asset.ID, TenantID: asset.TenantID, SessionID: sid,
Prefix: "/v/" + asset.ID + "/", Exp: exp, Geo: asset.AllowedCountries,
})
h.audit.Record(r.Context(), "playback.issued", user.ID, asset.ID, sid, clientIP(r))
writeJSON(w, http.StatusOK, map[string]any{
"manifest_url": h.cdnBase + "/v/" + asset.ID + "/master.m3u8?token=" + tok,
"expires_at": exp.UTC().Format(time.RFC3339),
"session_id": sid,
})
} এরর কোডগুলো (not_entitled, too_many_streams) স্থির স্ট্রিং; ফ্রন্টএন্ড এগুলো থেকে ইউজারকে সহজ ভাষায় বার্তা দেখায়, যেমন “আপনার প্ল্যানে একসাথে দুটো ডিভাইসে দেখা যায়।” কাঁচা এরর টেক্সট কখনো ইউজারের কাছে যায় না।
কনকারেন্সি লিমিট কীভাবে গুনবেন
“একসাথে দুটো স্ট্রিম” গোনার জন্য প্লেয়ারকে নিয়মিত হার্টবিট পাঠাতে হয় (ধরুন প্রতি ষাট সেকেন্ডে), আর সার্ভার শেষ হার্টবিট দেখে সেশনকে জীবিত ধরে। কেউ ট্যাব বন্ধ করলে হার্টবিট থামে, আর কয়েক মিনিট পর সেশন নিজে থেকেই মুছে যায়। Redis-এ একটা sorted set এর জন্য যথেষ্ট:
ZADD streams:user_42 1793001234 sess_9f3c # heartbeat: score = unix time
ZREMRANGEBYSCORE streams:user_42 -inf 1793000934 # drop sessions silent > 5 min
ZCARD streams:user_42 # active streams নিয়ম দুটো: হার্টবিট মিস হলে সাথে সাথে প্লেব্যাক কাটবেন না (নেটওয়ার্ক এক মুহূর্তের জন্য পড়তে পারে), আর লিমিটে পৌঁছালে ইউজারকে অন্য ডিভাইস থেকে সেশন বন্ধ করার উপায় দিন। “কেন চলছে না” না বলে চুপচাপ আটকে দেওয়া একটা ডার্ক প্যাটার্ন।
সাইনড ম্যানিফেস্ট বনাম সাইনড কুকি
HLS-এ একটা প্লেব্যাক মানে একটা master playlist, কয়েকটা media playlist, আর শত শত সেগমেন্ট। টোকেন সবগুলোতে পৌঁছাতে হবে। তিনটা উপায়:
| উপায় | কীভাবে | সুবিধা | অসুবিধা |
|---|---|---|---|
| প্রতিটা URL আলাদা সাইন | প্যাকেজার বা origin প্রতিটা সেগমেন্ট URL-এ টোকেন বসিয়ে ম্যানিফেস্ট লেখে | সবচেয়ে কড়া, যেকোনো প্লেয়ারে চলে | ম্যানিফেস্ট প্রতি ইউজারে আলাদা, তাই ম্যানিফেস্ট ক্যাশ হয় না |
| পাথ-প্রিফিক্স টোকেন | একটা টোকেন পুরো /v/asset_123/ প্রিফিক্সে বৈধ, প্লেয়ার প্রতিটা রিকোয়েস্টে যোগ করে | ম্যানিফেস্ট আর সেগমেন্ট দুটোই ক্যাশ হয় | প্লেয়ারকে কোয়েরি প্যারামিটার বহন করাতে হয় (hls.js-এ xhrSetup) |
| সাইনড কুকি | প্লেব্যাক API একটা কুকি সেট করে যা প্রিফিক্সে বৈধ | URL পরিষ্কার, প্লেয়ারে কোনো কোড নেই | কুকি ডোমেইন মিলতে হয়; নেটিভ অ্যাপ ও স্মার্ট TV-তে ঝামেলা |
বেশিরভাগ প্ল্যাটফর্মের জন্য পাথ-প্রিফিক্স টোকেন সেরা মাঝামাঝি। ম্যানিফেস্ট আর সেগমেন্ট দুটোর ক্যাশ কী থেকে টোকেন বাদ দিন, নাহলে প্রতিটা ইউজারের জন্য আলাদা ক্যাশ এন্ট্রি হবে আর hit ratio শূন্যে নামবে (ক্যাশ কী চ্যাপ্টার)। এজ টোকেন যাচাই করে, তারপর কী বানানোর আগে কোয়েরি থেকে ফেলে দেয়।
ক্যাশ কী থেকে টোকেন বাদ দেওয়া আর যাচাই বাদ দেওয়া এক জিনিস নয়। টোকেন বাদ দিয়ে ক্যাশ করলেন কিন্তু HIT-এর আগে যাচাই করলেন না, তাহলে একবার কেউ বৈধ টোকেনে ক্যাশ ভরিয়ে দিলে পরের সবাই টোকেন ছাড়াই পাবে। যাচাই সবসময় ক্যাশ লুকআপের আগে।
AES-128 বনাম পূর্ণ DRM: কোনটা কখন
প্যাকেজিং চ্যাপ্টারের টেবিলটা মনে রাখুন। প্ল্যাটফর্ম চালানোর দিক থেকে সিদ্ধান্তটা আসে এই প্রশ্নগুলো থেকে:
AES-128 (HLS METHOD=AES-128) যথেষ্ট যখন:
- কনটেন্ট আপনার নিজের বা ইউজারের আপলোড করা (কোর্স, ইন্টারনাল ট্রেনিং, ক্রিয়েটরের ভিডিও)।
- লক্ষ্য হলো “রাইট-ক্লিক করে সেভ” আর সাধারণ ডাউনলোডার ঠেকানো।
- কোনো স্টুডিও চুক্তিতে নির্দিষ্ট DRM বা robustness level লেখা নেই।
AES-128-এ কী আপনার নিজের কী সার্ভার থেকে HTTPS-এ আসে, টোকেন দিয়ে সুরক্ষিত। দুর্বলতা জানা: কী শেষ পর্যন্ত ব্রাউজারের JavaScript মেমরিতে আসে, তাই একজন দক্ষ মানুষ সেটা বের করতে পারে। এটা দরজায় তালা, সিন্দুক নয়।
পূর্ণ DRM (Widevine, FairPlay, PlayReady) দরকার যখন:
- লাইসেন্স করা কনটেন্ট, যেখানে চুক্তিতেই DRM বাধ্যতামূলক।
- HD বা 4K-এর জন্য হার্ডওয়্যার-ভিত্তিক secure path চাওয়া হয়েছে।
- অফলাইন ডাউনলোড চাই কিন্তু ফাইল ডিভাইসের বাইরে কাজ করবে না।
DRM-এর খরচ শুধু টাকা নয়। প্রতিটা DRM সিস্টেমের নিজস্ব প্রোভাইডার, সার্টিফিকেট আর অনবোর্ডিং আছে (FairPlay-এর জন্য Apple-এর কাছ থেকে আলাদা ক্রেডেনশিয়াল লাগে), পুরনো ডিভাইসে সাপোর্ট ভাঙে, আর প্রতিটা প্লেব্যাকে একটা বাড়তি লাইসেন্স রাউন্ড-ট্রিপ স্টার্টআপ টাইম বাড়ায়। নিজের কনটেন্টে “সিকিউরিটির জন্য” DRM চাপানো সাধারণত ইউজারের জন্য খারাপ, পাইরেটের জন্য সামান্য বাধা।
প্রথমে AES-128 + এক্সপায়ারিং টোকেন + কনকারেন্সি লিমিট দিয়ে শুরু করুন। প্যাকেজিং CMAF cbcs-এ রাখুন, যাতে পরে DRM লাগলে সেগমেন্ট আবার এনকোড না করে শুধু কী ডেলিভারি বদলানো যায়।
কী ম্যানেজমেন্ট
এনক্রিপশন যত শক্ত, কী রাখার জায়গা তার চেয়ে দুর্বল হলে সব বৃথা। কয়েকটা নিয়ম:
প্রতি asset (বা প্রতি ট্র্যাক) আলাদা কী। এক কী দিয়ে পুরো লাইব্রেরি এনক্রিপ্ট করলে একটা কী লিক মানে সব কনটেন্ট খোলা। স্টুডিও কনটেন্টে প্রায়ই ভিডিও আর অডিওর জন্য আলাদা কী, আর SD, HD, UHD-র জন্য আলাদা কী চাওয়া হয়, যেন দুর্বল ডিভাইসে দেওয়া SD কী দিয়ে 4K খোলা না যায়।
কন্টেন্ট কী কখনো প্লেইনটেক্সটে ডেটাবেসে নয়। প্রচলিত প্যাটার্ন হলো envelope encryption: প্রতিটা কন্টেন্ট কী একটা মাস্টার কী (KEK) দিয়ে এনক্রিপ্ট করে ডেটাবেসে রাখা, আর মাস্টার কী থাকে KMS-এ। সেলফ-হোস্টেডে HashiCorp Vault বা OpenBao-র transit ইঞ্জিন এই কাজটা করে; ডেটাবেস ডাম্প চুরি হলেও কী পড়া যায় না।
CREATE TABLE content_keys (
key_id uuid PRIMARY KEY,
asset_id text NOT NULL REFERENCES assets(id) ON DELETE CASCADE,
track text NOT NULL, -- 'video-hd', 'video-sd', 'audio'
wrapped_key bytea NOT NULL, -- content key encrypted by KMS KEK
kek_version int NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (asset_id, track)
); রোটেশন দুই রকম, গুলিয়ে ফেলবেন না।
- সাইনিং কী রোটেশন (টোকেন সাইন করার কী): সহজ আর নিয়মিত করা উচিত। নতুন
kidদিয়ে সাইন শুরু করুন, পুরনোটা যাচাইয়ের জন্য সবচেয়ে লম্বা টোকেনের মেয়াদ পর্যন্ত রেখে দিন, তারপর সরান। - কন্টেন্ট কী রোটেশন: VOD-এ এর মানে কনটেন্ট আবার এনক্রিপ্ট আর প্যাকেজ করা, যা ব্যয়বহুল। সাধারণত শুধু কী লিক হলে করা হয়। KEK রোটেশন আলাদা আর সস্তা: শুধু
wrapped_keyনতুন KEK দিয়ে আবার র্যাপ করা, কনটেন্ট ছোঁয়া লাগে না।
প্যাকেজার কী পায় কীভাবে? প্যাকেজিং জব কী সার্ভিস থেকে কী চায়, ব্যবহার করে, আর ডিস্কে কখনো লেখে না। জব ওয়ার্কারের লগে কী প্রিন্ট হয়ে যাওয়া একটা ক্লাসিক লিক; লগিং লেয়ারে কী-জাতীয় ফিল্ড মাস্ক করুন।
কী ও লাইসেন্স এন্ডপয়েন্ট রক্ষা
কী এন্ডপয়েন্ট প্ল্যাটফর্মের সবচেয়ে মূল্যবান API। একটা চুরি হওয়া টোকেন দিয়ে কেউ যদি পুরো লাইব্রেরির কী টেনে নিতে পারে, এনক্রিপশন অর্থহীন। প্রতিরক্ষা:
- টোকেনের
aidআর চাওয়া কী-এর asset মিলতে হবে। asset_123-এর টোকেন দিয়ে asset_456-এর কী চাওয়া মানে সাথে সাথে403, আর একটা অ্যালার্ট। - প্রতি
sidরেট লিমিট। একটা স্বাভাবিক প্লেব্যাক কয়েকটা কী চায় (প্রতি ট্র্যাকে একটা, রোটেশন থাকলে কিছু বেশি)। এক সেশন থেকে মিনিটে শত শত কী রিকোয়েস্ট স্বাভাবিক নয়। রেট লিমিটের অ্যালগরিদম আছে রেট লিমিটিং চ্যাপ্টারে। - প্রতি ইউজার দৈনিক ভিন্ন asset-এর সংখ্যা। একজন মানুষ দিনে বিশটা সিনেমা দেখে না; দুশোটা asset-এর কী চাওয়া একটা স্ক্র্যাপারের লক্ষণ।
- কী রেসপন্স কখনো ক্যাশ নয়।
Cache-Control: no-store, আর CDN-এ কী পাথ সবসময় বাইপাস।
ফরেনসিক ওয়াটারমার্কিং
স্ক্রিন রেকর্ডিং ঠেকানো যায় না, তাই উচ্চমূল্যের কনটেন্টে লক্ষ্য হয় লিক হলে কোন অ্যাকাউন্ট থেকে লিক হলো সেটা বের করা। দুই রকম ওয়াটারমার্ক:
দৃশ্যমান ওয়াটারমার্ক — পর্দার কোণে ইউজারের ইমেইল বা আইডি, প্লেয়ারের উপর একটা লেয়ার হিসেবে। সস্তা, আর শেয়ার করতে মানসিক বাধা তৈরি করে। কিন্তু প্লেয়ার লেয়ার হলে ক্রপ বা ব্লার করে সরানো যায়। কোর্স প্ল্যাটফর্মে এটা প্রায়ই যথেষ্ট।
অদৃশ্য A/B সেশন ওয়াটারমার্কিং — এখানে প্যাকেজিংয়ের সময় প্রতিটা সেগমেন্টের দুটো ভার্সন বানানো হয়, A আর B, যাদের পিক্সেলে চোখে-না-পড়া সামান্য ভিন্নতা থাকে। প্রতিটা প্লেব্যাক সেশনের জন্য সার্ভার একটা A/B ক্রম ঠিক করে, যেমন সেগমেন্ট ১-এ A, ২-এ B, ৩-এ B, ৪-এ A। ক্রমটা সেশন আইডি থেকে আসে, তাই প্রতিটা ইউজার একটু আলাদা ভিডিও দেখে। লিক হওয়া কপি পেলে প্রতিটা সেগমেন্ট A না B সেটা বের করে ক্রমটা পড়া যায়, আর ক্রম থেকে সেশন।
session sess_9f3c -> bits 1 0 0 1 1 0 1 ...
segment N served -> variant B if bit N == 1 else A
leaked copy -> detect A/B per segment -> 1 0 0 1 1 0 1 -> sess_9f3c -> user_42 এর দাম: স্টোরেজ প্রায় দ্বিগুণ (ওয়াটারমার্ক করা অংশের জন্য), এজে প্রতি সেশনে ভিন্ন সেগমেন্ট বেছে দেওয়ার লজিক (এজ কম্পিউট বা টোকেনে এনকোড করা ক্রম), আর ডিটেকশন টুল সাধারণত বিশেষায়িত ভেন্ডরের। বড় স্টুডিও কনটেন্ট ছাড়া এটা সাধারণত বাড়াবাড়ি।
জিও রেস্ট্রিকশন ও আইনি দিক
লাইসেন্স করা কনটেন্টের অধিকার প্রায়ই দেশভিত্তিক: একটা সিরিজ বাংলাদেশে দেখানো যায়, ভারতে নয়। প্রয়োগ দুই জায়গায়:
- প্লেব্যাক API-তে — ক্যাটালগেই ভুল দেশে কনটেন্ট দেখাবেন না; “এই দেশে পাওয়া যায় না” বার্তা স্পষ্ট দিন।
- এজে — টোকেনের
geoক্লেইম আর এজের দেখা দেশ মেলান, কারণ টোকেন ইস্যুর পর ইউজার VPN চালু করতে পারে।
IP থেকে দেশ নির্ণয় নিখুঁত নয়; সীমান্তবর্তী ISP বা মোবাইল রোমিংয়ে ভুল হয়। আপিলের একটা পথ রাখুন, আর ভুলের হার মনিটর করুন। VPN ধরা আরেকটা অস্ত্র প্রতিযোগিতা; চুক্তিতে স্পষ্টভাবে না চাইলে এর পেছনে বেশি বিনিয়োগ সাধারণত লাভজনক নয়।
ইউজারের কাছে স্বচ্ছ থাকুন: কোন দেশে কী পাওয়া যায়, কয়টা ডিভাইসে একসাথে দেখা যায়, অফলাইন ডাউনলোড কতদিন থাকে, সবকিছু সাবস্ক্রাইব করার আগেই প্ল্যানের পাতায় লেখা থাকা উচিত।
অডিট
অডিট লগ সিকিউরিটির স্মৃতি। লিক হলে বা কোনো অ্যাকাউন্ট অস্বাভাবিক আচরণ করলে প্রথম প্রশ্ন হয় “কে কখন কী পেয়েছিল।” যা লিখবেন:
| ইভেন্ট | ফিল্ড |
|---|---|
playback.issued | user, tenant, asset, sid, IP, দেশ, ডিভাইস, প্ল্যান |
key.served | sid, asset, track, key_id (কী নিজে নয়!), IP |
playback.denied | user, asset, কারণের কোড (not_entitled, geo_blocked) |
source.downloaded | স্টাফ ইউজার, asset, কারণ — সোর্স ফাইল কে নামাল |
key.exported | কোন সার্ভিস বা মানুষ র্যাপ খোলা কী পেল |
ইনসাইডার লিক ঠেকানোর মূল হলো শেষ দুটো। সোর্স মাস্টার ফাইল আর কী-এর অ্যাক্সেস খুব কম মানুষের থাকা উচিত, প্রতিটা অ্যাক্সেস লগ হওয়া উচিত, আর লগটা এমন জায়গায় থাকা উচিত যেখানে সেই মানুষরাই মুছতে পারে না (append-only টেবিল, বা আলাদা অ্যাকাউন্টের object storage)।
অডিট লগে ব্যক্তিগত ডেটা (IP, ডিভাইস) থাকে, তাই রিটেনশন নির্দিষ্ট রাখুন আর প্রাইভেসি পলিসিতে লিখুন কী কতদিন রাখা হয়।
ডিবাগিং: এই চ্যাপ্টারের সমস্যাগুলো
| লক্ষণ | নিশ্চিত করুন | সমাধান |
|---|---|---|
লম্বা ভিডিওর মাঝপথে প্লেব্যাক থেমে 403 | সেগমেন্ট রিকোয়েস্টে টোকেনের exp ডিকোড করুন; বর্তমান সময়ের সাথে মেলান | exp ভিডিওর দৈর্ঘ্য + বাফার রাখুন, বা প্লেয়ারে টোকেন রিফ্রেশ যোগ করুন |
ম্যানিফেস্ট চলে, সেগমেন্ট 403 | curl -sI দিয়ে একটা সেগমেন্ট URL; টোকেন কোয়েরিতে আছে কিনা দেখুন | প্লেয়ার সেগমেন্টে টোকেন বহন করছে না; hls.js xhrSetup বা সাইনড কুকি ব্যবহার করুন |
| ক্যাশ hit ratio হঠাৎ প্রায় শূন্য | CDN লগে একই সেগমেন্টের ভিন্ন ক্যাশ কী গুনুন | ক্যাশ কী থেকে token কোয়েরি বাদ দিন, তবে যাচাই রাখুন |
| টোকেন ছাড়াই সেগমেন্ট পাওয়া যাচ্ছে | curl -sI টোকেন ছাড়া; রেসপন্সে HIT আসে কিনা দেখুন | এজে যাচাই ক্যাশ লুকআপের আগে সরান |
মোবাইল ইউজারদের বিক্ষিপ্ত 403 | অডিট লগে একই sid-এ ভিন্ন IP খুঁজুন | কড়া IP বাইন্ডিং সরান বা ASN-লেভেলে নামান |
কী সাইনিং রোটেশনের পর সবাই 401 | ব্যর্থ টোকেনের kid দেখুন; যাচাইকারী সেটা চেনে কিনা | পুরনো kid যাচাই সেটে ফিরিয়ে দিন, সবচেয়ে লম্বা exp পার হওয়ার পর সরান |
| ভিডিও চলে কিন্তু ছবি কালো, শুধু অডিও | প্লেয়ার কনসোলে কী রিকোয়েস্টের স্ট্যাটাস; ভিডিও ট্র্যাকের কী 403 কিনা | কী এন্ডপয়েন্ট ট্র্যাকভিত্তিক কী চেনে কিনা; প্ল্যান অনুযায়ী HD কী আটকানো হয়েছে কিনা |
| “too many streams” কিন্তু ইউজার একটাই ডিভাইসে | Redis-এ ZRANGE streams:user_42 0 -1 WITHSCORES | পুরনো সেশন হার্টবিট টাইমআউটে মুছছে না; ZREMRANGEBYSCORE চলছে কিনা দেখুন |
| বাংলাদেশের ইউজার “এই দেশে পাওয়া যায় না” | এজ যে দেশ দেখছে সেটা লগ করুন; ইউজারের IP-এর জিও ডেটা চেক করুন | জিও ডেটাবেস আপডেট; আপিলের পথ; সীমান্তবর্তী ASN-এর জন্য ম্যানুয়াল ওভাররাইড |
মূল শিক্ষা
- থ্রেট মডেল আগে, প্রযুক্তি পরে। কাকে, কী ঠেকাতে, কত খরচে, সেটা না লিখে DRM কেনা টাকা নষ্ট।
- পর্দায় যা দেখা যায় তা রেকর্ড করা যায়। লক্ষ্য খরচ বাড়ানো আর উৎস খুঁজে পাওয়া, শূন্য লিক নয়।
- Entitlement একবার, প্লেব্যাক API-তে। এজ আর কী সার্ভিস শুধু টোকেনের সিগনেচার আর ক্লেইম যাচাই করে।
- টোকেনে asset, পাথ প্রিফিক্স, মেয়াদ, সেশন আর
kidরাখুন। কড়া IP বাইন্ডিং মোবাইলে ভাঙে; লগ করুন, ব্লক নয়। - ক্যাশ কী থেকে টোকেন বাদ দিন, যাচাই থেকে নয়।
- নিজের কনটেন্টে AES-128 + টোকেন + কনকারেন্সি লিমিট সাধারণত যথেষ্ট। CMAF
cbcs-এ প্যাকেজ করুন যেন পরে DRM যোগ করা সহজ হয়। - প্রতি asset আলাদা কী, envelope encryption, KMS-এ মাস্টার কী। সাইনিং কী নিয়মিত রোটেট করুন; কন্টেন্ট কী শুধু লিক হলে।
- কী এন্ডপয়েন্টে asset মিলান, প্রতি সেশন রেট লিমিট দিন, কখনো ক্যাশ করবেন না।
- অডিট লগে কী নিজে নয়, key_id রাখুন। সোর্স আর কী অ্যাক্সেস লগ এমন জায়গায় রাখুন যা অ্যাক্সেসকারীরা মুছতে পারে না।
- নিয়মগুলো ইউজারের কাছে স্বচ্ছ রাখুন। ডিভাইস লিমিট, দেশ, অফলাইন মেয়াদ সাবস্ক্রাইবের আগেই জানা থাকা উচিত।