C#でのPDF再編集:IronPDFで機密データを削除し、ドキュメントをサニタイズする

This article was translated from English: Does it need improvement?
Translated
View the article in English

PDF redaction in C# .NET with IronPDF は、ドキュメントの内部構造からセンシティブなコンテンツを永続的に削除します。 これはテキストの上に黒い四角形を置くだけではありません:IronPDFは正規表現パターンマッチングによるテキストの再編集署名や画像のための領域ベースの再編集、メタデータの除去、埋め込まれたスクリプトを除去するためのドキュメントのサニタイズ、脆弱性のスキャンを提供し、.NET開発者にHIPAAGDPRPCI DSSに準拠したドキュメント保護ワークフローのための完全なツールキットを提供します。

TL;DR:クイックスタートガイド

このチュートリアルでは、C# .NETで、テキストパターン、画像領域、メタデータ、埋め込みスクリプトなど、PDF文書から機密コンテンツを恒久的に削除する方法について説明します。

  • 対象者: .NET開発者で、医療、法律、金融、政府関連の機密文書を扱う人。
  • 構築するもの 正規表現パターンマッチングによるテキスト再編集(SSN、クレジットカード、電子メール)、署名や写真の座標ベースの領域再編集、メタデータのクリーンアップ、埋め込まれたスクリプトを取り除くPDFサニタイズ、YARAベースの脆弱性スキャン。
  • Where it runs: .NET 10、.NET 8 LTS、.NET Framework 4.6.2+、.NET Standard 2.0。すべての操作は、外部依存なしにローカルで実行されます。
  • このアプローチを使用する場合:法的証拠開示、情報公開請求、外部配布のために文書を共有する必要がある場合。
  • 技術的に重要な理由:ビジュアルオーバーレイは、PDFのコンテンツストリームに元のテキストを復元可能なまま残します。 IronPDFの再編集は文書構造自体から文字データを削除し、復元を不可能にします。

わずか数行のコードでPDFから機密テキストを再編集します:

Nuget Icon今すぐ NuGet で PDF を作成してみましょう:

  1. NuGet パッケージ マネージャーを使用して IronPDF をインストールします

    PM > Install-Package IronPdf

  2. このコード スニペットをコピーして実行します。

    using IronPdf;
    
    PdfDocument pdf = PdfDocument.FromFile("confidential-report.pdf");
    pdf.RedactTextOnAllPages("CONFIDENTIAL");
    pdf.SaveAs("redacted-report.pdf");
  3. 実際の環境でテストするためにデプロイする

    今すぐ無料トライアルでプロジェクトに IronPDF を使い始めましょう
    arrow pointer

IronPDFを購入または30日間のトライアルにサインアップした後、アプリケーションの最初にライセンスキーを追加してください。

IronPdf.License.LicenseKey = "KEY";
IronPdf.License.LicenseKey = "KEY";
$vbLabelText   $csharpLabel

今日あなたのプロジェクトでIronPDFを無料トライアルで使用開始。

最初のステップ:
green arrow pointer
NuGet 購入の準備ができていませんか?

PM >  Install-Package IronPdf

IronPDFNuGet でチェックしてください。1000万回以上のダウンロードで、C#によるPDF開発を変革しています。 DLL または Windowsインストーラー をダウンロードすることもできます。

目次

真のリダクションとビジュアル オーバーレイの違いは何ですか?

機密文書を扱う人にとって、真の冗長化と視覚的オーバーレイの区別を理解することは非常に重要です。 多くのツールや手作業による方法では、実際に基礎となるデータを削除することなく、再編集したように見せかけることができます。 このような誤ったセキュリティ意識は、多くの著名なデータ漏洩やコンプライアンス違反の原因となっています。

ビジュアルオーバーレイアプローチは、通常、機密コンテンツの上に不透明な図形を描画します。 テキストは、PDFの構造内に完全にそのまま残ります。 ドキュメントを見た人には黒い四角形が見えますが、ファイルのコンテンツストリームには元の文字が残っています。 ページ上のすべてのテキストを選択するか、アクセシビリティツールを使用するか、生のPDFデータを調べれば、隠されていたはずのものがすべて明らかになります。 冗長化された提出書類が、相手方の弁護士によって冗長化されていなかったために、裁判が危うくなったことがあります。 政府機関は、検閲されたように見えても完全に復元可能な機密情報を誤って公開したことがあります。

真の再編集とは異なります。 IronPdfのredactionメソッドを使用すると、ライブラリはPDFの内部構造から指定されたテキストを探し出し、完全に削除します。 文字データはコンテンツストリームから削除されます。 視覚的な表現は、通常、黒い四角形の再編集マークで置き換えられますが、元のコンテンツはもはやファイルのどこにも存在しません。どんなに選択しても、コピーしても、フォレンジック分析しても、永久に消去されたものを復元することはできません。

IronPDFはPDFを構造レベルで修正することにより、真の再編集を実装します。 RedactTextOnAllPagesメソッドとその亜種は、ページのコンテンツを検索し、一致するテキストを識別し、ドキュメントオブジェクトモデルから削除し、オプションで、コンテンツが表示されていた場所に視覚的なインジケータを描画します。 このアプローチは、安全な文書の再編集に関する NIST などの組織のガイドラインに沿ったものです。

実用的な意味合いは大きい。 ドキュメントを社外と共有する必要がある場合、法的証拠開示のためにファイルを提出する必要がある場合、情報公開請求の下で記録を公開する必要がある場合、または個人を特定できる情報を保護しながらレポートを配布する必要がある場合、適切な保護を提供するのは真の再編集のみです。 視覚的なオーバーレイは、単に特定の部分から注意を引きたい内部草稿には十分かもしれませんが、実際のデータ保護には決して信用してはなりません。 その他の文書セキュリティ対策については、PDFの暗号化デジタル署名のガイドを参照してください。


文書全体にわたって C# で PDF テキストを再編集するにはどうすればよいですか?

最も一般的な再編集のシナリオは、文書全体を通して特定のテキストをすべて削除することです。 レポートから人名を削除したり、財務諸表から口座番号を削除したり、社外に配布する前に社内の参照コードを削除したりする必要があるかもしれません。 IronPDFはRedactTextOnAllPagesメソッドでこれを簡単にします。

入力

氏名、社会保障番号、従業員IDなどの個人情報を含む従業員記録文書。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-text-all-pages.cs
using IronPdf;

// Load the source document
PdfDocument pdf = PdfDocument.FromFile("employee-records.pdf");

// Redact an employee name from the entire document
pdf.RedactTextOnAllPages("John Smith");

// Redact a Social Security Number
pdf.RedactTextOnAllPages("123-45-6789");

// Redact an internal employee ID
pdf.RedactTextOnAllPages("EMP-2024-0042");

// Save the cleaned document
pdf.SaveAs("employee-records-redacted.pdf");
$vbLabelText   $csharpLabel

このコードは、従業員情報を含むPDFをロードし、各値に対してRedactTextOnAllPagesを呼び出すことによって3つの機密データを削除します。 各コールは、文書内のすべてのページを検索し、従業員の名前、社会保障番号、社内識別子に一致するすべてのインスタンスを恒久的に削除します。

サンプル出力

デフォルトの動作では、冗長化されたテキストが表示される場所に黒い四角形が描かれ、ドキュメント構造内の実際の文字がアスタリスクに置き換えられます。 これにより、再編集が行われたことを視覚的に確認し、元のコンテンツが完全に消えていることを確認することができます。

長い文書や複数の冗長化ターゲットで作業する場合は、これらの呼び出しを効率的に連鎖させることができます:

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-text-list.cs
using IronPdf;
using System.Collections.Generic;

// Load the document once
PdfDocument pdf = PdfDocument.FromFile("quarterly-report.pdf");

// Define all terms that need redaction
List<string> sensitiveTerms = new List<string>
{
    "Project Titan",
    "Sarah Johnson",
    "Budget: $4.2M",
    "Q3-INTERNAL-2024",
    "sarah.johnson@company.com"
};

// Redact each term
foreach (string term in sensitiveTerms)
{
    pdf.RedactTextOnAllPages(term);
}

// Save the result
pdf.SaveAs("quarterly-report-public.pdf");
$vbLabelText   $csharpLabel

このパターンは、削除すべき微妙な値のリストがわかっている場合に有効です。 文書は一度読み込まれ、すべての再編集がメモリ内で適用され、最終結果が保存されます。 各用語は独立して処理されるため、用語間の部分的な一致や書式の違いが他の再編集に影響することはありません。

特定のページのみテキストを再編集するにはどうすればよいですか?

時には、編集箇所をより正確にコントロールする必要があります。 文書には、そのまま残すべき情報が記載された表紙ページがあるかもしれませんし、機密データが特定のセクションにのみ記載されていることがわかっているかもしれません。 IronPDFは単一ページの再編集のためにRedactTextOnPageを、複数の特定のページを対象とするためにRedactTextOnPagesを提供します。

