Using the ASP.NET Health Monitoring Provider to Secure Your Application in the Case of an Attack
The ASP.NET Health Monitoring is a featured introduced in ASP.NET 2.0. Basically, you have your application raise web events – not to be confused with user interface events – and you configure rules that define, for a given event code or range of event codes, a time interval and a minimum and maximum number of occurrences, the health monitoring provider that the events will be routed to. The ASP.NET infrastructure already raises a number of these events, and you can also define your own events, and take the responsibility to raise them when appropriate. I won’t go into describing how the health monitoring works, there are several web sites that describe it, instead, I will talk about a different use.
The included providers operate in a passive way, they do things such as inserting the raised event into a database, send an email with its data, write an entry to the event log, etc:
Writes events to the Event Log |
||
Sends simple, unformatted email |
||
Sends template-formatted email |
||
Writes events to SQL Server |
||
Routes events to IIS 7 logging |
||
Writes events to the trace |
||
Routes events to WMI |
I want to have something different: whenever there are more than 5 failed logins in 1 minute, I assume an attack is under way, and the site is locked down. I understand this is an extreme decision, but there may be cases where this actually makes sense.
A WebAuthenticationFailureAuditEvent event is raised with event code AuditFormsAuthenticationFailure is raised when a failed authentication using Forms Authentication occurs. So, in order to fire a provider whenever there are 5 in a minute, we must add the following to the Web.config file:
1: <healthMonitoring>
2: <rules>
3: <add name="Failed Authentication Events" eventName="Failed Authentication Events" provider="Failed Authentication Provider" minInterval="00:01:00" minInstances="5" maxLimit="*" />
4: </rules>
5: <eventMappings>
6: <add name="Failed Authentication Events" startEventCode="4005" endEventCode="4005" type="System.Web.Management.WebAuthenticationFailureAuditEvent, System.Web.Management" />
7: </eventMappings>
8: </healthMonitoring>
As you can see, the start and end event codes are the same, we just want to fire this for the Forms Authentication login failed event.
The next thing is the implementation of the custom provider. What this will do is, when it is fired, will create a App_Offline.htm file on the root of the web site, thus effectively disabling any accesses. It’s easy to do:
1: public class FailedAuthenticationWebEventProvider : WebEventProvider
2: {
3: public override void Initialize(String name, NameValueCollection config)
4: {
5: //additional properties may be specified on the Web.config file
6: this.SomeAdditionalProperty = config["someAdditionalProperty"];
7:
8: config.Remove("someAdditionalProperty");
9:
10: base.Initialize(name, config);
11: }
12:
13: public String SomeAdditionalProperty
14: {
15: get;
16: set;
17: }
18:
19: public override void Flush()
20: {
21: }
22:
23: public override void ProcessEvent(WebBaseEvent raisedEvent)
24: {
25: //copy an existing template file to App_Offline.htm
26: String file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Offline.htm.bak");
27: File.Copy(file, Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "App_Offline.htm"));
28: }
29:
30: public override void Shutdown()
31: {
32: }
33: }
I included an additional property just for demonstrative purposes, it is not being used.
The final step is to register the new provider:
1: <healthMonitoring>
2: <providers>
3: <add name="Failed Authentication Provider" type="FailedAuthenticationWebEventProvider" someAdditionalProperty="someValue" />
4: </providers>
5: ...
6: </healthMonitoring>
And that’s it!