安全でないXMLパーサー

安全でないXMLパーサー

説明

外部エンティティ参照を解決するXMLパーサーは、攻撃者が指定したファイルやネットワーク上のリソースにアクセスするおそれがあります。入力サイズやエンティティ展開を制限しない場合は、サービス拒否(DoS)にもつながります。実際の危険性は、パーサーのバージョンと設定によって異なります。

想定される影響

  • 情報漏えい: 外部エンティティを解決できる環境では、機密ファイルの内容が漏れる可能性があります。
  • サービス拒否: 過大な入力やエンティティ展開により、メモリやCPUが使い尽くされるおそれがあります。
  • 内部リソースへのアクセス: ネットワーク参照を許可すると、サーバーから到達できるリソースへのリクエストを誘発される可能性があります。

対処方法

  • 外部エンティティの解決と不要なDTDの読み込みを無効にしてください。
  • 入力サイズと処理リソースを制限し、解析エラーを処理してください。
  • Python、XMLパーサー、その基盤となるライブラリを、セキュリティ修正が適用されたバージョンに保ってください。

例

lxml

変更前

python
# lxmlの既定パーサーによるXML解析
from lxml import etree

def unsafe_parse(xml_string):
    root = etree.fromstring(xml_string)
    return root

xml_data = """<?xml version="1.0"?>
<!DOCTYPE root [
<!ELEMENT root ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root>&xxe;</root>"""
root = unsafe_parse(xml_data)

変更後

python
# エンティティを展開しないXML解析
from lxml import etree

def safe_parse(xml_string):
    parser = etree.XMLParser(resolve_entities=False, load_dtd=False, no_network=True)
    root = etree.fromstring(xml_string, parser=parser)
    return root

xml_data = """<?xml version="1.0"?>
<!DOCTYPE root [
<!ELEMENT root ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root>&xxe;</root>"""
root = safe_parse(xml_data)

解説:

  • 変更前: 既定のパーサーを使用しています。lxml 5.0以降の XMLParser は resolve_entities="internal" が既定値のため、この呼び出しが外部ファイルを読み込むとは限りません。古いバージョンや既定のパーサーを変更した環境は、別途確認してください。
  • 変更後: エンティティ展開、外部DTDの読み込み、ネットワークアクセスを明示的に無効にします。保守されていない defusedxml.lxml の代わりに、lxml のパーサーを直接設定しています。

ElementTree

変更前

python
# 標準ElementTreeによるXML解析
import xml.etree.ElementTree as ET

def unsafe_parse(xml_string):
    root = ET.fromstring(xml_string)
    return root

xml_data = """<?xml version="1.0"?>
<!DOCTYPE root [
<!ELEMENT root ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root>&xxe;</root>"""
root = unsafe_parse(xml_data)

変更後

python
# defusedxmlによるXML解析
from defusedxml.ElementTree import fromstring

def safe_parse(xml_string):
    root = fromstring(xml_string)
    return root

xml_data = """<?xml version="1.0"?>
<!DOCTYPE root [
<!ELEMENT root ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<root>&xxe;</root>"""
root = safe_parse(xml_data)

解説:

  • 変更前: 標準のElementTreeは、この外部エンティティを展開せず、解析エラーを返します。既定の呼び出しだけでファイル漏えいが起きるという意味ではありません。
  • 変更後: defusedxml.ElementTree は既定でエンティティ宣言を拒否するため、この入力には EntitiesForbidden 例外が発生します。アプリケーションの境界でエラーを処理し、入力サイズも別途制限してください。

参考資料