How to hide WebMethods unhandled exception stacktrace using Response.Filter

29 Nov

If you’re interested in the code shown in this post, right-click here, click Save As and rename to zip.

Here’s a nice trick how to manipulate outgoing ASP.NET Response. Specifically, in situations that you would like to provide global handling of errors, removing the stacktrace.

In case you are wondering why you would remove the stacktrace – it is considered a security issue for attackers to be able to review the stacktrace (

Consider this code, a web method raising an exception.

public static void RaiseException()
	throw new ApplicationException("test");

And the page code:

<asp:ScriptManager runat="server" EnablePageMethods="true" />
<input type="button" value="Go" id="btn" />
	window.onload = function () {
		var btn = document.getElementById('btn');
		btn.onclick = function () {
			PageMethods.RaiseException(function () { }, function (error) {

Running this code as is and clicking the button would result with a JSON response that includes the error message, exception type and stacktrace:


To remove the stacktrace it is possible to use ASP.NET’s Response.Filter. The Filter property is actually a Stream that allows us to override the default stream behavior. In this particular case the custom stream is simply a wrapper. All the stream methods except the method Write() will simply invoke the original stream’s methods.

public class ExceptionFilterStream : Stream
    private Stream stream;
    private HttpResponse response;
    public ExceptionFilterStream(HttpResponse response)
        this.response = response; = this.response.Filter;

    public override bool CanRead { get { return; } }
    public override bool CanSeek { get { return; } }
    public override bool CanWrite { get { return; } }
    public override long Length { get { return; } }
    public override long Position { get { return; } set { = value; } }
    public override void Flush() {; }
    public override int Read(byte[] buffer, int offset, int count) { return, offset, count); }
    public override long Seek(long offset, SeekOrigin origin) { return, origin); }
    public override void SetLength(long value) {; }

    public override void Write(byte[] buffer, int offset, int count)
        if (this.response.StatusCode == 500 && this.response.ContentType.StartsWith("application/json"))
            string response = System.Text.Encoding.UTF8.GetString(buffer);

            var serializer = new JavaScriptSerializer();
            var map = serializer.Deserialize<Dictionary<string, object>>(response);
            if (map != null && map.ContainsKey("StackTrace"))
                map["StackTrace"] = "Forbidden";

                response = serializer.Serialize(map);
                buffer = System.Text.Encoding.UTF8.GetBytes(response);
      , 0, buffer.Length);

        }, offset, count);

Note that all the methods simply call the original stream’s methods. Points of interest:

  • Line 8: Apparently if you don’t call the original Filter property “on time” you will run into an HttpException: “Exception Details: System.Web.HttpException: Response filter is not valid”.
  • Line 23: From this point on the customization of the Write() is up to you. As can be seen, I selected to intercept error codes of 500 (“internal server errors”) where the response are application/json. The code simply reads the text, deserializes it and if the key “StackTrace” appears, replace its value with the word “Forbidden”. Note that there’s a return statement following this replacement so the code does not proceed to line 41. In all other cases, the original stream’s Write() kicks-in (Line 41).

Finally, we need to actually use the custom Stream. I used Application_BeginRequest in Global.asax like so:

<script runat="server">

    protected void Application_BeginRequest(Object sender, EventArgs e)
        Response.Filter = new ExceptionFilterStream(Response);


The result:

Obviously you can use this technique to manipulate outgoing responses for totally different scenarios.

Leave a comment

Posted by on 29/11/2015 in Software Development


Tags: , , ,

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: