如何在libxml2in C中禁用XXE?

时间:2014-04-08 07:12:57

标签: c xml-parsing libxml2 xxe

要求:当我将以下请求传递给我的申请时,

1)如何对这样的输入xml进行XML验证,这是风险

2)如何在libxml2中禁用XXE,即不应解析ENTITY字段

<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY foo SYSTEM "file:///etc/issue">
]><TRANSACTION>
<FUNCTION_TYPE>LINE_ITEM</FUNCTION_TYPE>
<COMMAND>ADD</COMMAND>
<COUNTER>3</COUNTER>
<MAC>qof2EtycqT9YMcmOfKowpyXVbRpgM/7rncS3liK4JOs=</MAC>
<MAC_LABEL>P_206</MAC_LABEL>
<RUNNING_TAX_AMOUNT>0.00</RUNNING_TAX_AMOUNT>
<RUNNING_TRANS_AMOUNT>1.00</RUNNING_TRANS_AMOUNT>
<LINE_ITEMS>
<MERCHANDISE>
<LINE_ITEM_ID>1</LINE_ITEM_ID>
<DESCRIPTION>&foo;</DESCRIPTION>
<QUANTITY>1</QUANTITY>
<UNIT_PRICE>5.00</UNIT_PRICE>
<EXTENDED_PRICE>5.00</EXTENDED_PRICE>
</MERCHANDISE>
</LINE_ITEMS>
</TRANSACTION>

据我所知,从libxml2版本2.9开始,默认情况下已禁用XXE。但我们目前使用的是2.7.7版本。

根据此链接XML_ENTITY_PROCESSING

Enum xmlParserOption不应在libxml2中定义以下选项:

XML_PARSE_NOENT:扩展实体并用替换文本替换它们 XML_PARSE_DTDLOAD:加载外部DTD

直到现在我使用xmlParseMemory函数来解析XML内存块并构建树。此函数不接受任何参数来设置xmlParserOption。

然后我更改为xmlReadMemory函数,该函数与xmlParseMemory函数的作用相同,但采用不同的参数。

docPtr = xmlReadMemory(szXMLMsg, iLen, "noname.xml", NULL, XML_PARSE_RECOVER);

我仍然观察到ENTITY字段正在被解析。 谁能帮助我?如果您需要更多其他信息,请与我们联系。

感谢您的时间。

问候

普利文

1 个答案:

答案 0 :(得分:2)

如果您没有指定XML_PARSE_NOENT,则仍会解析ENTITY声明,但该实体不会被替换。此外,文件/etc/issue无法打开,您可以使用strace进行验证。因此,为了防止XXE,您只是不通过XML_PARSE_NOENT解析器选项。

选项的名称有点误导,XML_PARSE_NOENT表示不应在解析的文档中创建任何实体节点。因此,每个实体都在扩展。更好的名称就像XML_PARSE_EXPAND_ENTITIES

如果您确实想要确定或希望扩展具有对要加载的URL的细粒度控制的实体,则可以使用xmlSetExternalEntityLoader安装自己的外部实体加载程序。如果您的处理程序始终返回NULL,那么您就是安全的一面。但请注意,外部实体加载器用于加载所有类型的外部资源,因此完全禁用它可能会破坏其他内容(例如,XIncludes或XSLT样式表)。

编辑:我不知道为什么在您的情况下更换实体。这是一个测试程序:

#include <stdio.h>
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

static xmlNodePtr
find_node(xmlNodePtr parent, const char *name) {
    for (xmlNodePtr cur = parent->children; cur != NULL; cur = cur->next) {
        if (cur->type == XML_ELEMENT_NODE
            && xmlStrcmp(cur->name, (const xmlChar*)name) == 0
        ) {
            return cur;
        }
    }

    fprintf(stderr, "Element '%s' not found\n", name);
    abort();

    return NULL;
}

int
main(int argc, char **argv) {
    static const char buf[] =
        "<?xml version=\"1.0\"?>\n"
        "<!DOCTYPE foo [\n"
        "<!ENTITY foo SYSTEM \"file:///etc/issue\">\n"
        "]><TRANSACTION>\n"
        "<FUNCTION_TYPE>LINE_ITEM</FUNCTION_TYPE>\n"
        "<COMMAND>ADD</COMMAND>\n"
        "<COUNTER>3</COUNTER>\n"
        "<MAC>qof2EtycqT9YMcmOfKowpyXVbRpgM/7rncS3liK4JOs=</MAC>\n"
        "<MAC_LABEL>P_206</MAC_LABEL>\n"
        "<RUNNING_TAX_AMOUNT>0.00</RUNNING_TAX_AMOUNT>\n"
        "<RUNNING_TRANS_AMOUNT>1.00</RUNNING_TRANS_AMOUNT>\n"
        "<LINE_ITEMS>\n"
        "<MERCHANDISE>\n"
        "<LINE_ITEM_ID>1</LINE_ITEM_ID>\n"
        "<DESCRIPTION>&foo;</DESCRIPTION>\n"
        "<QUANTITY>1</QUANTITY>\n"
        "<UNIT_PRICE>5.00</UNIT_PRICE>\n"
        "<EXTENDED_PRICE>5.00</EXTENDED_PRICE>\n"
        "</MERCHANDISE>\n"
        "</LINE_ITEMS>\n"
        "</TRANSACTION>\n";

    xmlDocPtr doc = xmlReadMemory(buf, sizeof(buf), "noname.xml", NULL,
                                  XML_PARSE_RECOVER);

    xmlNodePtr trans = find_node((xmlNodePtr)doc, "TRANSACTION");
    xmlNodePtr items = find_node(trans, "LINE_ITEMS");
    xmlNodePtr merch = find_node(items, "MERCHANDISE");
    xmlNodePtr desc  = find_node(merch, "DESCRIPTION");

    for (xmlNodePtr cur = desc->children; cur != NULL; cur = cur->next) {
        if (cur->type == XML_ENTITY_REF_NODE) {
            printf("entity ref node\n");
        }
        else {
            printf("other node of type: %d\n", cur->type);
        }
    }

    xmlFreeDoc(doc);

    return 0;
}

如果我用

编译
gcc -std=c99 -O2 -I/usr/include/libxml2 so.c -lxml2 -o so

并运行它,结果是

entity ref node