【问题标题】:Handle cross site scripting flaw raised by veracode处理由 veracode 引发的跨站脚本漏洞
【发布时间】:2017-04-02 10:42:05
【问题描述】:

我们有一个使用 ASP.Net 和 C# 的旧版 Web 应用程序,为此我们收到了大约 400 个由 Veracode 扫描引发的跨站点脚本缺陷。 我创建了一个示例 Web 应用程序并模拟了这个问题,发现每当我们直接使用任何字符串输入时,它都会引发缺陷。 做HttpUtility.HtmlEncode(TextBox1.Text);"satisfies the veracode,但是在所有 400 个地方应用这个更改是不可行的,因为那时会有大量的工作和测试工作。 我正在寻找某种方式在 httphandler 中实现一些插件,以便所有输入都在一个地方编码,我们不必在任何地方更改它。 如果可以的话,有人可以指导我吗?即使您可以指导我进近,至少也足以有一个方向。 非常感谢。

StringOps strop = new StringOps();
        string txt1, txt2;
        txt1 = HttpUtility.HtmlEncode(TextBox1.Text);
        txt2 = HttpUtility.HtmlEncode(TextBox2.Text);
        Response.Write(strop.Add(txt1, txt2));

如果我删除 HttpUtility.HTMLEncode 行,Veracode 会抱怨它。由于我们在很多地方都在做这个字符串操作,所以在任何地方实现这个都是不可行的。这是否有可能在一个地方实现这种编码,并且所有响应和请求都应该通过该管道,例如HTTPHandler 和 HTTPModule。

【问题讨论】:

  • edit您的问题包括minimal reproducible example,包括输入和输出。您肯定不想对所有输入进行百分比编码,您只需要对再次打印为 HTML 的输入进行编码。
  • @Alok 你的意思是应用程序将原始输入字符串直接写入响应,从而绕过所有 ASP.NET 的安全检查和保护?那么是的,你必须修复每一行有问题的代码。 并且向所有文本框添加验证,以便它们首先不允许无效输入。
  • 是的,如果你要去Response.Write(TextBox1.Text),你会过得很糟糕。这种模式在您的代码中出现的次数那么多吗?
  • @Alok,您正在使用Response.Write绕过任何此类接口。这会将 raw 文本发送到客户端。 为什么你还是要这样做?这是一种 REST 服务吗?无论如何,错误在于您没有验证您的输入。编码只会隐藏验证工具中的错误。如果客户端输入x'; drop table users; --,它不会阻止例如SQL注入
  • 好吧,我的感觉是,即使你找到了一些方法来防止 XSS 在一个地方,Veracode 也可能无法识别这种方法,并且当它没有看到使用的 HtmlEncode 时仍然会抱怨。这只是一种感觉。

标签: c# asp.net veracode


【解决方案1】:

您可以使用自定义HttpModule 来完成此操作,该自定义HttpModule 有条件地分配给HttpResponse.Filter 以拦截和处理HttpResponse.Write 的使用。


模块示例

本例使用request.Header的Content-Type的值来判断是否应用html编码。

public class FilterResponseWriteModule : IHttpModule, IDisposable
{
    private System.IO.Stream filterStream;


    public FilterResponseWriteModule()
    {
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += Context_BeginRequest;
    }

    private void Context_BeginRequest(object sender, EventArgs e)
    {
        var context = (sender as HttpApplication).Context;


        if (ShouldApplyFilter(context.Request))
            ApplyFilter(context.Response);
    }

    private bool ShouldApplyFilter(HttpRequest request)
    {
        return string.Equals(request.ContentType, @"text/plain", StringComparison.OrdinalIgnoreCase);
    }

    private void ApplyFilter(HttpResponse response)
    {
        filterStream = new EncodeStreamFilter(response.Filter);
        response.Filter = filterStream;
    }

    public void Dispose()
    {
        if (filterStream != null)
        {
            filterStream.Dispose();
        }
    }
}

过滤流示例(封装和覆盖)

Stream 是一个抽象类,因此它将生成所有相关的覆盖方法存根。

public class EncodeStreamFilter : Stream, IDisposable
{
    private Stream _baseStream;

    public EncodeStreamFilter(Stream responseFilter)
    {
        _baseStream = responseFilter;            
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        byte[] bufferBlock = new byte[count];
        Buffer.BlockCopy(buffer, offset, bufferBlock, 0, count);

        var encodedBytes = Encoding.UTF8.GetBytes(HttpUtility.HtmlEncode(Encoding.UTF8.GetString(bufferBlock)));

        _baseStream.Write(encodedBytes, 0, encodedBytes.Length);
    }

    public override bool CanRead
    {
        get
        {
            return _baseStream.CanRead;
        }
    }

    public override bool CanSeek
    {
        get
        {
            return _baseStream.CanSeek;
        }
    }

    public override bool CanWrite
    {
        get
        {
            return _baseStream.CanWrite;
        }
    }

    public override long Length
    {
        get
        {
            return _baseStream.Length;
        }
    }

    public override long Position
    {
        get
        {
            return _baseStream.Position;
        }

        set
        {
            _baseStream.Position = value;
        }
    }

    public override void Flush()
    {
        _baseStream.Flush();
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return _baseStream.Read(buffer, offset, count);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return _baseStream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        _baseStream.SetLength(value);
    }



    protected override void Dispose(bool disposing)
    {
        if (!disposing)
        {
            _baseStream.Dispose();
        }
        base.Dispose(disposing);
    }
}

将模块添加到 Web.Config

注意:在这种情况下,我已将模块定义为我的应用程序的 App_Start 文件夹中的一个类。

<system.webServer>
    <modules>
        <add name="FilterResponseWriteModule" type="HttpModulesTestApp.App_Start.FilterResponseWriteModule"/>
    </modules>
</system.webServer>

【讨论】:

  • 这似乎回答了这个问题,但我敢打赌 Veracode 仍然会抱怨 (?)
  • @jdigital 可能,但似乎这个 veracode 框架/组件/工具正在抓取应用程序、发出请求并分析响应..
  • 这提醒了我.. byte[] buffer 被传递给 write 也可以有条件地处理.. 很可能会多次调用Write。需要更好的 OP 响应样本来反映这种考虑。
【解决方案2】:

听着,我在 asp.net 1.0 或 2.0 中也有一个旧站点。我们确实将其框架更改为 4.0。

所以,我的建议是更改其框架并运行冒烟测试,可能会出现一些问题,然后按预期解决,然后主要关注处理诸如 Response.Write 之类的事情。由于 ASP.net 现在是开源的,因此获取这些代码并对核心功能进行最少的更改并完成您的工作,请尝试尽可能多地利用部分功能或任何类似的东西,以便在不丢失更改的情况下进行升级。

【讨论】: