Introduction
Good web application antivirus solution needs to meet the following acceptance criteria:
- Real-time inbound and outbound file scanning
- Low latency, sub second performance
- Transparent, no need to make changes to your web app
- Requires no maintenance and is easy to setup
If you are using PaaS service such as Azure Web Apps your antivirus options are:
Option | Limitation | Installation Effort | Maintenance Effort | Cost | Meets criteria |
---|---|---|---|---|---|
Firewall with ICAP integration | Expense and ongoing maintenance | High | Medium | High | Yes |
Send files to the antivirus API from your app | Extra unnecessary development work | Medium/High | Medium | Low | No |
Background service that scans your files | Not real time, high possibility that viruses will be uploaded and downloaded | Medium | Low | Low | No |
Reverse proxy binary content forwarding | Scans only uploads | Medium | Low | Low | No |
IIS Filter (this solution) | PoC, not production ready | Medium | Low | Low | Yes |
In depth exploration of different antivirus options is beyond the scope this article.
IIS Filter Solution, inspired by ModSecurity
This article will only cover the interesting parts of the solution, if you have questions please do comment.
AVFilter, File Upload/Download Filter
Filtering uploads and downloads is relatively straight forward thanks to IIS http modules, read more about them here.
Filtering Uploads
Here is an example of how you can detect file upload inside the http module:
private void Context_PreRequestHandlerExecute(object sender, EventArgs e) { HttpApplication httpApplication = (HttpApplication)sender; HttpRequest httpRequest = httpApplication.Context.Request; if (httpRequest.RequestType == WebRequestMethods.Http.Post) { if (httpRequest.Files.Count != 0) { //Check files } } }Take a look at the full implementation here.
Filtering Downloads
When it comes to intercepting downloads it’s not as simple. Unfortunately outgoing traffic is written to the System.Web.HttpResponseStreamFilterSink and this stream is write only, so you can’t read it. So the only option that I and Google can think of is to proxy the outgoing stream. To make this happen I have created proxy class that changes write only steam to write/read stream, I have conveniently called it ReadWriteProxyStream, take a look at the implementation here.
Now that we have ReadWriteProxyStream class, we need to hook it up response filter, this is done when web request is first received:
private void Context_PreRequestHandlerExecute(object sender, EventArgs e) { HttpApplication httpApplication = (HttpApplication)sender; httpApplication.Context.Response.Filter = new ReadWriteProxyStream(httpApplication.Context.Response.Filter); }Take a look at the full implementation here.
Before web response is returned back to user we need to check it, we can do this by reading the stream:
private void Context_PreSendRequestContent(object sender, EventArgs e) { HttpApplication httpApp = (HttpApplication)sender; HttpResponse httpResponse = httpApp.Context.Response; if(httpResponse.ContentType == "application/octet-stream") { ReadWriteProxyStream filter = httpResponse.Filter as ReadWriteProxyStream; if (filter != null) { byte[] data = new byte[filter.Length]; filter.Position = 0; filter.Read(data, 0, data.Length); //Check files } } }Take a look at the full implementation here.
AVFileReceiver, Virus Scanning
For this proof of concept I have used Symantec virus scanner which comes with CLI, and to test it I have used EICAR test virus. EICAR test virus is harmless, it can’t actually do anything to your machine.
Symantec CLI will scan the specified file synchronously, so if the file is no longer there you know that it was not safe:
using (Process process = Process.Start( "C:\\Program Files (x86)\\Symantec\\Symantec Endpoint Protection\\DoScan.exe", "/ScanFile " + filePath)) { process.WaitForExit(); } if (!File.Exists(filePath)) throw new Exception("File was not safe!")Take a look at the full implementation here.
Final Thoughts
If you like this concept then please do share and rate this blog post. If this post will generate enough interest I will productionise this concept. However, if you just can’t wait, then here is what you need to do to make this production ready:
- Secure communication between AVFilter and AVFileReceiver
- Secure AVFileReceiver access (authentication, network security group, etc)
- Reduce VM maintenance through automation, use PowerShell files provided and place Cloud Service inside availability and upgrade group
- Reduce memory consumption by intercepting binary responses only
- Make debugging easier by logging exceptions
- Improve performance by forwarding binary content using MTOM encoding to the AVFileReceiver and remove AVFilter temp storage
- Improve scalability and responsiveness by using Async in the AVFileReceiver and AVFilter
No comments:
Post a Comment