比較

wkhtmltopdf與IronPDF:技術比較指南

當.NET開發者需要將HTML轉換為PDF時,因為其開源性質和簡單的命令行,wkhtmltopdf歷來是一個受歡迎的選擇。 然而,由於專案的棄置和嚴重的安全漏洞,許多團隊開始評估現代替代方案。 本技術比較檢查了wkhtmltopdf與IronPDF,幫助架構師和開發者瞭解在安全姿態、渲染能力和長期可行性方面的顯著差異。

理解wkhtmltopdf

wkhtmltopdf是一個將HTML轉換為PDF文檔的工具,直接從命令行操作並利用Qt WebKit來處理HTML內容。 在其活躍開發的歲月中,該程式庫因其免費的LGPLv3許可和跨平台可用性而受到歡迎。

然而,目前wkhtmltopdf呈現出不可忽視的關鍵挑戰:

  • 專案棄置: 最後一次有意義的軟體更新大約在2016-2017年間
  • 嚴重安全漏洞: CVE-2022-35583 (CVSS 9.8嚴重級別) 是一種SSRF漏洞,仍未被修復
  • 過時的渲染引擎: 依賴2015年的Qt WebKit
  • 有限的現代網頁支持: 沒有CSS Grid支持,Flexbox實現損壞,沒有ES6+ JavaScript
  • 生態系統停滯: 所有.NET封裝程式庫 (DinkToPdf, Rotativa, TuesPechkin, wkhtmltopdf-.NET, NReco.PdfGenerator) 繼承了這些漏洞

CVE-2022-35583安全危機

wkhtmltopdf的伺服器端請求偽造(SSRF)漏洞允許攻擊者:

  • 訪問內部服務: 接觸防火牆後面的內部API、資料庫和服務
  • 竊取憑據: 訪問雲端元數據端點(AWS, GCP, Azure)以竊取IAM憑據
  • 端口掃描: 從基礎設施內部掃描內部網路
  • 數據外洩: 通過製作的HTML/CSS提取敏感數據

攻擊向量很直接——將惡意HTML提交給PDF生成器:


<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin"/>

<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/"></iframe>
<img src="http://internal-database:5432/admin"/>
HTML

當wkhtmltopdf渲染此HTML時,它將從伺服器的網路環境中抓取這些URLs,繞過防火牆和安全控制。 由於專案已正式棄置,這個漏洞永遠不會被修復。

了解IronPDF

IronPDF提供了一個強大的替代方案來解決wkhtmltopdf的缺點。 透過活躍的維護、定期更新和依賴當前的Chromium渲染引擎,IronPDF提供了安全性和現代網頁標準的合規性。

主要特徵包括:

  • 現代Chromium引擎: 使用當前的Chromium渲染引擎,完全支持ES2024 JavaScript
  • 無已知CVE: 零已知安全漏洞
  • 活躍開發: 定期發布安全更新和功能增強
  • 完整的CSS支援: 完全的CSS Grid、Flexbox和現代佈局系統
  • 綜合PDF功能: 數位簽名、PDF/A合規、PDF操作能力
  • 專業支援: 豐富的文件和專屬支援渠道

功能比較

下表突出顯示了wkhtmltopdf與IronPDF之間的基本差異:

功能wkhtmltopdfIronPDF
授權LGPLv3 (免費)商業
渲染引擎Qt WebKit (2015)當前的Chromium引擎
安全狀態CVE-2022-35583 關鍵 (9.8) 未修補無已知CVE
最後的有意義更新2016-2017活躍開發
CSS Grid不支持支持
Flexbox破損的支持
ES6+ JavaScript不支持支持
Async/Await不支持支持
PDF操作不支持支持
數位簽名不支持支持
PDF/A合規性不支持支持
專業支持無(被棄置)商業與SLA
C#整合通過第三方封裝程式庫直接、定期更新

受影響的封裝程式庫

所有.wkhtmltopdf的.NET封裝程式庫都繼承了相同的漏洞:

