如何防止XXE攻击

时间:2016-11-17 07:39:21

标签: java xml xml-parsing transform

我们对代码进行了安全审核,并提到我们的代码容易受到XML外部实体(XXE)攻击。

  

说明

     

XML外部实体攻击受益于XML功能,可在处理时动态构建文档。一个XML   实体允许从给定资源动态包含数据。外部实体允许XML文档包含数据   来自外部URI。除非配置为执行其他操作,否则外部实体会强制XML解析器访问指定的资源   通过URI,例如本地机器上或远程系统上的文件。此行为将应用程序公开给XML External   实体(XXE)攻击,可用于执行本地系统的拒绝服务,获取对文件的未授权访问   本地计算机,扫描远程计算机,并执行远程系统的拒绝服务。

     

以下XML文档显示了XXE攻击的示例。

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///dev/random" >]><foo>&xxe;</foo>
     

如果XML解析器尝试使用内容替换实体,则此示例可能会使服务器(在UNIX系统上)崩溃   / dev / random文件。

     

建议

     

应该安全地配置XML unmarshaller,以便它不允许外部实体作为传入XML的一部分   文档。

     

要避免XXE注入,请不要使用直接处理XML源的解组方法java.io.Filejava.io.Reader或   java.io.InputStream。使用安全配置的解析器解析文档,并使用将安全解析器作为XML源的unmarshal方法,如以下示例所示:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(<XML Source>);
Model model = (Model) u.unmarshal(document);

以下代码是审计发现XXE攻击的地方:

Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
System.out.println("outputing to : " + outputLocation);
File outputFile = new File(outputLocation);
StreamResult result = new StreamResult(outputFile);
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);

如何在我的代码中实现上述建议?我在哪里错过了什么?

5 个答案:

答案 0 :(得分:6)

您可以对DocumentBuilderFactory使用相同的方法:

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
...

要让每个人自动使用它,您需要创建自己的实现(通过扩展您当前正在使用的实现;使用您的调试器来查找)。在构造函数中设置该功能。

然后,您可以将新工厂传递给System属性javax.xml.parsers.DocumentBuilderFactory使用到Java VM,每个人都将使用它。

答案 1 :(得分:3)

请注意,仅使用FEATURE_SECURE_PROCESSING似乎不够安全(来自blackhat-pdf):

  

...尽管Oracle推荐使用XML   解析器实际上并没有限制外部连接   特征   _SECURE_PROCESSING   已启用。

OWASP建议使用ACCESS_EXTERNAL_DTD和ACCESS_EXTERNAL_STYLESHEET。

这将使:

TransformerFactory trfactory = TransformerFactory.newInstance();
trfactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
trfactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
trfactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");

实际上,这个问题是以下内容的重复:How to Prevent XML External Entity Injection on TransformerFactory

答案 2 :(得分:1)

您希望在TransformerFactory上启用安全处理功能。它将限制某些可能发生的恶意事件(DOS攻击等)

TransformerFactory tf = TransformerFactory.newInstance();
tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = tf.newTransformer();

答案 3 :(得分:0)

由于市场上有大量的xml解析引擎,因此防止XXE攻击的方法因引擎而异。请参考引擎文档。这里的基础是防止外部DOCTYPE声明。如果需要外部DOCTYPE声明,则禁用外部通用实体和外部参数实体将防止对您的代码进行XXE攻击。以下是使用SAX解析器时防止XXE的示例代码。

package com.doepiccoding.facedetection;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class CustomView extends View {
    private int posX;
    private int posY;
    private Bitmap leftEyeBmp;
    private int leftEyeBmpWidth;
    private int leftEyeBmpHeight;
    private Paint paint = new Paint();

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        leftEyeBmp = BitmapFactory.decodeResource(context.getResources(), R.drawable.my_face_glasses);
        if(leftEyeBmp != null){
            leftEyeBmpWidth = leftEyeBmp.getWidth();
            leftEyeBmpHeight = leftEyeBmp.getHeight();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if(leftEyeBmp != null && !leftEyeBmp.isRecycled())leftEyeBmp.recycle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
//      super.onDraw(canvas);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(3);
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.argb(255, 237, 28, 36));
        canvas.drawBitmap(leftEyeBmp, posX - leftEyeBmpWidth /2, posY - leftEyeBmpHeight /2, paint);
        //canvas.drawBitmap(leftEyeBmp, leftEyeBmpWidth , leftEyeBmpHeight, paint);
//      canvas.drawLine(2,2,4,4, paint);
        int screenWidth = posX - leftEyeBmpWidth /2;
        int screenHeight = posY - leftEyeBmpHeight /2;
//      canvas.drawLine(posX, posX, posXx, posY, paint);
//      canvas.drawCircle(getWidth(), getHeight()/2, 100, paint);

        //garis horizontal tengah
        canvas.drawLine(
                0, // startX
                canvas.getHeight() / 2, // startY
                canvas.getWidth() , // stopX
                canvas.getHeight() / 2, // stopY
                paint // Paint
        );

        //garis horizontal sebelah atas kamera
        canvas.drawLine(
                0, // startX
                canvas.getHeight() / 2 - 250, // startY
                canvas.getWidth() , // stopX
                canvas.getHeight() / 2 - 250, // stopY
                paint // Paint
        );

        //garis vertical tengah
        canvas.drawLine(
                canvas.getWidth() / 2, // startX
                0, // startY
                canvas.getWidth() / 2, // stopX
                canvas.getWidth(), // stopY
                paint // Paint
        );

        //garis vertical sebelah kiri kamera
        canvas.drawLine(
                canvas.getWidth() / 2 - 250, // startX
                0, // startY
                canvas.getWidth() / 2 - 250, // stopX
                canvas.getWidth(), // stopY
                paint // Paint
        );

        //garis vertical sebelah kanan kamera
        canvas.drawLine(
                canvas.getWidth() / 2 + 250, // startX
                0, // startY
                canvas.getWidth() / 2 + 250, // stopX
                canvas.getHeight(), // stopY
                paint // Paint
        );
        canvas.drawRect(60, 50, 210, 297, paint);

    }

    public void setPoints(int x, int y){
        this.posX = x;
        this.posY = y;
    }

}

答案 4 :(得分:0)

我确实尝试过Java 8

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///dev/random" >]><foo>&xxe;</foo>

并得到以下异常:

Caused by: org.xml.sax.SAXParseException: External Entity: Failed to read external document 'logs', because 'file' access is not allowed due to restriction set by the accessExternalDTD property.