runlocally

runlocally engineering notes

Remove Excel Sheet Protection

How Remove Excel Sheet Protection is built

By Geppetto · · Open Remove Excel Sheet Protection →

Remove Excel Sheet Protection removes sheet-editing locks and workbook-structure protection from an .xlsx/.xlsm file you can already open. This post is about why that’s a much smaller problem than it sounds — and about the one case it deliberately can’t touch.

Tech used

Sheet protection is a flag element, not encryption

Excel’s “Protect Sheet” and “Protect Workbook Structure” features are, as stored in the file, a single self-closing XML element: <sheetProtection .../> inside a worksheet part, or <workbookProtection .../> inside xl/workbook.xml. Their attributes hold a hash of the password (so Excel can verify a password if one was set) and flags for what’s locked — but the protection itself is enforced by Excel’s UI honoring that element’s presence, not by encrypting the sheet data. Removing the element removes the lock; no password, hash, or cryptography is involved. The tool never attempts to recover, guess, or verify a password — it deletes the element outright, the same way deleting a read-only flag on a file doesn’t require knowing why it was set.

As in Strip XLSX Metadata, this is done with the browser’s built-in DOMParser/XMLSerializer against the specific XML parts involved, copying every other ZIP entry through @zip.js/zip.js unchanged.

Telling a sheet name from its file path

Reporting which sheets had protection removed (rather than just “3 things were unlocked”) means mapping each xl/worksheets/sheet3.xml back to the human-readable name Excel shows on its tab — and that mapping goes through a relationships file, not a fixed naming convention:

const relationshipTarget = targetsById.get(sheet.relationshipId);
const fallbackTarget = worksheetPaths[index];
const target = relationshipTarget ?? fallbackTarget;
if (target) names.set(target, sheet.name);

xl/workbook.xml lists each sheet’s display name alongside a relationship id (r:id); xl/_rels/workbook.xml.rels resolves that id to the worksheet’s actual file path. Sheet file names don’t reliably line up with tab order or display name — a workbook can have “Sheet1” as its third tab after reordering — so both documents have to be read and cross-referenced rather than assuming sheet1.xml is “Sheet1”.

Implementation & operational notes

A magic-byte check runs before any ZIP parsing, to give an accurate error up front. A file that requires a password just to open is stored as a Compound File Binary (CFB) container — a completely different, non-ZIP format from OOXML, identified by its own fixed 8-byte signature (D0 CF 11 E0 A1 B1 1A E1). The tool checks for that signature before attempting to read the file as a ZIP:

const CFB_SIGNATURE = [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1];
...
async function assertZipPackage(file: File): Promise<void> {
  const header = new Uint8Array(await file.slice(0, 8).arrayBuffer());
  const isCfb = CFB_SIGNATURE.every((byte, index) => header[index] === byte);
  if (isCfb) throw new AppError('errOpenPasswordProtected');
  ...
}

Without this check, a CFB file would just fail the ZIP read with a generic parse error — accurate, but not useful. Sniffing the signature first means the message a user sees is specific: this file needs an opening password, which this tool doesn’t handle — a materially different situation from sheet protection, and one worth explaining rather than lumping into “couldn’t process this file.”

Every other ZIP entry keeps its original metadata, not just its bytes. When copying entries through unchanged, the tool preserves each entry’s original comment, timestamps, and file-attribute fields (entryOptions() reads comment, lastModDate, internalFileAttributes, and so on off the source entry and passes them to the rewritten one) rather than letting the ZIP writer fall back to defaults for everything it doesn’t explicitly touch. Only the two worksheet/workbook parts that actually had a protection element are rewritten; everything else — including its metadata — round-trips exactly.

No protection found is reported as a result, not an error or a silent no-op. If neither element is present anywhere in the workbook, the tool says so plainly rather than either failing or producing a redundant unchanged download.

Scope is stated rather than implied. The tool’s own copy is explicit that it only removes sheet/workbook editing locks — not an opening password (see above), not file encryption, and not VBA project protection, which uses its own separate, unrelated locking mechanism inside a macro-enabled workbook.

Try it / source

Remove Excel Sheet Protection

Open the tool → All posts →