封裝程式庫狀態安全風險
DinkToPdf被棄置嚴重
Rotativa被棄置嚴重
TuesPechkin被棄置嚴重
wkhtmltopdf-.NET被棄置嚴重
NReco.PdfGenerator使用wkhtmltopdf嚴重

如果您的應用程式使用這些程式庫中的任何一個,則易受CVE-2022-35583的攻擊。

API架構差異

wkhtmltopdf的封裝程式庫與IronPDF之間的API模式揭示了複雜性和可用性上的巨大差異。

wkhtmltopdf配置模式

wkhtmltopdf的封裝程式庫需要創建具有嵌套設置配置的文檔對象:

// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                Orientation = Orientation.Portrait,
                PaperSize = PaperKind.A4
            },
            Objects = {
                new ObjectSettings()
                {
                    HtmlContent = "<h1>Hello World</h1><p>This is a PDF from HTML.</p>"
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("output.pdf", pdf);
    }
}
// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                Orientation = Orientation.Portrait,
                PaperSize = PaperKind.A4
            },
            Objects = {
                new ObjectSettings()
                {
                    HtmlContent = "<h1>Hello World</h1><p>This is a PDF from HTML.</p>"
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("output.pdf", pdf);
    }
}
' NuGet: Install-Package WkHtmlToPdf-DotNet
Imports WkHtmlToPdfDotNet
Imports WkHtmlToPdfDotNet.Contracts
Imports System.IO

Class Program
    Shared Sub Main()
        Dim converter = New SynchronizedConverter(New PdfTools())
        Dim doc = New HtmlToPdfDocument() With {
            .GlobalSettings = New GlobalSettings() With {
                .ColorMode = ColorMode.Color,
                .Orientation = Orientation.Portrait,
                .PaperSize = PaperKind.A4
            },
            .Objects = {
                New ObjectSettings() With {
                    .HtmlContent = "<h1>Hello World</h1><p>This is a PDF from HTML.</p>"
                }
            }
        }
        Dim pdf As Byte() = converter.Convert(doc)
        File.WriteAllBytes("output.pdf", pdf)
    End Sub
End Class
$vbLabelText   $csharpLabel

此模式需要使用Objects集合,然後手動將位元組陣列寫入檔案。

IronPDF簡化模式

IronPDF使用ChromePdfRenderer類的簡化方法:

// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF from HTML.</p>");
        pdf.SaveAs("output.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF from HTML.</p>");
        pdf.SaveAs("output.pdf");
    }
}
Imports IronPdf
Imports System

Class Program
    Shared Sub Main()
        Dim renderer = New ChromePdfRenderer()
        Dim pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF from HTML.</p>")
        pdf.SaveAs("output.pdf")
    End Sub
End Class
$vbLabelText   $csharpLabel

ChromePdfRenderer類去除了嵌套配置對象,返回具有內置保存方法的PdfDocument。 欲獲取完整的HTML轉換指南,請參閱HTML轉PDF指南

URL到PDF的轉換

將網頁轉換為PDF顯示了這兩種方法間複雜性的差異。

wkhtmltopdf實現

wkhtmltopdf在Page屬性來指定URLs:

// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                Orientation = Orientation.Portrait,
                PaperSize = PaperKind.A4
            },
            Objects = {
                new ObjectSettings()
                {
                    Page = "https://www.example.com"
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("webpage.pdf", pdf);
    }
}
// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                Orientation = Orientation.Portrait,
                PaperSize = PaperKind.A4
            },
            Objects = {
                new ObjectSettings()
                {
                    Page = "https://www.example.com"
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("webpage.pdf", pdf);
    }
}
' NuGet: Install-Package WkHtmlToPdf-DotNet
Imports WkHtmlToPdfDotNet
Imports WkHtmlToPdfDotNet.Contracts
Imports System.IO

Module Program
    Sub Main()
        Dim converter As New SynchronizedConverter(New PdfTools())
        Dim doc As New HtmlToPdfDocument() With {
            .GlobalSettings = New GlobalSettings() With {
                .ColorMode = ColorMode.Color,
                .Orientation = Orientation.Portrait,
                .PaperSize = PaperKind.A4
            },
            .Objects = {
                New ObjectSettings() With {
                    .Page = "https://www.example.com"
                }
            }
        }
        Dim pdf As Byte() = converter.Convert(doc)
        File.WriteAllBytes("webpage.pdf", pdf)
    End Sub
