permission flag ไม่ใช่กลไกความปลอดภัย บิตที่ระบุว่า "ห้ามคัดลอก" อยู่ใน /Encrypt dictionary เดียวกันกับส่วนวิทยาการรหัสลับ ซึ่งทำให้มันดูราวกับมีอำนาจบังคับใช้ทั้งที่ไม่มีจริง และทันทีที่คุณมองสองสิ่งนี้เป็นเรื่องเดียวกัน การตรวจสอบของคุณก็จะเริ่มให้คำตอบที่ผิด คำถามเดียวที่ควรถามกับไฟล์ PDF ไม่ใช่ "ไฟล์นี้เข้ารหัสหรือไม่" แต่เฉพาะเจาะจงและยากกว่านั้น คือใช้อัลกอริทึมใด security handler revision ใด ตั้งรหัสผ่านตัวไหนในสองตัว อ้างสิทธิ์ permission bits ใดไว้บ้าง และการเข้ารหัสแตะต้องส่วนใดของไฟล์จริง ๆ ไฟล์หนึ่งอาจเข้ารหัสไว้อย่างเป็นทางการแต่เปิดอ่านได้ในทางปฏิบัติ อาจปฏิเสธการอ่านทั้งที่ปล่อย metadata ไว้เป็น plaintext อาจล็อกการพิมพ์ไว้ในแฟล็กที่โปรแกรมอ่านตัวใดก็ตามมีอิสระจะเมินเสียก็ได้ การตรวจสอบไฟล์ PDF จึงหมายถึงการคลี่ทุกประเด็นเหล่านี้ออกจากกัน และ PDF Library for Delphi ซึ่งเป็นเอนจิน PDF ของ losLab สำหรับ Delphi และ C++Builder เปิดให้เข้าถึงทุกประเด็นผ่านทั้ง API แบบ flat integer-handle และเลเยอร์คลาสแบบ typed
สิ่งที่ /Encrypt dictionary บันทึกไว้จริง
ISO 32000-1 §7.6 กำหนดความปลอดภัยของเอกสารผ่าน dictionary entry เพียงไม่กี่รายการ และ PDF Library for Delphi สะท้อนรายการเหล่านั้นแบบหนึ่งต่อหนึ่งไว้ใน record TPDFEncryption ตัว filter version V และ revision R เป็นตัวเลือกตระกูลอัลกอริทึม Length บอกขนาดคีย์ ส่วน permission bits อยู่ใน P สตริงสำหรับตรวจสอบรหัสผ่านของ owner และ user อยู่ใน O และ U (โดยมี OE และ UE เพิ่มเข้ามาสำหรับ AES-256) มีแฟล็ก EncryptMetadata ประกบอยู่ด้วย และอีกสามฟิลด์ระบุ crypt filter ที่ใช้กับสตริง สตรีม และไฟล์ฝังตัวตามลำดับ
คุณค่าของ record นี้อยู่ที่มันไม่ตีความอะไรแทนคุณ มันคืน dictionary ดิบกลับมาแล้วปล่อยให้คุณสรุปเอง ซึ่งตรงกับสิ่งที่งานตรวจสอบต้องการพอดี กรณี plaintext ที่ซ่อนอยู่ในไฟล์ที่เข้ารหัสจะปรากฏใน StringFilterIdentity และ StreamFilterIdentity เมื่อค่าใดค่าหนึ่งเป็นจริง ข้อมูลส่วนนั้นจะผ่าน Identity filter ไปโดยไม่ถูกแตะต้อง ไม่ว่าสถานะการเข้ารหัสของเอกสารจะรายงานว่าอย่างไรก็ตาม ตัวสแกนที่หยุดอยู่แค่ "มี /Encrypt dictionary อยู่" จะตัดสินว่าไฟล์แบบนี้ได้รับการป้องกัน ทั้งที่สตริงและสตรีมของมันวางอยู่โล่ง ๆ ความละเอียดอ่อนแบบเดียวกันนี้ครอบคลุมถึง metadata ด้วย เมื่อ EncryptMetadata เป็นเท็จ แพ็กเกจ XMP จะยังอ่านได้โดย indexer ใด ๆ ในขณะที่เนื้อหาของหน้าอ่านไม่ได้ ซึ่งเป็นเรื่องที่ควรรู้ทันทีที่กฎกำหนดเส้นทางของคุณอ้างอิงฟิลด์ title หรือ author
การตรวจความปลอดภัยแบบสั้นด้วย flat API
สำหรับไปป์ไลน์ส่วนใหญ่ การเรียกแบบ flat สี่ครั้งก็ตอบคำถามประจำวันได้แล้ว LoadFromFile คืนค่า 1 เมื่อสำเร็จ และเมื่อเปิดเอกสารได้แล้ว ตัวตรวจสอบการเข้ารหัสจะรายงานตามสถานะที่ถอดรหัสแล้วของเอกสาร:
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('contract.pdf', UserPassword) <> 1 then
raise Exception.Create('Open failed: wrong password or damaged file');
Writeln('status : ', PDF.EncryptionStatus); // ค่าที่เป็นไปได้: decrypted / encrypted / unknown
Writeln('algorithm : ', PDF.EncryptionAlgorithm); // ตระกูล RC4 เทียบกับ AES
Writeln('strength : ', PDF.EncryptionStrength); // คลาสความยาวคีย์
Writeln('owner pw? : ', PDF.CheckPassword(CandidatePassword));
finally
PDF.Free;
end;
end;
CheckPassword สำคัญกว่าที่ลายเซ็นบรรทัดเดียวของมันบ่งบอก PDF กำหนดรหัสผ่านไว้สองตัวที่มีอำนาจไม่เท่ากัน user password จำเป็นต่อการเปิดไฟล์ตั้งแต่แรก ส่วน owner password ให้สิทธิ์เต็มและมีผลเหนือ permission bit ทุกตัว ไบต์บนดิสก์เหมือนกันไม่ว่าจะเปิดด้วยตัวไหน แต่เซสชันที่เปิดด้วย owner password ทำสิ่งที่เซสชันแบบ user password ทำไม่ได้ ดังนั้นการตรวจสอบที่ไม่บันทึกว่าใช้ข้อมูลรับรองตัวใดจึงบันทึกความจริงไว้เพียงครึ่งเดียว เลเยอร์คลาสทำให้ความต่างนี้สอบถามได้ TPDFDocument.HasUserPassword และ HasOwnerPassword รายงานว่าไฟล์ต้องการอะไร ขณะที่ IsUserPassword และ IsOwnerPassword รายงานว่ารหัสผ่านตัวใดเป็นตัวที่เปิดเซสชันปัจจุบันได้จริง จงบันทึกข้อเท็จจริงนั้นไว้ และอย่าบันทึกค่ารหัสผ่านเองเป็นอันขาด
บันได Strength ที่คำว่า "AES-256" หมายถึงสองสิ่ง
ฟังก์ชันแบบ flat อย่าง Encrypt และ EncryptFile รับค่า Strength เป็นจำนวนเต็มที่มีความหมายห้าค่า ได้แก่ 0 สำหรับ RC4 40 บิต, 1 สำหรับ RC4 128 บิต, 2 สำหรับ AES 128 บิตที่อ่านได้ตั้งแต่ Acrobat 7, 3 สำหรับ AES 256 บิตแบบที่มาพร้อม Acrobat 9 และ 4 สำหรับ AES 256 บิตตามที่ Acrobat X และรุ่นใหม่กว่ากำหนด
ส่วนที่น่าสนใจคือ 3 และ 4 ต่างก็ถูกเรียกว่า AES-256 ทั้งคู่ แต่ไม่ใช่สคีมเดียวกัน Strength 3 แมปกับ security handler revision 5 ซึ่งเป็นการออกแบบชั่วคราวที่ Acrobat 9 ปล่อยออกมาและ ISO ไม่เคยรับมาเป็นมาตรฐาน ส่วน Strength 4 แมปกับ revision 6 ที่ฟังก์ชันสร้างคีย์ถูกทำให้แข็งแรงขึ้นและกำหนดเป็นมาตรฐานใน ISO 32000-2 สำหรับเอกสารที่คุณสร้างวันนี้ไม่มีเหตุผลใดให้เลือก 3 แทน 4 แต่สำหรับงานตรวจสอบ ช่องว่างนี้ชี้ขาด นโยบายที่เขียนว่า "AES-256 ตาม ISO 32000-2" มีเพียง R6 เท่านั้นที่ผ่าน ส่วนไฟล์ R5 ที่เรียกตัวเองว่า AES-256 จะไม่ผ่านนโยบายนั้น ทั้งที่ผ่านการตรวจ strength แบบผิวเผิน เลเยอร์คลาสแยกทั้งสองออกจากกันด้วยชื่อ คือ esAES256Bit สำหรับ R5 เทียบกับ esAES256BitAcroX สำหรับ R6 และพร็อพเพอร์ตี EncryptionAcroX ตอบคำถามเรื่อง revision ด้วยค่าบูลีนตัวเดียว
Permission bits กับข้อกำหนดย่อยเรื่องความยาวคีย์
EncodePermissions อัดแฟล็กแปดตัวลงในจำนวนเต็มที่ Encrypt และ EncryptFile ต้องการ การพิมพ์ การคัดลอก การแก้ไข และการเพิ่มโน้ต ประกอบกันเป็นชุดพื้นฐาน ส่วนการกรอกฟิลด์ การคัดลอกเพื่อการเข้าถึง การประกอบเอกสาร และการพิมพ์คุณภาพเต็ม ประกอบกันเป็นชุดขยาย ข้อกำหนดย่อยที่เดโมการเข้ารหัสของไลบรารีเองระบุไว้ตรง ๆ คือสี่ตัวในชุดขยายมีผลเฉพาะที่ความแข็งแรงระดับ 128 บิตขึ้นไปเท่านั้น แฟล็กการพิมพ์คุณภาพเต็มก็อยู่ใต้กฎเดียวกัน หากล้างค่ามันเพื่อบังคับให้พิมพ์ที่ความละเอียดต่ำ เอกสาร 40 บิตจะเมินคุณ เพราะการลดระดับนั้นก็ต้องใช้การเข้ารหัส 128 บิตขึ้นไปเช่นกัน ลองเข้ารหัสนโยบาย "พิมพ์ที่ความละเอียดต่ำเท่านั้น" ลงในไฟล์ 40 บิตดู แล้วโปรแกรมอ่านทุกตัวก็จะพิมพ์ที่คุณภาพเต็มอยู่ดี
คำถามที่ลึกกว่านั้นคือใครเป็นผู้บังคับใช้บิตเหล่านี้ และคำตอบคือไม่มีใครที่คุณไว้ใจได้ permission เป็นคำสั่งถึงโปรแกรมอ่านที่ทำตามข้อกำหนด ไม่ใช่ข้อจำกัดเชิงวิทยาการรหัสลับ คีย์ถอดรหัสเหมือนกันทุกประการไม่ว่าจะอนุญาตหรือห้ามการคัดลอก ชุด permission ที่ล็อกแน่นหนาจึงเพียงทำให้โปรแกรมอ่านที่ซื่อสัตย์ยังคงซื่อสัตย์ต่อไป โปรแกรมอ่านที่เลือกจะเมินบิตเหล่านั้นไม่พบอุปสรรคเชิงรหัสลับใด ๆ เลย หากภาระผูกพันคือการป้องกันการดึงข้อมูลออกไป ไม่ใช่เพียงการทำให้ไม่อยากทำ ไฟล์นั้นต้องมี user password และเวิร์กโฟลว์ต้องมีการควบคุมระดับกระบวนการล้อมรอบไว้ และรายงานการตรวจสอบควรระบุว่าไฟล์แต่ละไฟล์อยู่ภายใต้ระบอบใดในสองแบบนี้จริง ๆ แทนที่จะมองแฟล็ก permission เป็นกุญแจล็อก
การตั้งนโยบายและการพิสูจน์ว่ามันมีผลจริง
การใส่การเข้ารหัสให้ไฟล์ที่มีอยู่แล้วไม่จำเป็นต้องโหลดไฟล์เข้า object tree EncryptFile ประมวลผลจากอินพุตไปยังเอาต์พุตในการเรียกครั้งเดียว และลูปการตรวจสอบจะเปิดผลลัพธ์ขึ้นมาใหม่เพื่อยืนยันสิ่งที่ลงไปอยู่บนดิสก์จริง เดโมการเข้ารหัสที่มาพร้อมไลบรารีก็ใช้รูปแบบเขียนแล้วอ่านกลับแบบเดียวกัน:
var
PDF: TPDFlib;
R: Integer;
begin
PDF := TPDFlib.Create;
try
R := PDF.EncryptFile('in.pdf', 'out.pdf', 'owner-secret', 'user-secret', 4,
PDF.EncodePermissions(1, 0, 0, 0, // อนุญาตพิมพ์ ห้ามคัดลอก/แก้ไข/ใส่โน้ต
0, 0, 0, 1)); // ชุดขยาย: พิมพ์คุณภาพเต็มเท่านั้น
if (R = 1) and (PDF.LoadFromFile('out.pdf', 'user-secret') = 1) then
begin
Writeln('algorithm = ', PDF.EncryptionAlgorithm);
Writeln('strength = ', PDF.EncryptionStrength);
Writeln('owner pw accepted: ', PDF.CheckPassword('owner-secret'));
end;
finally
PDF.Free;
end;
end;
ทีมที่ทำงานที่ระดับเอกสารจะได้การทำงานเดียวกันโดยใช้ typed set แทนการอัดบิต ซึ่งผ่านการรีวิวโค้ดไปได้โดยไม่ต้องเพ่งมองมากนัก:
if not Doc.Encrypt('owner-secret', 'user-secret', esAES256BitAcroX,
[ppCanPrint], [ppCanPrintFull]) then
raise Exception.Create('Encryption failed');
ไม่ว่าจะใช้ทางไหน ขั้นตอนอ่านกลับก็ไม่ใช่พิธีกรรมที่จะข้ามได้ตามใจ มันจับข้อผิดพลาดตอนดีพลอยที่มิฉะนั้นจะโผล่ขึ้นมาอีกหลายเดือนถัดมาบนเครื่องของลูกค้า ไม่ว่าจะเป็นไลบรารีบิลด์เก่าที่ลดระดับความแข็งแรงที่ร้องขอไว้อย่างเงียบ ๆ พาธเอาต์พุตที่ไม่เคยถูกเขียนเพราะไดเรกทอรีเป็นแบบอ่านอย่างเดียว หรือจำนวนเต็ม permission ที่ส่งอาร์กิวเมนต์ผิดลำดับ ทั้งสามกรณีผ่าน smoke test ในเครื่องแต่ไปพังหน้างาน และการเปิดเอาต์พุตขึ้นมาใหม่จะเปลี่ยนแต่ละกรณีให้กลายเป็น exception ที่คุณเห็นระหว่างการรันที่สร้างไฟล์นั้นเอง GetEncryptionFingerprint คืนค่ากะทัดรัดที่คุณเก็บไว้คู่กับเรกคอร์ดของงานได้ การเปรียบเทียบภายหลังจึงบอกได้ว่าเอาต์พุตสองไฟล์ใช้คอนฟิกการเข้ารหัสเดียวกันหรือไม่ โดยไม่ต้องเปิดไฟล์ใดขึ้นมาอีก
ผลบวกลวงในการตรวจสอบที่ควรเขียนโค้ดรับมือ
มีรูปแบบไม่กี่อย่างที่ผลักตัวสแกนความปลอดภัยไปสู่ข้อสรุปที่ผิดอย่างสม่ำเสมอ และแต่ละอย่างเกิดจากการยุบคำถามหลายส่วนให้เหลือคำตอบใช่หรือไม่ใช่ Identity crypt filter เป็นตัวอย่างที่ชัดที่สุด มี /Encrypt dictionary อยู่ ไฟล์รายงานว่าเข้ารหัสแล้ว แต่สตริงและสตรีมกลับวิ่งผ่าน Identity filter โดยไม่เปลี่ยนแปลง เนื้อหาจริงจึงเป็น plaintext ทางแก้คือการอ่าน StringFilterIdentity และ StreamFilterIdentity ก่อนจะประกาศว่าอะไรได้รับการป้องกันแล้ว
การแยกส่วนของ metadata ละเอียดอ่อนกว่านั้น EncryptMetadata ขัดแย้งกับส่วนที่เหลือของเอกสารได้ทั้งสองทิศทาง ทิ้งให้ไฟล์ที่เข้ารหัสมีแพ็กเกจ XMP ที่อ่านได้ หรือในกรณีที่พบน้อยกว่าก็กลับกัน คำว่า "ไฟล์นี้เข้ารหัสแล้ว" ไม่ได้บอกอะไรเลยว่า metadata ของมันเข้ารหัสด้วยหรือไม่ ซึ่งสำคัญทันทีที่ indexer หรือกฎกำหนดเส้นทางเอื้อมไปหยิบ title ไฟล์ฝังตัวเพิ่มแกนที่สามเข้ามา PDF อนุญาตให้มี crypt filter เฉพาะสำหรับไฟล์แนบโดยตรง ไฟล์แนบจึงอาจเป็นส่วนเดียวที่เข้ารหัสในเอกสารที่เปิดโล่ง หรือเป็นส่วนเดียวที่เป็น plaintext ในเอกสารที่เข้ารหัสก็ได้ จงเก็บการกำหนด filter ทั้งสามเป็นฟิลด์แยกกันสำหรับสตริง สตรีม และไฟล์ฝังตัว แล้วกับดักเหล่านี้จะไม่มีอันไหนเล่นงานคุณได้ แต่ถ้าเก็บเป็นบูลีนตัวเดียว การตัดสินผิดก็เป็นเพียงเรื่องของเวลา
การถอดการเข้ารหัส และการเลือกใช้กับไฟล์ใหม่
งานตรวจสอบมักจบลงด้วยการตัดสินใจถอดการป้องกันออก และกลไกก็ไม่ใช่อุปสรรคตรงนั้น DecryptFile(InputFileName, OutputFileName, Password) เขียนสำเนาที่ถอดรหัสแล้วออกมาโดยไม่ต้องโหลดเต็มรูปแบบ ส่วน Decrypt ของเอกสารที่โหลดแล้วก็ทำแบบเดียวกันในหน่วยความจำเมื่อไฟล์ถูกเปิดอยู่ก่อนแล้ว ทั้งคู่ต้องใช้รหัสผ่านที่ถูกต้อง และไม่มีตัวไหนข้ามการเข้ารหัสไปได้ ประตูจริงคือนโยบายมากกว่าโค้ด ดังนั้นจงเขียนกฎการรับงานให้ระบุชัดว่าเมื่อใดจึงอนุญาตให้ถอดได้ และบันทึกว่ารหัสผ่านชั้นใดเป็นผู้อนุมัติ เพราะขั้นตอนทางเทคนิคเองไม่ทิ้งร่องรอยไว้เลย
ตัวเลือกสำหรับเอาต์พุตใหม่แคบกว่าที่ค่า Strength ทั้งห้าค่าชวนให้คิด ให้ใช้ Strength 4 ซึ่งคือ AES-256 revision 6 เว้นแต่คุณจำเป็นต้องเปิดไฟล์ในโปรแกรมอ่านที่เก่ากว่า Acrobat X ส่วน Strength 2 หรือ AES-128 เป็นพื้นเชิงปฏิบัติสำหรับกองโปรแกรมอ่านรุ่นเก่าที่อัปเกรดไม่ได้ ตัวเลือก RC4 ที่ 0 และ 1 มีไว้ให้คุณอ่านและตรวจสอบคลังเอกสารเก่า ไม่ใช่มีไว้ให้ผลิตอะไรใหม่ด้วยมัน การหยิบมาใช้กับงานออกแบบปี 2026 เป็นสัญญาณว่าข้อกำหนดจากต้นทางล้าสมัยแล้ว
สถานะการเข้ารหัสส่งผลตรงต่อการตัดสินใจเรื่องการลงลายเซ็น เพราะเวิร์กเบนช์ที่ตรวจสอบและลงลายเซ็นเอกสารต้องการวินัยการอ่านกลับแบบเดียวกับที่การตรวจสอบนี้พึ่งพา เรื่องนั้นครอบคลุมไว้ในบทความว่าด้วยเวิร์กเบนช์การปฏิบัติตามข้อกำหนดและการลงลายเซ็น เมื่อมีงานแบตช์ที่เรียก EncryptFile กับเอกสารขนาดใหญ่นับพันไฟล์ คู่มือการเข้าถึงโดยตรงสำหรับ PDF ขนาดใหญ่ แสดงวิธีตรึงการใช้หน่วยความจำให้คงที่ตลอดการทำงาน ส่วนเอกสารอ้างอิง API การเข้ารหัสฉบับสมบูรณ์อยู่ที่หน้าผลิตภัณฑ์ PDF Library for Delphi