入力

複数ページにわたる契約書のバンドルで、署名ページには顧客名、文書全体の特定のページには財務用語が記載されています。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-specific-pages.cs
using IronPdf;

// Load the document
PdfDocument pdf = PdfDocument.FromFile("contract-bundle.pdf");

// Redact text only on page 1 (index 0)
pdf.RedactTextOnPage(0, "Client Name: Acme Corporation");

// Redact text on pages 3, 5, and 7 (indices 2, 4, 6)
int[] financialPages = { 2, 4, 6 };
pdf.RedactTextOnPages(financialPages, "Payment Terms: Net 30");

// Other pages remain untouched except for the specific redactions applied

pdf.SaveAs("contract-bundle-redacted.pdf");
$vbLabelText   $csharpLabel

このコードは、単一のページにはRedactTextOnPageを、複数の特定のページにはRedactTextOnPagesを使用することで、ターゲットを絞った再編集を示しています。 クライアント名は1ページ目(インデックス0)のみ削除し、支払い条件は3、5、7ページ目(インデックス2、4、6)から削除します。

サンプル出力

IronPdfのページインデックスはゼロベースです。つまり、最初のページはインデックス0、2ページ目はインデックス1、といった具合です。 これは、標準的なプログラミングの慣例と一致し、多くの開発者が配列アクセスについて考える方法と一致します。

特定のページをターゲットにすることで、大きな文書を処理する際のパフォーマンスが向上します。 数箇所にしか出てこないテキストを何百ページもスキャンするのではなく、再編集エンジンに正確に指示することができます。 これは、何千ものドキュメントを扱う可能性のあるバッチ処理シナリオにとって重要です。 スループットを最大化するために、asyncとマルチスレッドテクニックの使用を検討してください。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-large-document.cs
using IronPdf;

// Process a large document efficiently
PdfDocument pdf = PdfDocument.FromFile("annual-report-500-pages.pdf");

// We know from document structure that:
// - Executive summary with names is on pages 1-3
// - Financial data is on pages 45-60
// - Appendix with employee info is on pages 480-495

// Redact executive names from summary section
for (int i = 0; i <= 2; i++)
{
    pdf.RedactTextOnPage(i, "CEO: Robert Williams");
    pdf.RedactTextOnPage(i, "CFO: Maria Garcia");
}

// Redact specific financial figures from the financial section
int[] financialSection = { 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59 };
pdf.RedactTextOnPages(financialSection, "Net Revenue: $847M");

// Redact employee identifiers from appendix
for (int i = 479; i <= 494; i++)
{
    pdf.RedactTextOnPage(i, "Employee ID:");
}

pdf.SaveAs("annual-report-public-release.pdf");
$vbLabelText   $csharpLabel

このターゲットを絞ったアプローチでは、500ページの文書から関連するセクションのみを処理するため、各ページをスキャンして各修正用語を探すのに比べ、実行時間が大幅に短縮されます。

再編集されたコンテンツの外観をカスタマイズするにはどうすればよいですか?

IronPdfは最終的なドキュメントにどのように再編集が表示されるかをコントロールするためのいくつかのパラメータを提供します。 大文字と小文字の区別、単語全体のマッチング、視覚的な四角形を描くかどうか、冗長化されたコンテンツの代わりに表示される置換テキストを調整できます。

入力

分類ラベル、パスワード、内部参照コードなど、さまざまな機密用語が含まれ、さまざまな再編集処理が必要な法務書類。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/customize-redaction-appearance.cs
using IronPdf;

// Load the document
PdfDocument pdf = PdfDocument.FromFile("legal-brief.pdf");

// Case-sensitive redaction: only matches exact case
// "CLASSIFIED" will be redacted but "classified" or "Classified" will not
pdf.RedactTextOnAllPages(
    "CLASSIFIED",
    CaseSensitive: true,
    OnlyMatchWholeWords: true,
    DrawRectangles: true,
    ReplacementText: "[REDACTED]"
);

// Case-insensitive redaction: matches regardless of case
// Will redact "Secret", "SECRET", "secret", etc.
pdf.RedactTextOnAllPages(
    "secret",
    CaseSensitive: false,
    OnlyMatchWholeWords: true,
    DrawRectangles: true,
    ReplacementText: "*****"
);

// Whole word disabled: matches partial strings too
// Will redact "password", "passwords", "mypassword123", etc.
pdf.RedactTextOnAllPages(
    "password",
    CaseSensitive: false,
    OnlyMatchWholeWords: false,
    DrawRectangles: true,
    ReplacementText: "XXXXX"
);

// No visual rectangle: text is removed but no black box appears
// Useful when you want seamless removal without obvious redaction marks
pdf.RedactTextOnAllPages(
    "internal-reference-code",
    CaseSensitive: true,
    OnlyMatchWholeWords: true,
    DrawRectangles: false,
    ReplacementText: ""
);

pdf.SaveAs("legal-brief-redacted.pdf");
$vbLabelText   $csharpLabel

このコードでは、RedactTextOnAllPagesのオプションのパラメータを使用して、4つの異なる再編集の構成を示します。 REDACTED]"置換による大文字小文字を区別しない完全一致、アスタリスクによる大文字小文字を区別しない一致、"password "のようなバリエーションをキャッチするための部分的な単語の一致、シームレスなコンテンツ除去のための視覚的な四角形を使用しない不可視の除去を示します。

サンプル出力

パラメータは、要件に応じてさまざまな目的を果たします:

CaseSensitiveは、マッチングが文字の大文字小文字を考慮するかどうかを決定します。 法律文書では、意味を持つ特定の大文字小文字を使用することが多いため、大文字小文字を区別するマッチングにより、完全に一致するもののみを削除します。 大文字と小文字が異なる一般的なテキストを処理する場合は、大文字と小文字を区別しないマッチングを行い、すべてのインスタンスをキャッチする必要があります。

OnlyMatchWholeWordsは、検索が完全な単語と部分的な文字列のどちらにマッチするかを制御します。 名前を再編集する場合、"Smith"が誤って"Blacksmith"や"Smithfield"の一部を再編集してしまわないよう、単語全体を一致させるのが一般的です。 口座番号の接頭辞のようなパターンを再編集する場合、バリエーションをキャッチするために部分的なマッチングが必要になることがあります。

DrawRectanglesは、コンテンツが削除された場所にブラックボックスが表示されるかどうかを指定します。 ほとんどの規制や法的な文脈では、コンテンツが誤って省略されたのではなく、意図的に削除されたことを示す証拠として、目に見える再編集マークが必要です。 社内のワークフローでは、よりクリーンなアウトプットのために、見えない部分を削除することが望ましい場合があります。

置換テキストは、冗長化されたコンテンツの代わりに表示される文字を定義します。 一般的な選択肢としては、アスタリスク、"REDACTED"ラベル、空文字列などがあります。 誰かが冗長化された領域を選択またはコピーしようとすると、置換テキストが文書構造に表示されます。


どのように正規表現を使ってセンシティブなパターンを見つけ、再編集できますか?

既知のテキスト文字列の再編集は、削除する特定の値がある場合に機能しますが、多くの機密データ型は固定値ではなく予測可能なパターンに従います。 社会保障番号、クレジットカード番号、電子メールアドレス、電話番号、および日付はすべて、正規表現で一致させることができる認識可能な形式を持っています。 パターンベースの再編集システムを構築することで、事前にすべての特定の値を知ることなく、PDFコンテンツから個人情報を削除することができます。

IronPdf のテキスト抽出機能と再編集メソッドを組み合わせることで、強力なパターンマッチングワークフローが可能になります。 テキストを抽出し、.NET正規表現を使って一致するものを特定し、検出された値をそれぞれ再編集します。

using IronPdf;
using System.Text.RegularExpressions;
using System.Collections.Generic;

