Doing PDF Redaction Right: Why a Black Rectangle Is Not Enough
If you have ever received a "redacted" PDF where the sensitive text is behind a black rectangle, and you copied it out anyway, you already know the problem. Drawing a shape over text does not remove the text. The bytes a
If you have ever received a "redacted" PDF where the sensitive text is behind a black rectangle, and you copied it out anyway, you already know the problem. Drawing a shape over text does not remove the text. The bytes are still in the file. Ctrl+F still finds them. Copy and paste still pastes them.
Doing redaction correctly is not hard, but the two common approaches (annotation overlays and text replacement) both have failure modes worth understanding before you ship a tool or trust an output.
The three approaches
1. Draw a black rectangle (do not do this)
This is what most quick tools and most home-grown scripts do. They add an annotation or a drawing object on top of the page at the coordinates of the sensitive text. Visually it looks redacted. The text objects underneath are untouched.
Extracting is trivial:
import { getDocument } from 'pdfjs-dist';
const pdf = await getDocument('redacted.pdf').promise;
const page = await pdf.getPage(1);
const textContent = await page.getTextContent();
console.log(textContent.items.map(i => i.str).join(' '));
// prints the "redacted" text
Every PDF library on earth will do this. So will Preview, so will Acrobat's built-in text selection, so will Ctrl+A / Ctrl+C. If your redacted PDF failed this test, it was decorative, not real.
2. Text substitution (better, still fragile)
Some tools locate the target text in the content stream and replace those bytes with an equal number of "X" characters or spaces. Ctrl+F now fails, and copy-paste returns garbage. This is closer to real redaction, but it has three failure modes:
- Kerning tables leak the original. If the target was "SSN 123-45-6789" and it is replaced with 15 X's, the character widths of the original are still baked into the Tj operator's positioning. A determined reader can reconstruct the original text from the width sequence.
- Ligatures and non-ASCII. Text runs that used a ligature (fi, ffi) or non-Latin characters may not be substitutable byte-for-byte, and the tool either fails silently or corrupts the page.
- XObject forms and referenced content. If the sensitive text lives in a shared XObject that appears on multiple pages, replacing it on one page corrupts the others.
3. Rasterise, overlay, reflatten (the correct approach)
The reliable method is to render each redacted page to an image, paint the redaction rectangles onto the image, and rebuild the PDF page as an image-only page. The text objects are gone entirely because the page no longer contains text objects; it contains a raster.
const canvas = document.createElement('canvas');
canvas.width = page.width * 2;
canvas.height = page.height * 2;
const ctx = canvas.getContext('2d');
await page.render({ canvasContext: ctx, viewport: page.getViewport({ scale: 2 }) }).promise;
for (const [x, y, w, h] of redactionRects) {
ctx.fillStyle = 'black';
ctx.fillRect(x * 2, y * 2, w * 2, h * 2);
}
const dataUrl = canvas.toDataURL('image/png');
The output is bigger (an image instead of vector text) and it loses accessibility (screen readers cannot read the raster), but the text is genuinely gone. Optionally you can OCR the flattened page to add a searchable text layer that contains only the non-redacted content.
The compliance check
- Open the redacted file in a fresh reader.
- Ctrl+A, then Ctrl+C.
- Paste into a plain text editor.
- Search for any of the redacted terms.
If any redacted term appears, the redaction is decorative. If none appears, the redaction is real.
What I use
I moved to PDFslime's redaction tool because it defaults to the rasterise-and-flatten approach, warns you if you try the annotation-only mode, and runs the whole operation in the browser so the sensitive source never touches a server. The tool also runs the paste test on the output automatically and refuses to hand back a file that fails.
If you build a tool that does redaction, please default to the flattening approach. Users assume redaction is safe; if your default is unsafe you will ship a leaked contract for someone.
What flatten redaction cannot fix
Embedded images that contain the sensitive information. If page 3 has a photograph of a driver license, redacting the OCR text on page 3 does not touch the image. You have to redact the image itself before adding it to the PDF, or rasterise the page and paint over the image region in the raster.
And metadata. A PDF's Info dictionary and XMP packet often contain the author's name and previous document title, neither of which any page-level redaction can touch. Strip metadata separately.
Summary
annotation overlay (broken) < byte substitution (leaky) < rasterise-and-flatten (correct). Every "redacted" file worth taking seriously should be built with the last approach and tested with the paste test.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.