End Module
$vbLabelText   $csharpLabel

IronPDF實現

IronPDF提供了一個專用的RenderUrlAsPdf方法:

// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
        pdf.SaveAs("webpage.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
        pdf.SaveAs("webpage.pdf");
    }
}
Imports IronPdf
Imports System

Class Program
    Shared Sub Main()
        Dim renderer = New ChromePdfRenderer()
        Dim pdf = renderer.RenderUrlAsPdf("https://www.example.com")
        pdf.SaveAs("webpage.pdf")
    End Sub
End Class
$vbLabelText   $csharpLabel

RenderUrlAsPdf方法利用Chromium引擎來渲染頁面,具有完整的JavaScript執行和現代CSS支援——這些功能在wkhtmltopdf的2015年WebKit引擎中是有限的。

自訂PDF設置

配置頁面尺寸、邊距和方向展示了API之間的結構性差異。

wkhtmltopdf自訂設置

wkhtmltopdf需要嵌套MarginSettings對象:

// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                Orientation = Orientation.Landscape,
                PaperSize = PaperKind.A4,
                Margins = new MarginSettings() { Top = 10, Bottom = 10, Left = 10, Right = 10 }
            },
            Objects = {
                new ObjectSettings()
                {
                    Page = "input.html",
                    WebSettings = { DefaultEncoding = "utf-8" }
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("custom-output.pdf", pdf);
    }
}
// NuGet: Install-Package WkHtmlToPdf-DotNet
using WkHtmlToPdfDotNet;
using WkHtmlToPdfDotNet.Contracts;
using System.IO;

class Program
{
    static void Main()
    {
        var converter = new SynchronizedConverter(new PdfTools());
        var doc = new HtmlToPdfDocument()
        {
            GlobalSettings = {
                ColorMode = ColorMode.Color,
                Orientation = Orientation.Landscape,
                PaperSize = PaperKind.A4,
                Margins = new MarginSettings() { Top = 10, Bottom = 10, Left = 10, Right = 10 }
            },
            Objects = {
                new ObjectSettings()
                {
                    Page = "input.html",
                    WebSettings = { DefaultEncoding = "utf-8" }
                }
            }
        };
        byte[] pdf = converter.Convert(doc);
        File.WriteAllBytes("custom-output.pdf", pdf);
    }
}
' NuGet: Install-Package WkHtmlToPdf-DotNet
Imports WkHtmlToPdfDotNet
Imports WkHtmlToPdfDotNet.Contracts
Imports System.IO

Class Program
    Shared Sub Main()
        Dim converter As New SynchronizedConverter(New PdfTools())
        Dim doc As New HtmlToPdfDocument() With {
            .GlobalSettings = New GlobalSettings() With {
                .ColorMode = ColorMode.Color,
                .Orientation = Orientation.Landscape,
                .PaperSize = PaperKind.A4,
                .Margins = New MarginSettings() With {.Top = 10, .Bottom = 10, .Left = 10, .Right = 10}
            },
            .Objects = {
                New ObjectSettings() With {
                    .Page = "input.html",
                    .WebSettings = New WebSettings() With {.DefaultEncoding = "utf-8"}
                }
            }
        }
        Dim pdf As Byte() = converter.Convert(doc)
        File.WriteAllBytes("custom-output.pdf", pdf)
    End Sub
End Class
$vbLabelText   $csharpLabel

IronPDF自定義設置

IronPDF使用RenderingOptions屬性進行直接配置:

// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
        renderer.RenderingOptions.MarginTop = 10;
        renderer.RenderingOptions.MarginBottom = 10;
        renderer.RenderingOptions.MarginLeft = 10;
        renderer.RenderingOptions.MarginRight = 10;
        renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;

        var pdf = renderer.RenderHtmlFileAsPdf("input.html");
        pdf.SaveAs("custom-output.pdf");
    }
}
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
using System;

