[C#] XXE攻撃を防ぐ方法


はじめに

XXE攻撃は、処理の際に動的にドキュメントを作成するXMLの機能を利用します。

XMLエンティティは、特定のリソースから動的にデータを取り込むことができます。
外部エンティティでは、XMLドキュメントに外部URIからのデータを含めることができます。

別途設定をしない限り、外部エンティティはXMLパーサーにURIで指定されたリソースへ強制的にアクセスすることになります。このような攻撃をXSS攻撃と呼ばれています。

では実際にどのような場合にXSS攻撃の脆弱性があると言われるのでしょうか。
下記の状態だとXSS攻撃に対する脆弱性があります。

private void Parser(string xml)
{
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(xml);
}

例:外部参照があるXML

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE foo [
    <!ELEMENT foo ANY >
    <!ENTITY xxe SYSTEM "file:///c:\winnt\win.ini">]><foo>&xxe;</foo>

本例では、XMLを読み込む際、ファイルの内容をC:\winnt\win.iniシステムファイルの内容に置き換えられ、外部に公開される可能性があります。

対策

XXE攻撃を避ける最良の方法は、DtdProcessingをDtdProcessing.Prohibitに設定してインラインDTDを無効にする方法と、XmlReaderSettings.XmlResolverプロパティをNULLに設定して、XMLエンティティレゾリューションを無効にする方法があります。

ここではXmlReaderSettings.XmlResolverプロパティをNULLに設定する方法を紹介します。

XmlDocument xmlDoc = new xmlDocument();
xmlDoc.XmlResolver = null;
xmlDoc.LoadXml(xml);

また、URLの場所をフィルタリングする場合は、下記のように独自クラスを派生させ、サニタイズする方法があります。

class CustomUrlResolver : XmlUrlResolver
{
    public override Uri ResolveUri(Uri base Uri, string relativeUri)
    {
        Uri uri = new Uri(baseUri, relativeUri)
        {
            If(IsUnsafeHost(uri.Host) == true)
            {
                return null;
            }
            return base.ResolveUri(baseUri, relativeUri);
        }       
    }

    private bool IsUnsafeHost(string host)
    {
        //  ここは独自に修正
        return false;
    }
}

IsUnsafeHostメソッドは指定されたホストが許可されているかどうかをチェックするカスタム関数となります。
必要に応じて処理を追加します。

上記カスタムクラスを利用すると下記のようになります。

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.XmlResolver = new CustomUrlResolver();
xmlDoc.LoadXml(xml);

こちらも参考にしてみてください。

http://docs.microsoft.com/en-us/dotnet/standard/data/xml/resolving-external-resources