Word 有個保護文件功能,可限定只有特定欄位可以編輯,其餘部分唯讀,很適合設計表單供使用者填寫。參考:學會 Word 限制編輯與 Excel 保護工作表,給人填寫表單不出錯 by 電腦玩物

例如以下文件,就只有黃色部分可以填寫:(有人想要這張獎狀嗎?本站的小額捐款帳戶是... )

但要注意,這種文件保護功能以方便為主,不具資安防護能力,設定時雖然可輸入密碼,必須知道密碼才能用 Word 解除保護,但輸入密碼時早有警語 - 文件並未加密,惡意使用者可以編輯檔案及移除密碼。(註:加密仍是唯一能提供有效數位防護的手段)

意思是保護文件密碼可以被破解?是的,對會用 OpenXML SDK 的人來說,幾行程式就能搞定,移除密碼拿回編輯檔案的權利,但我不是「惡意使用者」啦,會寫程式不是罪啊!

程式原理很簡單,設定文件保護 docx 的 XML 會有個 DocumentProtection 設定 設定,將它刪除就等同移除文件保護。寫個 .NET 小工具呼叫 OpenXML SDK 不難做到 (用 OpenXML SDK 處理 Word 文件的範例可參考這篇:使用 .NET 程式擷取 Word 表格內容),但以 EXE 檔形的缺點是程式內容不透明常被質疑安全性,故這回試試用 PowerShell 來實做。

由於需要用到 .Elements<T>() 等泛型方法,對直譯式弱型別的 PowerShell 用起來很花功夫,故這回我改用混搭做法,在 PowerShell 裡直接寫 C# 程式再用 Add-Type -TypeDefinition "...C# Code..." -Language CSharp 現場編譯成類別使用。有個眉角是編譯時需參照 DocumentFormat.OpenXML.dll 及 WindowsBase.dll,我的做法是先開個 .NET 專案用 NuGet 安裝拿到檔案再複製到 .ps1 目錄,接著用 Add-Type -ReferencedAssemblies ("$PSScriptRoot\DocumentFormat.OpenXml.dll","$PSScriptRoot\WindowsBase.dll") 加入參照。完整程式碼如下:(Unlock-Docx.ps1)

Param (
    [Parameter(Mandatory=$true)][string]$docxPath,
    [Parameter(Mandatory=$true)][string]$saveAsPath
)
$ErrorActionPreference = "STOP"
Add-Type -Path "$PSScriptRoot\DocumentFormat.OpenXml.dll"
Add-Type -TypeDefinition @"
using System.IO;
using System.Linq;
using System;
public class DocxUnlocker
{
    public static void Unlock(string srcPath, string dstPath)
    {
        using (var ms = new MemoryStream(File.ReadAllBytes(srcPath)))
        {
            using (var doc = DocumentFormat.OpenXml.Packaging.WordprocessingDocument.Open(ms, true))
            {
                var protect = doc.MainDocumentPart.DocumentSettingsPart.Settings
                    .Elements<DocumentFormat.OpenXml.Wordprocessing.DocumentProtection>()
                    .FirstOrDefault();
                if (protect == null) throw new ApplicationException(srcPath + " is not locked.");
                protect.Remove();
                doc.SaveAs(dstPath);
            }
        }
    }
}
"@ -Language CSharp -ReferencedAssemblies ("$PSScriptRoot\DocumentFormat.OpenXml.dll","$PSScriptRoot\WindowsBase.dll")
[DocxUnlocker]::Unlock($docxPath, $saveAsPath)

執行 .\Unlock-Docx.ps1 來源docx路徑 結果docx路徑 就能拿到可編輯的 Word 檔囉!

Example of how to remove document protection from docx with OpenXML SDK and PowerShell.


Comments

# by 羽山

笑死,頭香

# by ByTIM

哪時頒發?我要裱框!

# by Jeffrey

to ByTIM,大感謝! 咦,等等,那個斗內的部分呢?XD

Post a comment