เซสชันและความปลอดภัย

เซสชันเอกสารและการควบคุมการเข้าถึง

โทเค็น Doconut ระบุเอกสารที่เปิดอยู่ หากมีคำขอใด ๆ ที่พาโทเค็นนี้ไปให้บริการ ผู้ที่ได้มามันจะสามารถอ่านทุกหน้าได้ หน้านี้อธิบายว่าเซสชันเอกสารเก็บอะไรไว้, อายุการใช้งานเท่าไหร่, และ DocImage.axd ตัดสินใจว่าใครสามารถดูหน้าของมันได้อย่างไร — ทั้งบนเซิร์ฟเวอร์เดียวและในฟาร์มเว็บ

สิ่งที่เซสชันเอกสารเก็บไว้

แต่ละการเรียก OpenDocumentAsync ที่สำเร็จจะสร้างเซสชันหนึ่งเซสชัน, เก็บไว้ในหน่วยความจำ:

  • ตัวดูรูปแบบที่โหลดแล้ว — อินสแตนซ์เอนจินที่ถือเอกสารที่แปลงแล้ว,
  • สถานะต่อหน้า — การหมุน, การพลิก, และข้อมูลคำอธิบายที่ผู้ใช้ใส่ในวิดเจ็ต,
  • ดัชนีการค้นหา (ถ้ามี), สร้างขึ้นในการค้นหาครั้งแรก (หรือโหลดจากไฟล์ .srh ที่สร้างไว้ล่วงหน้า),
  • ลายน้ำของเซสชัน จาก DocOptions.Watermark.

อายุการใช้งาน

เซสชันหมดอายุตาม หน้าต่างเลื่อน: DocOptions.TimeOut นาที (ค่าเริ่มต้น 60) ซึ่งรีเซ็ตโดยทุกคำขอที่ส่งโทเค็นมา เมื่อเซสชันสิ้นสุด — ไม่ว่าจะหมดอายุหรือโดย CloseDocument(token) — เอนจินเอกสารจะถูกปล่อยและหน่วยความจำจะถูกคืนทันที

csharp
// A short-lived session for a one-shot preview
var token = await viewer.OpenDocumentAsync(path, new DocOptions { TimeOut = 10 });

คำขอที่ใช้โทเค็นที่หมดอายุจะได้รับภาพข้อผิดพลาดที่อ่านว่า Document session not found. Please re-open document. หน้าเพจต้องเปิดเอกสารใหม่เพื่อรับโทเค็นใหม่ บนเซิร์ฟเวอร์เดียว การรีไซเคิลพูลแอปพลิเคชันก็จะทำให้ทุกเซสชันสิ้นสุดเช่นกัน

หน้าเพจจะให้บริการเฉพาะกับเซสชันที่เปิดมันเท่านั้น

เมื่อ UnsafeMode = false — ค่าตั้งต้น — การเปิดเอกสารจะให้สิทธิ์เข้าถึง เซสชัน ASP.NET ของคำขอที่เปิดมัน โดยการเขียนรายการ secure-{token} ลงในเซสชันนั้น DocImage.axd ต้องการสถานะเซสชันเอง, และจะให้บริการหน้าต่าง ๆ ก็ต่อเมื่อเซสชันที่ร้องขอมีรายการนั้นอยู่ คำขอจากเบราว์เซอร์อื่นที่นำโทเค็นเดียวกันมา จะได้รับภาพข้อผิดพลาดที่อ่านว่า You Are Not Authorized To View This Page.

csharp
Doconut.DoconutHost.Initialize(options =>
{
    // false (the default): DocImage.axd serves a page only to the ASP.NET session that opened
    // the document. The handler asks for session state itself; it only needs the site not to
    // turn session state off in Web.config.
    options.UnsafeMode = false;
});

ผลที่ตามมาจากการทำงานจริงสามประการ:

  • จุดเปิดต้องการสถานะเซสชัน คอนโทรลเลอร์ MVC และหน้า Web Forms มีโดยค่าเริ่มต้น IHttpHandler ที่กำหนดเองจะไม่มีเว้นแต่จะทำการ implement System.Web.SessionState.IRequiresSessionState เซสชันที่ไม่มีจะไม่มีสิ่งที่จะให้สิทธิ์, ดังนั้นผู้ดูจะโชว์ข้อความ “You Are Not Authorized To View This Page.” บนทุกหน้า
  • สถานะเซสชันต้องเปิดอยู่ <sessionState mode="Off" /> ใน Web.config จะปิดการให้สิทธิ์และการตรวจสอบ, ทำให้ทุกหน้าโดนปฏิเสธ
  • เบราว์เซอร์ต้องส่งคุกกี้เซสชัน คำขอของวิดเจ็ตเป็น same‑origin จึงส่ง ASP.NET_SessionId อัตโนมัติ ลูกค้าที่ไม่มีคุกกี้ (สคริปต์, การเรียก server‑to‑server, การตั้งค่า cross‑origin ที่ตัดคุกกี้) จะถูกปฏิเสธ นี่คือคุณลักษณะที่ทำงานตามที่ออกแบบ

