A workaround you can use to prevent this vulnerability is to
enable the <customErrors> feature of ASP.NET, and explicitly configure
your applications to always return the same error page - regardless of the
error encountered on the server. By mapping all error pages to a single error
page, you prevent a hacker from distinguishing between the different types of
errors that occur on a server.
Important: It is not enough to simply turn on CustomErrors
or have it set to RemoteOnly. You also need to make sure that all errors are
configured to return the same error page. This requires you to explicitly
set the “defaultRedirect” attribute on the <customErrors> section and
ensure that no per-status codes are set.
Enabling the Workaround on ASP.NET V1.0 to V3.5
If you are using ASP.NET 1.0, ASP.NET 1.1, ASP.NET 2.0, or
ASP.NET 3.5 then you should follow the below steps to enable
<customErrors> and map all errors to a single error page:
1) Edit your ASP.NET Application’s root Web.Config
file. If the file doesn’t exist, then create one in the root directory of
the application.
2) Create or modify the <customErrors> section of the
web.config file to have the below settings:
<configuration>
<system.web>
<customErrors mode="On" defaultRedirect="~/error.html" />
</system.web>
</configuration>
3) You can then add an error.html file to your application
that contains an appropriate error page of your choosing (containing whatever
content you like). This file will be displayed anytime an error occurs
within the web application.
Notes: The important things to note above is that
customErrors is set to “on”, and that all errors are handled by the defaultRedirect
error page. There are not any per-status code error pages defined – which
means that there are no <error> sub-elements within the
<customErrors> section. This avoids an attacker being able to
differentiate why an error occurred on the server, and prevents information
disclosure.
Enabling the Workaround on ASP.NET V3.5 SP1 and ASP.NET
4.0
If you are using ASP.NET 3.5 SP1 or ASP.NET 4.0 then you
should follow the below steps to enable <customErrors> and map all errors
to a single error page:
1) Edit your ASP.NET Application’s root Web.Config
file. If the file doesn’t exist, then create one in the root directory of
the application.
2) Create or modify the <customErrors> section of the
web.config file to have the below settings. Note the use of
redirectMode=”ResponseRewrite” with .NET 3.5 SP1 and .NET 4.0:
<configuration>
<system.web>
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/error.aspx" />
</system.web>
</configuration>
3) You can then add an Error.aspx to your application that
contains an appropriate error page of your choosing (containing whatever
content you like). This file will be displayed anytime an error occurs
within the web application.
4) We recommend adding the below code to the Page_Load()
server event handler within the Error.aspx file to add a random, small sleep
delay. This will help to further obfuscate errors.
VB Version
Below is a VB version of an Error.aspx file that you can
use, and which has a random, small sleep delay in it. You do not need to
compile this into an application – you can optionally just save this Error.aspx
file into the application directory on your web-server:
<%@ Page Language="VB" AutoEventWireup="true" %>
<%@ Import Namespace="System.Security.Cryptography" %>
<%@ Import Namespace="System.Threading" %>
<script runat="server">
Sub Page_Load()
Dim delay As Byte() = New Byte(0) {}
Dim prng As RandomNumberGenerator = New RNGCryptoServiceProvider()
prng.GetBytes(delay)
Thread.Sleep(CType(delay(0), Integer))
Dim disposable As IDisposable = TryCast(prng, IDisposable)
If Not disposable Is Nothing Then
disposable.Dispose()
End If
End Sub
</script>
<html>
<head runat="server">
<title>Error</title>
</head>
<body>
<div>
Sorry - an error occured
</div>
</body>
</html>
C# Version
Below is a C# version of an Error.aspx file that you can
use, and which has a random, small sleep delay in it. You do not need to
compile this into an application – you can optionally just save it into the
application directory on your web-server:
<%@ Page Language="C#" AutoEventWireup="true" %>
<%@ Import Namespace="System.Security.Cryptography" %>
<%@ Import Namespace="System.Threading" %>
<script runat="server">
void Page_Load() {
byte[] delay = new byte[1];
RandomNumberGenerator prng = new RNGCryptoServiceProvider();
prng.GetBytes(delay);
Thread.Sleep((int)delay[0]);
IDisposable disposable = prng as IDisposable;
if (disposable != null) { disposable.Dispose(); }
}
</script>
<html>
<head runat="server">
<title>Error</title>
</head>
<body>
<div>
An error occurred while processing your request.
</div>
</body>
</html>