public class PatternRedactor
{
    // Common patterns for sensitive data
    private static readonly Dictionary<string, string> SensitivePatterns = new Dictionary<string, string>
    {
        // US Social Security Number: 123-45-6789
        { "SSN", @"\b\d{3}-\d{2}-\d{4}\b" },

        // Credit Card Numbers: various formats with 13-19 digits
        { "CreditCard", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },

        // Email Addresses
        { "Email", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },

        // US Phone Numbers: (123) 456-7890 or 123-456-7890
        { "Phone", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" },

        // Dates: MM/DD/YYYY or MM-DD-YYYY
        { "Date", @"\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b" },

        // IP Addresses
        { "IPAddress", @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b" }
    };

    public void RedactPatterns(string inputPath, string outputPath, params string[] patternNames)
    {
        // Load the PDF
        PdfDocument pdf = PdfDocument.FromFile(inputPath);

        // Extract all text from the document
        string fullText = pdf.ExtractAllText();

        // Track unique matches to avoid duplicate redaction attempts
        HashSet<string> matchesToRedact = new HashSet<string>();

        // Find all matches for requested patterns
        foreach (string patternName in patternNames)
        {
            if (SensitivePatterns.TryGetValue(patternName, out string pattern))
            {
                Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
                MatchCollection matches = regex.Matches(fullText);

                foreach (Match match in matches)
                {
                    matchesToRedact.Add(match.Value);
                }
            }
        }

        // Redact each unique match
        foreach (string sensitiveValue in matchesToRedact)
        {
            pdf.RedactTextOnAllPages(sensitiveValue);
        }

        // Save the redacted document
        pdf.SaveAs(outputPath);
    }
}

// Usage example
class Program
{
    static void Main()
    {
        PatternRedactor redactor = new PatternRedactor();

        // Redact SSNs and credit cards from a financial document
        redactor.RedactPatterns(
            "customer-data.pdf",
            "customer-data-safe.pdf",
            "SSN", "CreditCard", "Email"
        );
    }
}
using IronPdf;
using System.Text.RegularExpressions;
using System.Collections.Generic;

public class PatternRedactor
{
    // Common patterns for sensitive data
    private static readonly Dictionary<string, string> SensitivePatterns = new Dictionary<string, string>
    {
        // US Social Security Number: 123-45-6789
        { "SSN", @"\b\d{3}-\d{2}-\d{4}\b" },

        // Credit Card Numbers: various formats with 13-19 digits
        { "CreditCard", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },

        // Email Addresses
        { "Email", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },

        // US Phone Numbers: (123) 456-7890 or 123-456-7890
        { "Phone", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" },

        // Dates: MM/DD/YYYY or MM-DD-YYYY
        { "Date", @"\b\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b" },

        // IP Addresses
        { "IPAddress", @"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b" }
    };

    public void RedactPatterns(string inputPath, string outputPath, params string[] patternNames)
    {
        // Load the PDF
        PdfDocument pdf = PdfDocument.FromFile(inputPath);

        // Extract all text from the document
        string fullText = pdf.ExtractAllText();

        // Track unique matches to avoid duplicate redaction attempts
        HashSet<string> matchesToRedact = new HashSet<string>();

        // Find all matches for requested patterns
        foreach (string patternName in patternNames)
        {
            if (SensitivePatterns.TryGetValue(patternName, out string pattern))
            {
                Regex regex = new Regex(pattern, RegexOptions.IgnoreCase);
                MatchCollection matches = regex.Matches(fullText);

                foreach (Match match in matches)
                {
                    matchesToRedact.Add(match.Value);
                }
            }
        }

        // Redact each unique match
        foreach (string sensitiveValue in matchesToRedact)
        {
            pdf.RedactTextOnAllPages(sensitiveValue);
        }

        // Save the redacted document
        pdf.SaveAs(outputPath);
    }
}

// Usage example
class Program
{
    static void Main()
    {
        PatternRedactor redactor = new PatternRedactor();

        // Redact SSNs and credit cards from a financial document
        redactor.RedactPatterns(
            "customer-data.pdf",
            "customer-data-safe.pdf",
            "SSN", "CreditCard", "Email"
        );
    }
}
$vbLabelText   $csharpLabel

このパターンベースのアプローチは、一度パターンを定義すれば、どの文書にも適用できるため、拡張性に優れています。 新しいデータ型を追加するには、辞書に新しい正規表現パターンを追加するだけです。

再利用可能な機密データ スキャナーを構築するには?

本番環境では、文書をスキャンし、機密情報の有無を報告した上で、冗長化するかどうかを決定する必要があります。 これは、コンプライアンス監査に役立ち、再編集の決定を人間がレビューすることを可能にします。 次のクラスは、再編集と同時にスキャン機能を提供します。

using IronPdf;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;

public class SensitiveDataMatch
{
    public string PatternType { get; set; }
    public string Value { get; set; }
    public int PageNumber { get; set; }
}

public class ScanResult
{
    public string FilePath { get; set; }
    public List<SensitiveDataMatch> Matches { get; set; } = new List<SensitiveDataMatch>();
    public bool ContainsSensitiveData => Matches.Count > 0;

    public Dictionary<string, int> GetSummary()
    {
        return Matches.GroupBy(m => m.PatternType)
                      .ToDictionary(g => g.Key, g => g.Count());
    }
}

public class DocumentScanner
{
    private readonly Dictionary<string, string> _patterns;

    public DocumentScanner()
    {
        _patterns = new Dictionary<string, string>
        {
            { "Social Security Number", @"\b\d{3}-\d{2}-\d{4}\b" },
            { "Credit Card", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },
            { "Email Address", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },
            { "Phone Number", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" },
            { "Date of Birth Pattern", @"\b(?:DOB|Date of Birth|Birth Date)[:\s]+\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b" }
        };
    }

    public ScanResult ScanDocument(string filePath)
    {
        ScanResult result = new ScanResult { FilePath = filePath };
        PdfDocument pdf = PdfDocument.FromFile(filePath);

        // Scan each page individually to track location
        for (int pageIndex = 0; pageIndex < pdf.PageCount; pageIndex++)
        {
            string pageText = pdf.ExtractTextFromPage(pageIndex);

            foreach (var pattern in _patterns)
            {
                Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
                MatchCollection matches = regex.Matches(pageText);

                foreach (Match match in matches)
                {
                    result.Matches.Add(new SensitiveDataMatch
                    {
                        PatternType = pattern.Key,
                        Value = MaskValue(match.Value, pattern.Key),
                        PageNumber = pageIndex + 1
                    });
                }
            }
        }

        return result;
    }

    // Partially mask values for safe storage
    private string MaskValue(string value, string patternType)
    {
        if (patternType == "Social Security Number" && value.Length >= 4)
        {
            return "XXX-XX-" + value.Substring(value.Length - 4);
        }
        if (patternType == "Credit Card" && value.Length >= 4)
        {
            return "****-****-****-" + value.Substring(value.Length - 4);
        }
        if (patternType == "Email Address")
        {
            int atIndex = value.IndexOf('@');
            if (atIndex > 2)
            {
                return value.Substring(0, 2) + "***" + value.Substring(atIndex);
            }
        }
        return value.Length > 4 ? value.Substring(0, 2) + "***" : "****";
    }

    public void ScanAndRedact(string inputPath, string outputPath)
    {
        // First scan to identify sensitive data
        ScanResult scanResult = ScanDocument(inputPath);

        if (!scanResult.ContainsSensitiveData)
        {
            return;
        }

        // Load document for redaction
        PdfDocument pdf = PdfDocument.FromFile(inputPath);

        // Extract unique actual values (not masked) for redaction
        string fullText = pdf.ExtractAllText();
        HashSet<string> valuesToRedact = new HashSet<string>();

        foreach (var pattern in _patterns)
        {
            Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
            foreach (Match match in regex.Matches(fullText))
            {
                valuesToRedact.Add(match.Value);
            }
        }

        // Apply redactions
        foreach (string value in valuesToRedact)
        {
            pdf.RedactTextOnAllPages(value);
        }

        pdf.SaveAs(outputPath);
    }
}

// Usage
class Program
{
    static void Main()
    {
        DocumentScanner scanner = new DocumentScanner();

        // Scan only (for audit purposes)
        ScanResult result = scanner.ScanDocument("application-form.pdf");
        var summary = result.GetSummary();

        // Scan and redact in one operation
        scanner.ScanAndRedact("application-form.pdf", "application-form-redacted.pdf");
    }
}
using IronPdf;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;

public class SensitiveDataMatch
{
    public string PatternType { get; set; }
    public string Value { get; set; }
    public int PageNumber { get; set; }
}

public class ScanResult
{
    public string FilePath { get; set; }
    public List<SensitiveDataMatch> Matches { get; set; } = new List<SensitiveDataMatch>();
    public bool ContainsSensitiveData => Matches.Count > 0;

    public Dictionary<string, int> GetSummary()
    {
        return Matches.GroupBy(m => m.PatternType)
                      .ToDictionary(g => g.Key, g => g.Count());
    }
}

public class DocumentScanner
{
    private readonly Dictionary<string, string> _patterns;

    public DocumentScanner()
    {
        _patterns = new Dictionary<string, string>
        {
            { "Social Security Number", @"\b\d{3}-\d{2}-\d{4}\b" },
            { "Credit Card", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },
            { "Email Address", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },
            { "Phone Number", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" },
            { "Date of Birth Pattern", @"\b(?:DOB|Date of Birth|Birth Date)[:\s]+\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b" }
        };
    }

    public ScanResult ScanDocument(string filePath)
    {
        ScanResult result = new ScanResult { FilePath = filePath };
        PdfDocument pdf = PdfDocument.FromFile(filePath);

        // Scan each page individually to track location
        for (int pageIndex = 0; pageIndex < pdf.PageCount; pageIndex++)
        {
            string pageText = pdf.ExtractTextFromPage(pageIndex);

            foreach (var pattern in _patterns)
            {
                Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
                MatchCollection matches = regex.Matches(pageText);

                foreach (Match match in matches)
                {
                    result.Matches.Add(new SensitiveDataMatch
                    {
                        PatternType = pattern.Key,
                        Value = MaskValue(match.Value, pattern.Key),
                        PageNumber = pageIndex + 1
                    });
                }
            }
        }

        return result;
    }

    // Partially mask values for safe storage
    private string MaskValue(string value, string patternType)
    {
        if (patternType == "Social Security Number" && value.Length >= 4)
        {
            return "XXX-XX-" + value.Substring(value.Length - 4);
        }
        if (patternType == "Credit Card" && value.Length >= 4)
        {
            return "****-****-****-" + value.Substring(value.Length - 4);
        }
        if (patternType == "Email Address")
        {
            int atIndex = value.IndexOf('@');
            if (atIndex > 2)
            {
                return value.Substring(0, 2) + "***" + value.Substring(atIndex);
            }
        }
        return value.Length > 4 ? value.Substring(0, 2) + "***" : "****";
    }

    public void ScanAndRedact(string inputPath, string outputPath)
    {
        // First scan to identify sensitive data
        ScanResult scanResult = ScanDocument(inputPath);

        if (!scanResult.ContainsSensitiveData)
        {
            return;
        }

        // Load document for redaction
        PdfDocument pdf = PdfDocument.FromFile(inputPath);

        // Extract unique actual values (not masked) for redaction
        string fullText = pdf.ExtractAllText();
        HashSet<string> valuesToRedact = new HashSet<string>();

        foreach (var pattern in _patterns)
        {
            Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
            foreach (Match match in regex.Matches(fullText))
            {
                valuesToRedact.Add(match.Value);
            }
        }

        // Apply redactions
        foreach (string value in valuesToRedact)
        {
            pdf.RedactTextOnAllPages(value);
        }

        pdf.SaveAs(outputPath);
    }
}

// Usage
class Program
{
    static void Main()
    {
        DocumentScanner scanner = new DocumentScanner();

        // Scan only (for audit purposes)
        ScanResult result = scanner.ScanDocument("application-form.pdf");
        var summary = result.GetSummary();

        // Scan and redact in one operation
        scanner.ScanAndRedact("application-form.pdf", "application-form-redacted.pdf");
    }
}
$vbLabelText   $csharpLabel

このスキャナーは、修正前にどのような機密情報が存在するかを可視化します。 これは、何が発見され、何が削除されたかを示す文書が必要なコンプライアンス・ワークフローをサポートします。 マスキング機能により、ログファイルやレポート自体がデータ漏洩の原因にならないようにします。


PDFの特定の領域や領域を編集するにはどうすればよいですか?

テキストの再編集は文字ベースのコンテンツを効果的に処理しますが、PDFにはテキストマッチングでは対応できない形式の機密情報が含まれていることがよくあります。 署名、写真、手書きの注釈、スタンプ、グラフィック要素には、別のアプローチが必要です。 領域ベースの再編集では、座標で矩形領域を指定し、その境界内のすべてを永久に見えなくすることができます。

IronPDFはRectangleF構造を使って再編集領域を定義します。 左上隅のX座標とY座標を指定し、次に領域の幅と高さを指定します。 座標は、PDF仕様の座標系に一致するように、ページの左下からのポイントで測定されます。

入力

座標ベースのリージョンターゲティングを使用して編集する必要がある、手書きの署名と写真付き ID を含む署名入り契約文書。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-region-basic.cs
using IronPdf;
using IronSoftware.Drawing;

// Load a document with signature blocks and photos
PdfDocument pdf = PdfDocument.FromFile("signed-agreement.pdf");

// Define a region for a signature block
// Located 100 points from left, 650 points from bottom
// Width of 200 points, height of 50 points
RectangleF signatureRegion = new RectangleF(100, 650, 200, 50);

// Redact the signature region on all pages
pdf.RedactRegionsOnAllPages(signatureRegion);

// Define a region for a photo ID in the upper right
RectangleF photoRegion = new RectangleF(450, 700, 100, 120);
pdf.RedactRegionsOnAllPages(photoRegion);

// Save the document with regions redacted
pdf.SaveAs("signed-agreement-redacted.pdf");
$vbLabelText   $csharpLabel

このコードでは、RectangleF構造体を使用して、再編集のための矩形領域を定義しています。 署名領域は座標(100, 650)に配置され、200x50ピクセルの領域があり、写真領域は座標(450, 700)に配置され、100x120ピクセルの領域があります。 RedactRegionsOnAllPagesメソッドは、すべてのページにわたって、これらの領域に黒い四角形を適用します。

サンプル出力

正しい座標を決定するには、実験や測定が必要になることがよくあります。 PDFページは通常、1点が1/72インチに相当する座標系を使用します。 標準的なUSレターページのサイズは、幅612ポイント、高さ792ポイントです。 A4ページで約595×842ポイント。 カーソルを動かすと座標が表示されるPDF表示ツールも役立ちますし、プログラムでページ寸法を抽出することもできます:

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-region-dimensions.cs
using IronPdf;
using IronSoftware.Drawing;

PdfDocument pdf = PdfDocument.FromFile("form-document.pdf");

// Get dimensions of the first page
var pageInfo = pdf.Pages[0];

// Calculate regions relative to page dimensions
// Redact the bottom quarter of the page where signatures appear
float signatureAreaHeight = (float)(pageInfo.Height / 4);
RectangleF bottomQuarter = new RectangleF(
    0,                              // Start at left edge
    0,                              // Start at bottom
    (float)pageInfo.Width,          // Full page width
    signatureAreaHeight             // Quarter of page height
);

pdf.RedactRegionsOnAllPages(bottomQuarter);

// Redact a header area at the top containing letterhead with address
float headerHeight = 100;
RectangleF headerArea = new RectangleF(
    0,
    (float)(pageInfo.Height - headerHeight), // Position from bottom
    (float)pageInfo.Width,
    headerHeight
);

pdf.RedactRegionsOnAllPages(headerArea);

pdf.SaveAs("form-document-redacted.pdf");
$vbLabelText   $csharpLabel

異なるページにまたがる複数のリージョンを再編集するにはどうすればよいですか?

複雑な文書では、ページごとに異なる領域の編集が必要になることがよくあります。 複数ページのフォームには、さまざまな位置に署名欄があったり、ページごとに写真やスタンプ、その他のグラフィック要素が異なる位置にあったりします。 IronPdfはターゲット領域の再編集のためのページ固有のメソッドを含んでいます。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/redact-multiple-regions.cs
using IronPdf;
using IronSoftware.Drawing;

PdfDocument pdf = PdfDocument.FromFile("multi-page-application.pdf");

// Define page-specific redaction regions
// Page 1: Cover page with applicant photo
RectangleF page1Photo = new RectangleF(450, 600, 120, 150);
pdf.RedactRegionOnPage(0, page1Photo);

// Page 2: Personal information section
RectangleF page2InfoBlock = new RectangleF(50, 400, 250, 200);
pdf.RedactRegionOnPage(1, page2InfoBlock);

// Pages 3-5: Signature lines at the same position
RectangleF signatureLine = new RectangleF(100, 100, 200, 40);
int[] signaturePages = { 2, 3, 4 };
pdf.RedactRegionOnPages(signaturePages, signatureLine);

// Page 6: Multiple regions - notary stamp and witness signature
RectangleF notaryStamp = new RectangleF(400, 150, 150, 150);
RectangleF witnessSignature = new RectangleF(100, 150, 200, 40);
pdf.RedactRegionOnPage(5, notaryStamp);
pdf.RedactRegionOnPage(5, witnessSignature);

pdf.SaveAs("multi-page-application-redacted.pdf");
$vbLabelText   $csharpLabel

一貫したレイアウトを持つドキュメントは、再利用可能な領域定義によって恩恵を受けます:

using IronPdf;
using IronSoftware.Drawing;

public class FormRegions
{
    // Standard form regions based on common templates
    public static RectangleF HeaderLogo => new RectangleF(20, 720, 150, 60);
    public static RectangleF SignatureBlock => new RectangleF(72, 72, 200, 50);
    public static RectangleF DateField => new RectangleF(400, 72, 120, 20);
    public static RectangleF PhotoId => new RectangleF(480, 650, 100, 130);
    public static RectangleF AddressBlock => new RectangleF(72, 600, 250, 80);
}

class Program
{
    static void Main()
    {
        PdfDocument pdf = PdfDocument.FromFile("standard-form.pdf");

        // Apply standard redactions using predefined regions
        pdf.RedactRegionsOnAllPages(FormRegions.SignatureBlock);
        pdf.RedactRegionsOnAllPages(FormRegions.DateField);
        pdf.RedactRegionOnPage(0, FormRegions.PhotoId);

        pdf.SaveAs("standard-form-redacted.pdf");
    }
}
using IronPdf;
using IronSoftware.Drawing;

public class FormRegions
{
    // Standard form regions based on common templates
    public static RectangleF HeaderLogo => new RectangleF(20, 720, 150, 60);
    public static RectangleF SignatureBlock => new RectangleF(72, 72, 200, 50);
    public static RectangleF DateField => new RectangleF(400, 72, 120, 20);
    public static RectangleF PhotoId => new RectangleF(480, 650, 100, 130);
    public static RectangleF AddressBlock => new RectangleF(72, 600, 250, 80);
}

class Program
{
    static void Main()
    {
        PdfDocument pdf = PdfDocument.FromFile("standard-form.pdf");

        // Apply standard redactions using predefined regions
        pdf.RedactRegionsOnAllPages(FormRegions.SignatureBlock);
        pdf.RedactRegionsOnAllPages(FormRegions.DateField);
        pdf.RedactRegionOnPage(0, FormRegions.PhotoId);

        pdf.SaveAs("standard-form-redacted.pdf");
    }
}
$vbLabelText   $csharpLabel

機密情報を暴露する可能性のあるメタデータを削除するにはどうすればよいですか?

PDFのメタデータは、見過ごされがちな情報漏えいの原因です。 すべてのPDFには、作成者の名前とユーザー名、文書の作成に使用したソフトウェア、作成と変更のタイムスタンプ、元のファイル名、改訂履歴、さまざまなアプリケーションによって追加されたカスタムプロパティなど、機密情報を明らかにする可能性のあるプロパティが含まれています。 ドキュメントを外部と共有する前に、このメタデータを除去またはサニタイズすることが不可欠です。 メタデータ操作の包括的な概要については、メタデータ操作ガイドをご覧ください。

IronPDFはMetaDataプロパティを通してドキュメントのメタデータを公開します。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/view-metadata.cs
using IronPdf;
using System;

// Load a document containing sensitive metadata
PdfDocument pdf = PdfDocument.FromFile("internal-report.pdf");

// Access current metadata properties
string author = pdf.MetaData.Author;
string title = pdf.MetaData.Title;
string subject = pdf.MetaData.Subject;
string keywords = pdf.MetaData.Keywords;
string creator = pdf.MetaData.Creator;
string producer = pdf.MetaData.Producer;
DateTime? creationDate = pdf.MetaData.CreationDate;
DateTime? modifiedDate = pdf.MetaData.ModifiedDate;

// Get all metadata keys including custom properties
var allKeys = pdf.MetaData.Keys();
$vbLabelText   $csharpLabel

配布前に機密性の高いメタデータを削除すること:

入力

作成者名、作成タイムスタンプ、カスタムプロパティなどのメタデータが埋め込まれた社内メモ。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/remove-metadata.cs
using IronPdf;
using System;

PdfDocument pdf = PdfDocument.FromFile("confidential-memo.pdf");

// Replace identifying metadata with generic values
pdf.MetaData.Author = "Organization Name";
pdf.MetaData.Creator = "Document System";
pdf.MetaData.Producer = "";
pdf.MetaData.Title = "Public Document";
pdf.MetaData.Subject = "";
pdf.MetaData.Keywords = "";

// Normalize dates to remove timing information
pdf.MetaData.CreationDate = DateTime.Now;
pdf.MetaData.ModifiedDate = DateTime.Now;

// Remove specific custom metadata keys
pdf.MetaData.RemoveMetaDataKey("OriginalFilename");
pdf.MetaData.RemoveMetaDataKey("LastSavedBy");
pdf.MetaData.RemoveMetaDataKey("Company");
pdf.MetaData.RemoveMetaDataKey("Manager");

// Remove custom properties added by applications
try
{
    pdf.MetaData.CustomProperties.Remove("SourcePath");
}
catch { }

pdf.SaveAs("confidential-memo-cleaned.pdf");
$vbLabelText   $csharpLabel

このコードは、識別メタデータフィールドを一般的な値に置き換え、タイムスタンプを現在の日付に正規化し、アプリケーションが追加した可能性のあるカスタムメタデータキーを削除します。 RemoveMetaDataKeyメソッドは、内部情報を公開する可能性のある"OriginalFilename"や"LastSavedBy"のような特定のプロパティを対象としています。

サンプル出力

バッチ処理全体の徹底したメタデータのクリーニングには、体系的なアプローチが必要です:

using IronPdf;
using System;
using System.Collections.Generic;

public class MetadataCleaner
{
    private readonly string _defaultAuthor;
    private readonly string _defaultCreator;

    public MetadataCleaner(string organizationName)
    {
        _defaultAuthor = organizationName;
        _defaultCreator = $"{organizationName} Document System";
    }

    public void CleanMetadata(PdfDocument pdf)
    {
        // Replace standard metadata fields
        pdf.MetaData.Author = _defaultAuthor;
        pdf.MetaData.Creator = _defaultCreator;
        pdf.MetaData.Producer = "";
        pdf.MetaData.Subject = "";
        pdf.MetaData.Keywords = "";

        // Normalize timestamps
        DateTime now = DateTime.Now;
        pdf.MetaData.CreationDate = now;
        pdf.MetaData.ModifiedDate = now;

        // Get all keys and remove potentially sensitive ones
        List<string> keysToRemove = new List<string>();
        foreach (string key in pdf.MetaData.Keys())
        {
            // Keep only essential keys
            if (!IsEssentialKey(key))
            {
                keysToRemove.Add(key);
            }
        }

        foreach (string key in keysToRemove)
        {
            pdf.MetaData.RemoveMetaDataKey(key);
        }
    }

    private bool IsEssentialKey(string key)
    {
        // Keep only the basic display properties
        string[] essentialKeys = { "Title", "Author", "CreationDate", "ModifiedDate" };
        foreach (string essential in essentialKeys)
        {
            if (key.Equals(essential, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }
        return false;
    }
}

// Usage
class Program
{
    static void Main()
    {
        MetadataCleaner cleaner = new MetadataCleaner("Acme Corporation");

        PdfDocument pdf = PdfDocument.FromFile("report.pdf");
        cleaner.CleanMetadata(pdf);
        pdf.SaveAs("report-clean.pdf");
    }
}
using IronPdf;
using System;
using System.Collections.Generic;

public class MetadataCleaner
{
    private readonly string _defaultAuthor;
    private readonly string _defaultCreator;

    public MetadataCleaner(string organizationName)
    {
        _defaultAuthor = organizationName;
        _defaultCreator = $"{organizationName} Document System";
    }

    public void CleanMetadata(PdfDocument pdf)
    {
        // Replace standard metadata fields
        pdf.MetaData.Author = _defaultAuthor;
        pdf.MetaData.Creator = _defaultCreator;
        pdf.MetaData.Producer = "";
        pdf.MetaData.Subject = "";
        pdf.MetaData.Keywords = "";

        // Normalize timestamps
        DateTime now = DateTime.Now;
        pdf.MetaData.CreationDate = now;
        pdf.MetaData.ModifiedDate = now;

        // Get all keys and remove potentially sensitive ones
        List<string> keysToRemove = new List<string>();
        foreach (string key in pdf.MetaData.Keys())
        {
            // Keep only essential keys
            if (!IsEssentialKey(key))
            {
                keysToRemove.Add(key);
            }
        }

        foreach (string key in keysToRemove)
        {
            pdf.MetaData.RemoveMetaDataKey(key);
        }
    }

    private bool IsEssentialKey(string key)
    {
        // Keep only the basic display properties
        string[] essentialKeys = { "Title", "Author", "CreationDate", "ModifiedDate" };
        foreach (string essential in essentialKeys)
        {
            if (key.Equals(essential, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }
        return false;
    }
}

// Usage
class Program
{
    static void Main()
    {
        MetadataCleaner cleaner = new MetadataCleaner("Acme Corporation");

        PdfDocument pdf = PdfDocument.FromFile("report.pdf");
        cleaner.CleanMetadata(pdf);
        pdf.SaveAs("report-clean.pdf");
    }
}
$vbLabelText   $csharpLabel

埋め込みスクリプトや隠れた脅威を削除するためにPDFをサニタイズするにはどうすればよいですか?

PDFのサニタイズは、目に見えるコンテンツやメタデータだけでなく、セキュリティ上の懸念にも対応します。 PDFファイルには、JavaScriptコード、埋め込み実行可能ファイル、外部接続をトリガーするフォームアクション、その他の潜在的に悪意のある要素が含まれている可能性があります。 これらの機能は、インタラクティブなフォームやマルチメディアコンテンツのような正当な目的のために存在しますが、攻撃のベクトルも生み出します。 PDFをサニタイズすることで、視覚的なコンテンツを維持しながら、これらのアクティブな要素を削除することができます。 サニタイズ方法の詳細については、サニタイズPDFハウツーガイドを参照してください。

IronPDFのCleanerクラスは、PDFを画像フォーマットに変換し、それを元に戻すというエレガントなアプローチでサニタイズを処理します。 このプロセスでは、JavaScript、埋め込みオブジェクト、フォームアクション、注釈を取り除き、見た目の美しさはそのままにします。 このライブラリは、特性の異なる2つのサニタイズ方法を提供します。

入力

JavaScript、埋め込みオブジェクト、その他の潜在的に悪意のあるアクティブコンテンツを含む可能性のある、外部ソースから受信したPDF文書。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/sanitize-pdf.cs
using IronPdf;

// Load a PDF that may contain active content
PdfDocument pdf = PdfDocument.FromFile("received-document.pdf");

// Sanitize using SVG conversion
// Faster processing, results in searchable text, slight layout variations possible
PdfDocument sanitizedSvg = Cleaner.SanitizeWithSvg(pdf);
sanitizedSvg.SaveAs("sanitized-svg.pdf");

// Sanitize using Bitmap conversion
// Slower processing, text becomes image (not searchable), exact visual reproduction
PdfDocument sanitizedBitmap = Cleaner.SanitizeWithBitmap(pdf);
sanitizedBitmap.SaveAs("sanitized-bitmap.pdf");
$vbLabelText   $csharpLabel

このコードはIronPDFのCleanerクラスによって提供される2つのサニタイズ方法を示しています。 SanitizeWithSvgは、PDFをSVG中間フォーマットを通して変換し、アクティブなコンテンツを削除しながら検索可能なテキストを保持します。 SanitizeWithBitmapは、最初にページを画像に変換し、正確な視覚的コピーを作成しますが、テキストは検索不可能なグラフィックとしてレンダリングされます。

サンプル出力

SVGメソッドは、より速く動作し、検索可能なコンテンツとしてテキストを保持するため、インデックスやアクセス可能な状態を維持する必要があるドキュメントに適しています。 ビットマップ方式は、正確な視覚的コピーを作成しますが、テキストを画像に変換するため、テキストの選択や検索ができません。 出力文書の要件に基づいて選択してください。

サニタイズ時にレンダリングオプションを適用して、出力を調整することもできます:

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/sanitize-with-options.cs
using IronPdf;

// Load the potentially unsafe document
PdfDocument pdf = PdfDocument.FromFile("untrusted-source.pdf");

// Configure rendering options for sanitization
var renderOptions = new ChromePdfRenderOptions
{
    MarginTop = 10,
    MarginBottom = 10,
    MarginLeft = 10,
    MarginRight = 10
};

// Sanitize with custom options
PdfDocument sanitized = Cleaner.SanitizeWithSvg(pdf, renderOptions);
sanitized.SaveAs("untrusted-source-safe.pdf");
$vbLabelText   $csharpLabel

高度なセキュリティ環境では、サニタイズとその他の保護手段を組み合わせる必要があります:

using IronPdf;
using System;

public class SecureDocumentProcessor
{
    public PdfDocument ProcessUntrustedDocument(string inputPath)
    {
        // Load the document
        PdfDocument original = PdfDocument.FromFile(inputPath);

        // Step 1: Sanitize to remove active content
        PdfDocument sanitized = Cleaner.SanitizeWithSvg(original);

        // Step 2: Clean metadata
        sanitized.MetaData.Author = "Processed Document";
        sanitized.MetaData.Creator = "Secure Processor";
        sanitized.MetaData.Producer = "";
        sanitized.MetaData.CreationDate = DateTime.Now;
        sanitized.MetaData.ModifiedDate = DateTime.Now;

        // Remove all custom metadata
        foreach (string key in sanitized.MetaData.Keys())
        {
            if (key != "Title" && key != "Author" && key != "CreationDate" && key != "ModifiedDate")
            {
                sanitized.MetaData.RemoveMetaDataKey(key);
            }
        }

        return sanitized;
    }
}

// Usage
class Program
{
    static void Main()
    {
        SecureDocumentProcessor processor = new SecureDocumentProcessor();
        PdfDocument safe = processor.ProcessUntrustedDocument("email-attachment.pdf");
        safe.SaveAs("email-attachment-safe.pdf");
    }
}
using IronPdf;
using System;

public class SecureDocumentProcessor
{
    public PdfDocument ProcessUntrustedDocument(string inputPath)
    {
        // Load the document
        PdfDocument original = PdfDocument.FromFile(inputPath);

        // Step 1: Sanitize to remove active content
        PdfDocument sanitized = Cleaner.SanitizeWithSvg(original);

        // Step 2: Clean metadata
        sanitized.MetaData.Author = "Processed Document";
        sanitized.MetaData.Creator = "Secure Processor";
        sanitized.MetaData.Producer = "";
        sanitized.MetaData.CreationDate = DateTime.Now;
        sanitized.MetaData.ModifiedDate = DateTime.Now;

        // Remove all custom metadata
        foreach (string key in sanitized.MetaData.Keys())
        {
            if (key != "Title" && key != "Author" && key != "CreationDate" && key != "ModifiedDate")
            {
                sanitized.MetaData.RemoveMetaDataKey(key);
            }
        }

        return sanitized;
    }
}

// Usage
class Program
{
    static void Main()
    {
        SecureDocumentProcessor processor = new SecureDocumentProcessor();
        PdfDocument safe = processor.ProcessUntrustedDocument("email-attachment.pdf");
        safe.SaveAs("email-attachment-safe.pdf");
    }
}
$vbLabelText   $csharpLabel

セキュリティ脆弱性のためにPDFをスキャンするにはどうすればよいですか?

文書を処理またはサニタイズする前に、その文書にどのような潜在的脅威が含まれているかを評価するとよいでしょう。 IronPdfのCleaner.ScanPdfメソッドは、マルウェア分析や脅威検出でよく使われるパターン定義であるYARAルールを使ってドキュメントを検査します。 スキャンは、悪意のあるPDFファイルに関連する特性を識別します。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/scan-vulnerabilities.cs
using IronPdf;

// Load the document to scan
PdfDocument pdf = PdfDocument.FromFile("suspicious-document.pdf");

// Scan using default YARA rules
CleanerScanResult scanResult = Cleaner.ScanPdf(pdf);

// Check the scan results
bool threatsDetected = scanResult.IsDetected;
int riskCount = scanResult.Risks.Count;

// Process identified risks
if (scanResult.IsDetected)
{
    foreach (var risk in scanResult.Risks)
    {
        // Handle each identified risk
    }

    // Sanitize the document before use
    PdfDocument sanitized = Cleaner.SanitizeWithSvg(pdf);
    sanitized.SaveAs("suspicious-document-safe.pdf");
}
$vbLabelText   $csharpLabel

特殊な検出要件のために、カスタムYARAルールファイルを提供することができます。 特定の脅威モデルやコンプライアンスニーズを持つ組織は、特定の脆弱性パターンをターゲットとした独自のルールセットを維持することがよくあります。

:path=/static-assets/pdf/content-code-examples/tutorials/pdf-redaction-csharp/scan-custom-yara.cs
using IronPdf;

PdfDocument pdf = PdfDocument.FromFile("incoming-document.pdf");

// Scan with custom YARA rules
string[] customYaraFiles = { "corporate-rules.yar", "industry-specific.yar" };
CleanerScanResult result = Cleaner.ScanPdf(pdf, customYaraFiles);

if (result.IsDetected)
{
    // Document triggered custom rules and requires review or sanitization
    PdfDocument sanitized = Cleaner.SanitizeWithSvg(pdf);
    sanitized.SaveAs("incoming-document-safe.pdf");
}
$vbLabelText   $csharpLabel

スキャンを文書取り込みのワークフローに統合することで、セキュリティの決定を自動化することができます:

using IronPdf;
using System;
using System.IO;

public enum DocumentSafetyLevel
{
    Safe,
    Suspicious,
    Dangerous
}

public class DocumentSecurityGateway
{
    public DocumentSafetyLevel EvaluateDocument(string filePath)
    {
        PdfDocument pdf = PdfDocument.FromFile(filePath);
        CleanerScanResult scan = Cleaner.ScanPdf(pdf);

        if (!scan.IsDetected)
        {
            return DocumentSafetyLevel.Safe;
        }

        // Evaluate severity based on number of risks
        if (scan.Risks.Count > 5)
        {
            return DocumentSafetyLevel.Dangerous;
        }

        return DocumentSafetyLevel.Suspicious;
    }

    public PdfDocument ProcessIncomingDocument(string filePath, string outputDirectory)
    {
        DocumentSafetyLevel safety = EvaluateDocument(filePath);
        string fileName = Path.GetFileName(filePath);

        switch (safety)
        {
            case DocumentSafetyLevel.Safe:
                return PdfDocument.FromFile(filePath);

            case DocumentSafetyLevel.Suspicious:
                PdfDocument suspicious = PdfDocument.FromFile(filePath);
                return Cleaner.SanitizeWithSvg(suspicious);

            case DocumentSafetyLevel.Dangerous:
                throw new SecurityException($"Document {fileName} contains dangerous content");

            default:
                throw new InvalidOperationException("Unknown safety level");
        }
    }
}
using IronPdf;
using System;
using System.IO;

public enum DocumentSafetyLevel
{
    Safe,
    Suspicious,
    Dangerous
}

public class DocumentSecurityGateway
{
    public DocumentSafetyLevel EvaluateDocument(string filePath)
    {
        PdfDocument pdf = PdfDocument.FromFile(filePath);
        CleanerScanResult scan = Cleaner.ScanPdf(pdf);

        if (!scan.IsDetected)
        {
            return DocumentSafetyLevel.Safe;
        }

        // Evaluate severity based on number of risks
        if (scan.Risks.Count > 5)
        {
            return DocumentSafetyLevel.Dangerous;
        }

        return DocumentSafetyLevel.Suspicious;
    }

    public PdfDocument ProcessIncomingDocument(string filePath, string outputDirectory)
    {
        DocumentSafetyLevel safety = EvaluateDocument(filePath);
        string fileName = Path.GetFileName(filePath);

        switch (safety)
        {
            case DocumentSafetyLevel.Safe:
                return PdfDocument.FromFile(filePath);

            case DocumentSafetyLevel.Suspicious:
                PdfDocument suspicious = PdfDocument.FromFile(filePath);
                return Cleaner.SanitizeWithSvg(suspicious);

            case DocumentSafetyLevel.Dangerous:
                throw new SecurityException($"Document {fileName} contains dangerous content");

            default:
                throw new InvalidOperationException("Unknown safety level");
        }
    }
}
$vbLabelText   $csharpLabel

完全な再編集およびサニタイズ パイプラインを構築するにはどうすればよいですか?

プロダクションドキュメントの処理では、通常、複数の保護技術を統合ワークフローに組み合わせる必要があります。 完全なパイプラインは、脅威のために受信文書をスキャンし、最初のスクリーニングを通過したものをサニタイズし、テキストと領域の再編集を適用し、メタデータを除去し、実行されたすべてのアクションを文書化した監査ログを生成する可能性があります。 この例は、そのような統合的なアプローチを示しています。

using IronPdf;
using IronSoftware.Drawing;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

public class DocumentProcessingResult
{
    public string OriginalFile { get; set; }
    public string OutputFile { get; set; }
    public bool WasSanitized { get; set; }
    public int TextRedactionsApplied { get; set; }
    public int RegionRedactionsApplied { get; set; }
    public bool MetadataCleaned { get; set; }
    public List<string> SensitiveDataTypesFound { get; set; } = new List<string>();
    public DateTime ProcessedAt { get; set; }
    public bool Success { get; set; }
    public string ErrorMessage { get; set; }
}

public class ComprehensiveDocumentProcessor
{
    // Sensitive data patterns
    private readonly Dictionary<string, string> _sensitivePatterns = new Dictionary<string, string>
    {
        { "SSN", @"\b\d{3}-\d{2}-\d{4}\b" },
        { "Credit Card", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },
        { "Email", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },
        { "Phone", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" }
    };

    // Standard regions to redact (signature areas, photo locations)
    private readonly List<RectangleF> _standardRedactionRegions = new List<RectangleF>
    {
        new RectangleF(72, 72, 200, 50),    // Bottom left signature
        new RectangleF(350, 72, 200, 50)    // Bottom right signature
    };

    private readonly string _organizationName;

    public ComprehensiveDocumentProcessor(string organizationName)
    {
        _organizationName = organizationName;
    }

    public DocumentProcessingResult ProcessDocument(
        string inputPath,
        string outputPath,
        bool sanitize = true,
        bool redactPatterns = true,
        bool redactRegions = true,
        bool cleanMetadata = true,
        List<string> additionalTermsToRedact = null)
    {
        var result = new DocumentProcessingResult
        {
            OriginalFile = inputPath,
            OutputFile = outputPath,
            ProcessedAt = DateTime.Now
        };

        try
        {
            // Load the document
            PdfDocument pdf = PdfDocument.FromFile(inputPath);

            // Step 1: Security scan
            CleanerScanResult scanResult = Cleaner.ScanPdf(pdf);

            if (scanResult.IsDetected && scanResult.Risks.Count > 10)
            {
                throw new SecurityException("Document contains too many security risks to process");
            }

            // Step 2: Sanitization (if needed or requested)
            if (sanitize || scanResult.IsDetected)
            {
                pdf = Cleaner.SanitizeWithSvg(pdf);
                result.WasSanitized = true;
            }

            // Step 3: Pattern-based text redaction
            if (redactPatterns)
            {
                string fullText = pdf.ExtractAllText();
                HashSet<string> valuesToRedact = new HashSet<string>();

                foreach (var pattern in _sensitivePatterns)
                {
                    Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
                    MatchCollection matches = regex.Matches(fullText);

                    if (matches.Count > 0)
                    {
                        result.SensitiveDataTypesFound.Add($"{pattern.Key} ({matches.Count})");
                        foreach (Match match in matches)
                        {
                            valuesToRedact.Add(match.Value);
                        }
                    }
                }

                // Apply redactions
                foreach (string value in valuesToRedact)
                {
                    pdf.RedactTextOnAllPages(value);
                    result.TextRedactionsApplied++;
                }
            }

            // Step 4: Additional specific terms
            if (additionalTermsToRedact != null)
            {
                foreach (string term in additionalTermsToRedact)
                {
                    pdf.RedactTextOnAllPages(term);
                    result.TextRedactionsApplied++;
                }
            }

            // Step 5: Region-based redaction
            if (redactRegions)
            {
                foreach (RectangleF region in _standardRedactionRegions)
                {
                    pdf.RedactRegionsOnAllPages(region);
                    result.RegionRedactionsApplied++;
                }
            }

            // Step 6: Metadata cleaning
            if (cleanMetadata)
            {
                pdf.MetaData.Author = _organizationName;
                pdf.MetaData.Creator = $"{_organizationName} Document Processor";
                pdf.MetaData.Producer = "";
                pdf.MetaData.Subject = "";
                pdf.MetaData.Keywords = "";
                pdf.MetaData.CreationDate = DateTime.Now;
                pdf.MetaData.ModifiedDate = DateTime.Now;
                result.MetadataCleaned = true;
            }

            // Step 7: Save the processed document
            pdf.SaveAs(outputPath);
            result.Success = true;
        }
        catch (Exception ex)
        {
            result.Success = false;
            result.ErrorMessage = ex.Message;
        }

        return result;
    }
}

// Usage example
class Program
{
    static void Main()
    {
        var processor = new ComprehensiveDocumentProcessor("Acme Corporation");

        // Process a single document with all protections
        var result = processor.ProcessDocument(
            inputPath: "customer-application.pdf",
            outputPath: "customer-application-redacted.pdf",
            sanitize: true,
            redactPatterns: true,
            redactRegions: true,
            cleanMetadata: true,
            additionalTermsToRedact: new List<string> { "Project Alpha", "Internal Use Only" }
        );

        // Batch process multiple documents
        string[] inputFiles = Directory.GetFiles("incoming", "*.pdf");
        foreach (string file in inputFiles)
        {
            string outputFile = Path.Combine("processed", Path.GetFileName(file));
            processor.ProcessDocument(file, outputFile);
        }
    }
}
using IronPdf;
using IronSoftware.Drawing;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

public class DocumentProcessingResult
{
    public string OriginalFile { get; set; }
    public string OutputFile { get; set; }
    public bool WasSanitized { get; set; }
    public int TextRedactionsApplied { get; set; }
    public int RegionRedactionsApplied { get; set; }
    public bool MetadataCleaned { get; set; }
    public List<string> SensitiveDataTypesFound { get; set; } = new List<string>();
    public DateTime ProcessedAt { get; set; }
    public bool Success { get; set; }
    public string ErrorMessage { get; set; }
}

public class ComprehensiveDocumentProcessor
{
    // Sensitive data patterns
    private readonly Dictionary<string, string> _sensitivePatterns = new Dictionary<string, string>
    {
        { "SSN", @"\b\d{3}-\d{2}-\d{4}\b" },
        { "Credit Card", @"\b(?:\d{4}[-\s]?){3}\d{1,4}\b" },
        { "Email", @"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b" },
        { "Phone", @"\b(?:\(\d{3}\)\s?|\d{3}[-.])\d{3}[-.]?\d{4}\b" }
    };

    // Standard regions to redact (signature areas, photo locations)
    private readonly List<RectangleF> _standardRedactionRegions = new List<RectangleF>
    {
        new RectangleF(72, 72, 200, 50),    // Bottom left signature
        new RectangleF(350, 72, 200, 50)    // Bottom right signature
    };

    private readonly string _organizationName;

    public ComprehensiveDocumentProcessor(string organizationName)
    {
        _organizationName = organizationName;
    }

    public DocumentProcessingResult ProcessDocument(
        string inputPath,
        string outputPath,
        bool sanitize = true,
        bool redactPatterns = true,
        bool redactRegions = true,
        bool cleanMetadata = true,
        List<string> additionalTermsToRedact = null)
    {
        var result = new DocumentProcessingResult
        {
            OriginalFile = inputPath,
            OutputFile = outputPath,
            ProcessedAt = DateTime.Now
        };

        try
        {
            // Load the document
            PdfDocument pdf = PdfDocument.FromFile(inputPath);

            // Step 1: Security scan
            CleanerScanResult scanResult = Cleaner.ScanPdf(pdf);

            if (scanResult.IsDetected && scanResult.Risks.Count > 10)
            {
                throw new SecurityException("Document contains too many security risks to process");
            }

            // Step 2: Sanitization (if needed or requested)
            if (sanitize || scanResult.IsDetected)
            {
                pdf = Cleaner.SanitizeWithSvg(pdf);
                result.WasSanitized = true;
            }

            // Step 3: Pattern-based text redaction
            if (redactPatterns)
            {
                string fullText = pdf.ExtractAllText();
                HashSet<string> valuesToRedact = new HashSet<string>();

                foreach (var pattern in _sensitivePatterns)
                {
                    Regex regex = new Regex(pattern.Value, RegexOptions.IgnoreCase);
                    MatchCollection matches = regex.Matches(fullText);

                    if (matches.Count > 0)
                    {
                        result.SensitiveDataTypesFound.Add($"{pattern.Key} ({matches.Count})");
                        foreach (Match match in matches)
                        {
                            valuesToRedact.Add(match.Value);
                        }
                    }
                }

                // Apply redactions
                foreach (string value in valuesToRedact)
                {
                    pdf.RedactTextOnAllPages(value);
                    result.TextRedactionsApplied++;
                }
            }

            // Step 4: Additional specific terms
            if (additionalTermsToRedact != null)
            {
                foreach (string term in additionalTermsToRedact)
                {
                    pdf.RedactTextOnAllPages(term);
                    result.TextRedactionsApplied++;
                }
            }

            // Step 5: Region-based redaction
            if (redactRegions)
            {
                foreach (RectangleF region in _standardRedactionRegions)
                {
                    pdf.RedactRegionsOnAllPages(region);
                    result.RegionRedactionsApplied++;
                }
            }

            // Step 6: Metadata cleaning
            if (cleanMetadata)
            {
                pdf.MetaData.Author = _organizationName;
                pdf.MetaData.Creator = $"{_organizationName} Document Processor";
                pdf.MetaData.Producer = "";
                pdf.MetaData.Subject = "";
                pdf.MetaData.Keywords = "";
                pdf.MetaData.CreationDate = DateTime.Now;
                pdf.MetaData.ModifiedDate = DateTime.Now;
                result.MetadataCleaned = true;
            }

            // Step 7: Save the processed document
            pdf.SaveAs(outputPath);
            result.Success = true;
        }
        catch (Exception ex)
        {
            result.Success = false;
            result.ErrorMessage = ex.Message;
        }

        return result;
    }
}

// Usage example
class Program
{
    static void Main()
    {
        var processor = new ComprehensiveDocumentProcessor("Acme Corporation");

        // Process a single document with all protections
        var result = processor.ProcessDocument(
            inputPath: "customer-application.pdf",
            outputPath: "customer-application-redacted.pdf",
            sanitize: true,
            redactPatterns: true,
            redactRegions: true,
            cleanMetadata: true,
            additionalTermsToRedact: new List<string> { "Project Alpha", "Internal Use Only" }
        );

        // Batch process multiple documents
        string[] inputFiles = Directory.GetFiles("incoming", "*.pdf");
        foreach (string file in inputFiles)
        {
            string outputFile = Path.Combine("processed", Path.GetFileName(file));
            processor.ProcessDocument(file, outputFile);
        }
    }
}
$vbLabelText   $csharpLabel

入力

SSN、クレジットカード番号、電子メールアドレス、署名ブロックなど、包括的な保護が必要な複数の機密データを含む顧客申込書。

サンプル出力

この包括的なプロセッサは、このガイドでカバーされているすべてのテクニックを1つの設定可能なクラスに統合しています。 このツールは、脅威をスキャンし、必要に応じてサニタイズし、センシティブなパターンを見つけて再編集し、リージョンの再編集を適用し、メタデータをクリーンアップし、詳細なレポートを作成します。 特定の要件に合わせて、感度パターン、再編集領域、処理オプションを調整できます。


次のステップ

PDF文書内の機密情報を保護するには、表面的な対策だけでは不十分です。 真の再編集では、文書構造からコンテンツを永久に削除します。 パターン マッチングは、社会保障番号、クレジットカード情報、電子メールアドレスなどのデータの検出と削除を自動化します。 リージョンベースの再編集は、テキストマッチングでは対応できない署名、写真、その他のグラフィック要素に対応します。 メタデータのクリーニングは、作者、タイムスタンプ、内部ファイルパスを明らかにする可能性のある隠された情報を排除します。 サニタイズは、セキュリティリスクをもたらす埋め込みスクリプトやアクティブコンテンツを除去します。

IronPDFは、C#や.NETの開発プラクティスと自然に統合する一貫性のある優れた設計のAPIを通じて、これらの機能をすべて提供します。 このガイドで示す方法は、単一のドキュメントを扱うことも、バッチ処理数千のファイルに拡張することもできます。 医療データのためのコンプライアンス・ワークフローを構築する場合でも、証拠開示のための法的文書を準備する場合でも、単に内部レポートを安全に外部と共有できるようにする場合でも、これらのテクニックは責任ある文書処理の基礎を形成します。 包括的なセキュリティをカバーするには、再編集とパスワード保護と許可デジタル署名を組み合わせてください。

構築開始の準備はできましたか? IronPdfをダウンロードして無料トライアルでお試しください。 本ライブラリには無料の開発ライセンスが含まれているため、本番用ライセンスに移行する前に、再編集、テキスト抽出、およびサニタイズ機能を十分に評価することができます。 実装やコンプライアンスのワークフローについて質問がある場合は、当社のエンジニアリング・サポート・チームにお問い合わせください。

よくある質問

PDFの再編集とは何ですか?

PDFの再編集は、PDF文書から機密情報を永久に削除するプロセスです。これには、プライバシーやコンプライアンス上の理由から隠す必要のあるテキスト、画像、メタデータが含まれます。

C# を使用して PDF 内の情報を再編集するにはどうすればよいですか?

IronPDFを使ってC#でPDFの情報を再編集することができます。PDF文書内のテキスト、画像、メタデータを永久的に削除または非表示にすることができ、プライバシーやコンプライアンス基準を満たすことができます。

なぜ PDF の再編集がコンプライアンス上重要なのですか?

PDFの再編集は、HIPAA、GDPR、PCI DSSなどの標準に準拠する上で非常に重要です。

IronPdfはPDFの全領域を再編集できますか?

はい、IronPdfはPDFの領域全体を再編集することができます。これにより、セキュリティのために非表示または削除する必要のある特定の領域を文書内で定義することができます。

IronPdfを使ってどのような種類のデータを編集できますか?

IronPdfはPDFドキュメントからテキスト、画像、メタデータを含む様々なタイプのデータを再編集し、包括的なデータプライバシーとセキュリティを確保します。

IronPDFは文書のサニタイズに対応していますか?

IronPDFは文書のサニタイズに対応しています。これはPDFをクリーンアップし、見えないデータやメタデータを削除することです。

IronPDFでPDFの再編集を自動化することは可能ですか?

はい、IronPDFはC#でPDFの再編集処理を自動化し、機密データの除去が必要な大量の文書の処理を容易にします。

IronPdfはどのようにして再編集の永続性を確保しているのですか?

IronPDFは、選択されたテキストや画像を単に見えなくするのではなく、ドキュメントから永久的に削除することで、再編集の永続性を保証します。

IronPdfはPDFのメタデータを編集できますか?

IronPdfはPDFドキュメントのメタデータを再編集し、隠されたデータやバックグラウンドのデータを含むあらゆる機密データを徹底的に削除します。

PDFの再編集にIronPDFを使うメリットは何ですか?

PDFの再編集にIronPdfを使用することで、データ保護規制への準拠、ドキュメントのセキュリティ強化、機密情報の効率的な自動管理プロセスなどの利点が得られます。

カーティス・チャウ
テクニカルライター

Curtis Chauは、カールトン大学でコンピュータサイエンスの学士号を取得し、Node.js、TypeScript、JavaScript、およびReactに精通したフロントエンド開発を専門としています。直感的で美しいユーザーインターフェースを作成することに情熱を持ち、Curtisは現代のフレームワークを用いた開発や、構造の良い視覚的に魅力的なマニュアルの作成を楽しんでいます。

開発以外にも、CurtisはIoT(Internet of Things)への強い関心を持ち、ハードウェアとソフトウェアの統合方法を模索しています。余暇には、ゲームをしたりDiscordボットを作成したりして、技術に対する愛情と創造性を組み合わせています。

準備はできましたか?
Nuget ダウンロード 17,527,568 | バージョン: 2026.2 リリース