Apparently, it is possible to leak data in an SSL connection based on the size of the HTTPS request and response.
Considering the complexity of the ASP.NET pipeline, how do I securely add a random amount of data that rounds each size up to the nearest 500K?
The security constraints (as I'm aware of now) are
Don't leak timing inform开发者_JAVA技巧ation, and always take the same amount of time when generating the extra data
Always round up to the next 500K value
The data used as padding doesn't have to be random, our goal is to change the size of the HTTPS response
I'll post the related javascript question (HTTP POST) in a separate question as a courtesy to the PHP users who may not be looking at ASP implementations ;)
Having read the linked articles, I first want to say that best security would probably be clientside; a trusted, secure proxy -- this type of analysis relies upon intimate knowledge of the application, if the attacker can't tell what website your visiting, it can't analyze the responses.
That said, there are things you can do to make YOUR site less vulnerable. But its going to be more complex than just simple padding. One of the examples given is picking a doctor. The list of available doctors is available to all visitors. If you have an auto complete its not hard at all to reduce the number of potential doctors down significantly given that you can watch the traffic. An extreme example would be 500 doctors, only one with a name starting with Z: Dr Zhivago. Thus a 1 letter send that returns one doctor is probably him. Padding the send and receive data won't be enouh to hide this if it is based on a simple rounding up to the nearest 500 bytes. One send and receive that then moves on to the next action on the page is still going to be him. In this type of situation you can do several things -- first is always return MORE than one possibility, even when there is only one, reduce it to just one on the client. Secondly pad the number of REQUESTS, not just the data in the request.
Back to your question, compression makes padding something of a problem as the compressed size leaks. Pad as much as you can with valid data that is filtered out on the client, this will make any attempt to analyze it harder. Be sure that you don't leak padding time when doing your padding.
Finally -- this type of attack would have to be application and possibly user specific. Does anyone really care that much about your traffic?
Well you can create a filter to modify response before sending it to client. All you have to do is create a IHttpModule and intercept ASP.NET request pipeline. You can set Filter Stream, which will buffer your response, and at the end, ASP.NET pipeline will call "Close" method, that time you can modify buffer and send back to client.
To improve performance, you can also intercept Write method and write better logic instead of buffering whole response. But I will leave it on you.
First configure your web.config as,
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="RandomPaddingModule"
type="Namespace.RandomPaddingModule, AssemblyName"/>
</modules>
</system.webServer>
And add following code
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);
}
}
精彩评论