UnsafeMode = true ปิดการตรวจสอบ: คำขอใด ๆ ที่พาโทเค็นมาจะได้รับการให้บริการ นี่คือสวิตช์ระดับทั่วโลกสำหรับทุกเอกสาร, ไม่มีการตั้งค่าฉีดเฉพาะเอกสาร ปล่อยให้เป็น false

การเพิกถอน

CloseDocument(token) ทำมากกว่าการคืนหน่วยความจำ: มันลบรายการ secure-{token} จากเซสชันของคำขอปัจจุบันและเพิกถอนโทเค็น, ดังนั้นโทเค็นที่ปิดแล้วจะถูกปฏิเสธทันที

ฟาร์มเว็บ: ตั๋วที่ลงนาม

ในฟาร์ม, คำขอถัดไปอาจไปยังเซิร์ฟเวอร์ที่ไม่เคยเห็นเอกสาร, อยู่ในกระบวนการทำงานอื่น, มีเซสชัน ASP.NET ของตนเอง การให้สิทธิ์ผ่านเซสชันจึงไม่สามารถตามไปได้ โหมดกระจายจะแทนที่ด้วย ตั๋วที่ลงนาม ที่ผูกกับเบราว์เซอร์: ทุกโหนดแชร์ที่เก็บเซสชันเอกสารเดียวกันและคีย์การลงนามเดียวกัน, และ DocImage.axd จะยอมรับคำขอเมื่อตั๋วของมันถูกต้องสำหรับโทเค็นนั้น, เบราว์เซอร์นั้น, และ — เมื่อผู้ใช้เข้าสู่ระบบ — ผู้ใช้นั้น

การตั้งค่าที่เหมือนกันบนทุกโหนด:

csharp
// Global.asax.cs - Application_Start
Doconut.DoconutHost.Initialize(
    options => options.UnsafeMode = false,     // the ticket check only runs when this is false
    services => services.AddDoconutDistributed(d =>
    {
        d.Store             = CloudLocation.FileShare;
        d.FileShareRootPath = @"\\fileserver\doconut-shared";  // a folder every node can reach
        d.SigningKey        = farmKey;                          // at least 32 bytes, the SAME on every node
    }));

จุดเปิดจะผูกตั๋วกับเบราว์เซอร์ก่อนเปิด, แล้วส่งตั๋วพร้อมโทเค็นกลับ:

  • ตั้งค่า DocOptions.BrowserId = DoconutFarmBinding.EnsureBrowserId(System.Web.HttpContext.Current, securityOptions) — มันจะออกคุกกี้ doconut-client ที่ตั๋วผูกไว้ — และ DocOptions.UserId = DoconutFarmBinding.ResolveUserId(System.Web.HttpContext.Current);
  • หลัง OpenDocumentAsync, ส่งคืน viewer.AccessToken ควบคู่กับโทเค็น;
  • ในหน้า, ส่งทั้งสองค่าให้วิดเจ็ต: objViewer.View(token, access).

Doconut.MvcSample.WebFarm ในแพ็กเกจตัวอย่างเป็นโหนดฟาร์มที่สมบูรณ์และสามารถรันได้, และคู่มือการย้ายที่อยู่ข้าง ๆ อธิบายขั้นตอนแต่ละอย่าง

เก็บคีย์การลงนามไว้นอกระบบควบคุมเวอร์ชัน: คีย์นี้ใช้ทั้งในการลงนามตั๋วและการเข้ารหัสเอกสารในที่เก็บร่วม โหนดที่ใช้คีย์ต่างกันจะปฏิเสธทุกตั๋วที่โหนดอื่นออก

ทดสอบฟาร์มด้วยเบราว์เซอร์จริง, หรือสคริปต์ที่เก็บคุกกี้ ลูกค้าที่ไม่มีคุกกี้ doconut-client จะไม่ได้รับการตอบกลับด้วย 401: จะได้รับภาพข้อผิดพลาด “not authorized” พร้อมสถานะ 200, ขนาดประมาณ 2 KB, ในขณะที่หน้าจริงมีขนาดหลายสิบถึงหลายร้อยกิโลไบต์

รายการตรวจสอบสำหรับการผลิต

  • รักษา UnsafeMode = false (ค่าเริ่มต้น)
  • เปิดเอกสารจากคำขอที่มีสถานะเซสชัน, และอย่าปิดสถานะเซสชัน
  • ตรวจสอบให้คุกกี้เซสชันถึง DocImage.axd (same origin, HTTPS, นโยบาย SameSite ที่อนุญาต)
  • ในฟาร์ม, ใช้โหมดกระจายพร้อมคีย์การลงนามเดียวที่แชร์ระหว่างทุกโหนด
  • เรียก CloseDocument เมื่อผู้ใช้ออกจากเอกสาร — ทั้งหน่วยความจำและความปลอดภัยจะได้ประโยชน์
  • อย่าเก็บบันทึกหรือแชร์โทเค็น; ถือว่าเป็นข้อมูลประจำตัวที่มีอายุสั้น

หน้านี้เป็นประโยชน์หรือไม่?