class Program
{
    static void Main()
    {
        var renderer = new ChromePdfRenderer();
        renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
        renderer.RenderingOptions.MarginTop = 10;
        renderer.RenderingOptions.MarginBottom = 10;
        renderer.RenderingOptions.MarginLeft = 10;
        renderer.RenderingOptions.MarginRight = 10;
        renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;

        var pdf = renderer.RenderHtmlFileAsPdf("input.html");
        pdf.SaveAs("custom-output.pdf");
    }
}
Imports IronPdf
Imports IronPdf.Rendering
Imports System

Class Program
    Shared Sub Main()
        Dim renderer As New ChromePdfRenderer()
        renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape
        renderer.RenderingOptions.MarginTop = 10
        renderer.RenderingOptions.MarginBottom = 10
        renderer.RenderingOptions.MarginLeft = 10
        renderer.RenderingOptions.MarginRight = 10
        renderer.RenderingOptions.PaperSize = PdfPaperSize.A4

        Dim pdf = renderer.RenderHtmlFileAsPdf("input.html")
        pdf.SaveAs("custom-output.pdf")
    End Sub
End Class
$vbLabelText   $csharpLabel

API對映參考

評估從wkhtmltopdf轉換到IronPDF的團隊將發現這種映射對理解概念等價很有幫助:

CLI到C# API映射

wkhtmltopdf CLI選項IronPDF 等效
wkhtmltopdf input.html output.pdfrenderer.RenderHtmlFileAsPdf()
wkhtmltopdf URL output.pdfrenderer.RenderUrlAsPdf()
--page-size A4RenderingOptions.PaperSize = PdfPaperSize.A4
--page-size LetterRenderingOptions.PaperSize = PdfPaperSize.Letter
--orientation LandscapeRenderingOptions.PaperOrientation = Landscape
--margin-top 10mmRenderingOptions.MarginTop = 10
--margin-bottom 10mmRenderingOptions.MarginBottom = 10
--margin-left 10mmRenderingOptions.MarginLeft = 10
--margin-right 10mmRenderingOptions.MarginRight = 10
--header-html header.htmlRenderingOptions.HtmlHeader
--footer-html footer.htmlRenderingOptions.HtmlFooter
--footer-center "[page]"{page} 佔位符
--footer-center "[toPage]"{total-pages} 佔位符
--enable-javascript預設為啟用
--javascript-delay 500RenderingOptions.WaitFor.RenderDelay = 500
--print-media-typeRenderingOptions.CssMediaType = Print
--dpi 300RenderingOptions.Dpi = 300
--grayscaleRenderingOptions.GrayScale = true
--zoom 0.8RenderingOptions.Zoom = 80

C#封裝API映射

wkhtmltopdf封裝程式庫IronPDF
SynchronizedConverterChromePdfRenderer
HtmlToPdfDocumentRenderingOptions
GlobalSettings.Outpdf.SaveAs()
GlobalSettings.PaperSizeRenderingOptions.PaperSize
GlobalSettings.OrientationRenderingOptions.PaperOrientation
GlobalSettings.MarginsRenderingOptions.Margin*
ObjectSettings.PageRenderHtmlFileAsPdf()
ObjectSettings.HtmlContentRenderHtmlAsPdf()
HeaderSettings.CenterTextHeader.CenterText
FooterSettings.CenterTextFooter.CenterText
converter.Convert(doc)renderer.RenderHtmlAsPdf()

佔位符語法映射

wkhtmltopdf佔位符IronPDF佔位符
[page]{page}
[toPage]{total-pages}
[date]{date}
[time]{time}
[title]{html-title}
[url]{url}

當團隊考慮從wkhtmltopdf遷移到IronPDF時

幾個常見情況通常使開發團隊評估IronPDF作為wkhtmltopdf的替代方案:

安全合規要求

擁有安全合規要求的組織(SOC 2、PCI DSS、HIPAA)不能接受具有已知關鍵漏洞的應用程式。 CVE-2022-35583的9.8嚴重等級在大多數安全框架中觸發即時修復要求。

現代CSS框架採用

