JPCERT コーディネーションセンター

IDS17-J. XML 外部エンティティ(実体)攻撃を防ぐ

IDS17-J. XML 外部エンティティ(実体)攻撃を防ぐ

「エンティティ(実体)宣言」は、よく使われるテキストや特殊な文字列へのショートカットを定義する。エンティティ宣言では、「内部エンティティ」や「外部エンティティ」を定義できる。内部エンティティは、エンティティの内容が宣言で指定される。外部エンティティは、内容が Uniform Resource Identifier (URI) で指定される。

エンティティは「解析対象」か「解析対象外」かでも区別できる。解析対象エンティティの内容は「置換テキスト」と呼ばれる。解析対象外エンティティの内容は、XML の場合もあるが、XML 以外のテキストの場合や、テキストでない場合もある。解析対象エンティティは、「エンティティ参照」により名前で呼び出される。解析対象外エンティティは、ENTITY または ENTITIES の属性として指定された名前で呼び出される。

XML W3C 勧告のセクション 4.4.3「検証するなら取り込む」[W3C 2008] には、以下のように書かれている。

XML プロセッサがドキュメントを検証するなら、解析対象エンティティへの参照を認識したとき、必ず置換テキストを取り込まなければならない。ドキュメントを検証しないなら、外部エンティティの置換テキストは取り込んでも取り込まなくてもよい。

外部エンティティの置換テキストは取り込まなくてもよいため、すべての XML プロセッサが検証時の外部エンティティ攻撃に脆弱なわけではない。

「XML 外部エンティティ (XXE)」攻撃は、外部エンティティへの参照を持つ XML 入力が、適切に設定されていない XML パーザで処理される際に発生する。攻撃者は XXE 攻撃を使って、パスワードやユーザの個人情報などのセンシティブなデータを含むローカルファイルを参照するようエンティティの URI を操作し、センシティブな情報にアクセスする可能性がある。また、攻撃者は、例えば /dev/random/dev/tty を入力の URI に指定して、プログラムのクラッシュや無期限の停止を引き起こし、サービス運用妨害 (denial-of-service) 攻撃を試みる可能性もある。

違反コード

以下の違反コード例では、evil.xml というファイルのパーズを試み、エラーがあれば報告して終了する。しかし、SAX (Simple API for XML) や DOM (Document Object Model) のパーザは SYSTEM 属性で指定された URI にアクセスしようとして、ローカルの /dev/tty ファイルの内容を読み込もうとする。POSIX システムでこのファイルを読み込むと、端末のコンソールに入力データが与えられるまでプログラムがブロックされる。結果として、攻撃者は悪意ある XML ファイルによりプログラムを停止させられる。

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

class XXE {
  private static void receiveXMLStream(InputStream inStream,
                                       DefaultHandler defaultHandler)
      throws ParserConfigurationException, SAXException, IOException {
    SAXParserFactory factory = SAXParserFactory.newInstance();
    SAXParser saxParser = factory.newSAXParser();
    saxParser.parse(inStream, defaultHandler);
  }

  public static void main(String[] args) throws ParserConfigurationException,
      SAXException, IOException {
    try {
      receiveXMLStream(new FileInputStream("evil.xml"), new DefaultHandler());
    } catch (java.net.MalformedURLException mue) {
      System.err.println("Malformed URL Exception: " + mue);
    }
  }
}

このプログラムは、evil.xml ファイルが以下のような内容だとすると、遠隔で XXE 攻撃の影響を受ける。

<?xml version="1.0"?>
<!DOCTYPE foo SYSTEM "file:/dev/tty">
<foo>bar</foo>
適合コード (EntityResolver)

以下の適合コードでは、CustomResolver クラスを定義し、org.xml.sax.EntityResolver インターフェースを実装している。このインターフェースにより、SAX アプリケーションで外部エンティティの取り扱いを変更できる。以下のコードでは、外部エンティティに単純なホワイトリストを適用している。指定された安全なエンティティのソースパスに入力値が対応していない場合、resolveEntity() メソッドは空の InputSource を返す。

import java.io.IOException;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

class CustomResolver implements EntityResolver {
  public InputSource resolveEntity(String publicId, String systemId)
      throws SAXException, IOException {

    // 想定済みの問題ないエンティティであるか確認する
    String entityPath = "file:/Users/onlinestore/good.xml";
    if (systemId.equals(entityPath)) {
      System.out.println("Resolving entity: " + publicId + " " + systemId);
      return new InputSource(entityPath);
    } else {
      // 想定外のエンティティなら空のパスを返す
      return new InputSource(); 
    }
  }
}

setEntityResolver() メソッドは、インスタンスを対応する SAX ドライバに登録する。 悪意ある入力値をパーズするとき、CustomResolver から返された空の InputSource は、java.net.MalformedURLException を引き起こす。CustomResolver を登録するための XMLReader オブジェクトを作らなければならないことに注意。

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

class XXE {
  private static void receiveXMLStream(InputStream inStream,
      DefaultHandler defaultHandler) throws ParserConfigurationException,
      SAXException, IOException {
    SAXParserFactory factory = SAXParserFactory.newInstance();
    SAXParser saxParser = factory.newSAXParser();

    // CustomResolver を登録するための XMLReader を作る
    XMLReader reader = saxParser.getXMLReader();
    reader.setEntityResolver(new CustomResolver());
    reader.setContentHandler(defaultHandler);

    InputSource is = new InputSource(inStream);
    reader.parse(is);
  }

  public static void main(String[] args) throws ParserConfigurationException,
      SAXException, IOException {
    try {
      receiveXMLStream(new FileInputStream("evil.xml"), new DefaultHandler());
    } catch (java.net.MalformedURLException mue) {
      System.err.println("Malformed URL Exception: " + mue);
    }
  }
}
リスク評価

ユーザからの入力を無害化せずに処理したり保存したりすると、インジェクション攻撃につながる。

ルール

深刻度

可能性

修正コスト

優先度

レベル

IDS17-J

P8

L2

自動検出
ツール バージョン チェッカー 説明
The Checker Framework

2.1.3

Tainting Checker Trust and security errors (see Chapter 8)
Fortify 1.0

Missing_XML_Validation

Implemented
SonarQube

9.9

S2755

S4435

Untrusted XML should be parsed with a local, static DTD

XML transformers should be secured

参考文献

[OWASP 2005]

A Guide to Building Secure Web Applications and Web Services

[OWASP 2007]

OWASP Top 10 for Java EE

[OWASP 2008]

Testing for XML Injection (OWASP-DV-008)

[Seacord 2015]

[W3C 2008]

Section 4.4.3, "Included If Validating"

翻訳元

これは以下のページを翻訳したものです。

IDS17-J. Prevent XML External Entity Attacks (revision 23)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter