如何在所有ASP.NET AJAX HTTPS响应中安全地添加随机填充?

时间:2011-09-30 14:18:41

标签: asp.net ajax security ssl

显然,it is possible to leak data in an SSL connection based on the size of HTTPS请求和响应。

考虑到ASP.NET管道的复杂性,如何安全地添加随机数量的数据,将每个大小舍入到最接近的500K?

安全限制(正如我现在所知)是

  • 不要泄漏时间信息,并且在生成额外数据时总是花费相同的时间

  • 始终向下舍入到下一个500K值

  • 用作填充的数据不必是随机的,我们的目标是更改HTTPS响应的大小

我会post the related javascript question(HTTP POST)在一个单独的问题中,作为对可能没有查看ASP实现的PHP用户的礼貌;)

2 个答案:

答案 0 :(得分:2)

阅读完链接的文章后,我首先要说的是最好的安全性可能是客户端;一个值得信赖的安全代理 - 这种类型的分析依赖于对应用程序的深入了解,如果攻击者无法告诉您访问哪个网站,则无法分析响应。

也就是说,有些事情可以让您的网站不那么容易受到攻击。但它会比简单的填充更复杂。其中一个例子是选择医生。所有访客都可以获得可用医生名单。如果你有一个汽车完成它根本不是很难减少潜在医生的数量,因为你可以观察流量。一个极端的例子是500名医生,只有一名名字以Z开头:Zhivago博士。因此,一封寄回一位医生的信件可能是他。如果基于简单的舍入到最接近的500字节,则填充发送和接收数据将不会隐藏它。一个发送和接收,然后继续到页面上的下一个操作仍然是他。在这种情况下,你可以做几件事 - 首先总是返回多种可能性,即使只有一种,在客户端只减少一种。其次填充请求的数量,而不仅仅是请求中的数据。

回到你的问题,当压缩的大小泄漏时,压缩使填充成为问题。使用在客户端上过滤掉的有效数据尽可能多地填充,这将使任何尝试更难分析它。在做填充时,请确保不会泄漏填充时间。

最后 - 这种类型的攻击必须是应用程序,可能是用户特定的。有没有人真正关心你的流量呢?

答案 1 :(得分:2)

您可以创建一个过滤器来修改响应,然后再将其发送给客户端。您所要做的就是创建一个IHttpModule并拦截ASP.NET请求管道。您可以设置Filter Stream,它将缓冲您的响应,最后,ASP.NET管道将调用" Close"方法,那个时候你可以修改缓冲区并发送回客户端。

为了提高性能,您还可以截取Write方法并编写更好的逻辑,而不是缓冲整个响应。但我会留在你身上。

首先将web.config配置为

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
        <add name="RandomPaddingModule"
             type="Namespace.RandomPaddingModule, AssemblyName"/>
    </modules>
</system.webServer>

并添加以下代码

public class RandomPaddingModule : IHttpModule{

    public void Dispose()
    {
    }

    public void Init(HttpApplication context)
    {
        // Apply filter immediately
        context.BeginRequest += (s, e) =>
        {
            context.Response.Filter = 
               new RanndomPaddingStream(context.Response.Filter,
                   context.Context);

        };
    }

}


public class AtomPreCompilerStream : ResponseFilterStream
{

    private HttpContext context;

    public RanndomPaddingStream(Stream s, HttpContext c)
        : base(s)
    {
        this.context = c;
    }

    // process buffer before sending it to client...
    protected override byte[] ProcessBuffer(byte[] p)
    {

         if(string.Equals(
               context.Response.ContentType,
               "application/json", 
               StringComparison.OrdinalIgnoreCase)){
             // do some padding....
         }

         return p;
    }
}

public class ResponseFilterStream: Stream
{
    /// <summary>
    /// The original stream
    /// </summary>
    Stream _stream;


    /// <summary>
    /// Stream that original content is read into
    /// and then passed to TransformStream function
    /// </summary>
    MemoryStream _cacheStream = new MemoryStream(5000);

    public byte[] Buffer
    {
        get
        {
            return _cacheStream.ToArray();
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="responseStream"></param>
    public AtomPreCompilerFilterStream(Stream responseStream)
    {
        _stream = responseStream;
    }


    /// <summary>
    /// 
    /// </summary>
    public override bool CanRead
    {
        get { return true; }
    }

    public override bool CanSeek
    {
        get { return true; }
    }
    /// <summary>
    /// 
    /// </summary>
    public override bool CanWrite
    {
        get { return true; }
    }

    /// <summary>
    /// 
    /// </summary>
    public override long Length
    {
        get { return 0; }
    }

    /// <summary>
    /// 
    /// </summary>
    public override long Position
    {
        get { return _cacheStream .Position; }
        set { _cacheStream .Position = value; }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="offset"></param>
    /// <param name="direction"></param>
    /// <returns></returns>
    public override long Seek(long offset, System.IO.SeekOrigin direction)
    {
        return _cacheStream.Seek(offset, direction);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="length"></param>
    public override void SetLength(long length)
    {
        _cacheStream .SetLength(length);
    }

    /// <summary>
    /// 
    /// </summary>
    public override void Close()
    {
        byte[] data = ProcessBuffer(_cacheStream.ToArray());
        _stream.Write(data, 0, data.Length);
        _stream.Close();
    }

    protected virtual byte[] ProcessBuffer(byte[] p)
    {
        return p;
    }

    /// <summary>
    /// Override flush by writing out the cached stream data
    /// </summary>
    public override void Flush()
    {

        // default flush behavior
        //_stream.Flush();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    public override int Read(byte[] buffer, int offset, int count)
    {
        return _cacheStream.Read(buffer, offset, count);
    }


    /// <summary>
    /// Overriden to capture output written by ASP.NET and captured
    /// into a cached stream that is written out later when Flush()
    /// is called.
    /// </summary>
    /// <param name="buffer"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    public override void Write(byte[] buffer, int offset, int count)
    {
        _cacheStream.Write(buffer, offset, count);
        //_stream.Write(buffer, offset, count);

    }

}