採用Bootstrap 5、Tailwind CSS或自訂CSS Grid佈局的團隊發現wkhtmltopdf無法正確渲染這些內容。 2015年的WebKit引擎完全缺乏CSS Grid支援,Flexbox實現損壞。

JavaScript應用要求

使用現代JavaScript功能的應用程式——包括箭頭函數、async/await、類和模板字面的ES6+語法——在wkhtmltopdf中會出現問題。 IronPDF的Chromium引擎提供完整的JavaScript支援。

雲端和容器部署

使用Docker、Kubernetes或雲端平台的現代部署策略受益於IronPDF的容器友好架構。 在容器中對wkhtmltopdf二進制檔的安全掃描將標記CVE漏洞。

長期維護問題

由於wkhtmltopdf無法預期未來的更新,隨著網頁標準的演變,團隊將面臨越來越多的技術債務。 IronPDF的活躍開發確保與未來.NET版本(包括預計於2026年的.NET 10)的持續兼容性。

額外的IronPDF功能

除了HTML至PDF轉換外,IronPDF還提供wkhtmltopdf無法提供的文件操作功能:

Async支持

IronPDF提供web應用程式性能的async/await支持:

public async Task<byte[]> GeneratePdfAsync(string html)
{
    var renderer = new ChromePdfRenderer();
    var pdf = await renderer.RenderHtmlAsPdfAsync(html);
    return pdf.BinaryData;
}
public async Task<byte[]> GeneratePdfAsync(string html)
{
    var renderer = new ChromePdfRenderer();
    var pdf = await renderer.RenderHtmlAsPdfAsync(html);
    return pdf.BinaryData;
}
Imports System.Threading.Tasks

Public Async Function GeneratePdfAsync(html As String) As Task(Of Byte())
    Dim renderer As New ChromePdfRenderer()
    Dim pdf = Await renderer.RenderHtmlAsPdfAsync(html)
    Return pdf.BinaryData
End Function
$vbLabelText   $csharpLabel

這防止了高負載web應用程式中的線程阻塞——wkhtmltopdf的僅同步封裝程序無法提供此功能。

.NET兼容性和未來準備

wkhtmltopdf的棄置意味著沒有為較新的.NET版本進行的相容性測試或更新。 IronPDF持續活躍開發並定期更新,確保與.NET 8,.NET 9的兼容性,以及未來包括預計2026年推出的.NET 10版本的兼容性。 程式庫在其API中全面支持async/await,符合包括預期於C# 14中推出的現代C#開發實踐。

結論

在安全性、渲染能力和長期可行性方面,wkhtmltopdf和IronPDF之間的差異是顯著的。 wkhtmltopdf的關鍵SSRF漏洞(CVE-2022-35583)結合專案棄置,對於生產應用程式創造了一個無法維持的安全姿態。 2015年的WebKit引擎無法處理現代CSS Grid、Flexbox支援壞掉,並且在ES6+ JavaScript上失敗。

IronPDF基於Chromium的渲染引擎在維持零已知CVE的同時,提供了對現代網頁標準的全部支援。 其簡化的API設計——諸如SaveAs()的方法,而不是嵌套的配置對象——減少了程式碼的複雜性,同時增加了PDF操作、數位簽名和async支持等wkhtmltopdf無法提供的功能。

對於目前使用wkhtmltopdf或其封裝程式庫(DinkToPdf、Rotativa、TuesPechkin)的團隊,安全性考量要求立即評估替代方案。 wkhtmltopdf的CLI選項與IronPDF的RenderingOptions之間的API映射是直接的,IronPDF始終需要更少的程式碼,並在消除wkhtmltopdf固有的安全風險的同時做到這一點。

欲瞭解更多實施指導,請探索IronPDF文檔教程,涵蓋具體使用案例和高級功能。

請注意DinkToPdf, NReco, Rotativa, TuesPechkin和wkhtmltopdf是其各自所有者的註冊商標。 本網站與DinkToPdf、NReco、Rotativa、TuesPechkin或wkhtmltopdf沒有關聯、贊助或支持。 所有產品名稱、標誌及商標均為其各自所有者的財產。 比較僅供信息參考,反映在寫作時公開的相關信息。)}]