We’re hiring


The Information Security Tools Team at Microsoft is hiring. Oh yes, your dream could come true, you could work with me …

We have an opening for a Senior SDE to help us develop software to support Microsoft’s information security program, analysing everyone else’s software for potential flaws and helping mitigate, manage and analyse. Mad SQL and OLAP skillz will put you at the top of the queue.

You can either apply though the link above, or email me your résumé/CV to my work email, bdorrans@microsoft.com (which has until now been spam free *sigh*. I promise not to ask how you move Mount Fuji.

author: Barry Dorrans | posted @ Friday, August 27, 2010 11:07 AM | Feedback (0)

Going to the MVP summit next year? Why not sign up with GeekGive?


The unemployment rate in Washington State as of May 2010 was 9.1%, and while this is slightly lower than the national average, there are still a great number of people going hungry. You can help! On Saturday, February 26th, the Saturday before the MVP Summit, GeekGive is organizing volunteers to work with Northwest Harvest in the Seattle Area. Join MVPs from around the world in this unique opportunity to give back to the community, network with your peers, and enjoy the Seattle area a bit more.

You can sign up online – I have.

author: Barry Dorrans | posted @ Monday, July 26, 2010 6:33 AM | Feedback (1)

Upcoming AntiXSS surrogate support


Historically AntiXSS has had problems with surrogates (go on, make the baby jokes, I’ll wait). Unicode surrogates are a way of combining two characters to enable the character range in UTF16 to go beyond 0xFFFF. Characters (or more accurately code points) between 0x000 and 0xFFFF made up the Basic Multilingual Plane however the code points and tables within the BMP are pretty much all used up – so how do you get beyond this? Any code point beyond 0xFFFF is broken down into two characters, a high surrogate (which lies between DB800 and DBFF) and a low surrogate (between DC00 and DFFF). Un UTF16 a high surrogate must always be followed by a low surrogate and the values are then combined to map to a code point outside the BMP using the following formula,

0x10000 + (High Surrogate value - 0xD800) ×  400 + (Low Surrogate value - 0xDC00)

For example the high surrogate 0xDB00 followed by a low surrogate 0xDC00 maps to the 0x10000 code point, which is the Linear B Syllable.

When AntiXSS is configured it creates an array of all the encoded values after which it changes the array contents for each safe listed value to be null, so for basic UTF16 we have a jagged char array char[0xFFFF][]. The maximum length for the second dimension is 8 characters, to support the thetasym named entity. Obviously this has an impact on memory (roughly 1Mb), but the offset in speed when we only calculate once more than makes up for it. If I extended that array to support the full UTF32 range suddenly the memory footprint leaps to more than 25Mb.

So what will happen in the next drop is the code will combine the surrogate pairs and then calculate the character value each time, which is safer than just leaving the characters as is. This does mean you cannot mark any of the code tables outside of the base plane as safe as the memory impact would just be too high.

If AntiXSS encounters a high surrogate not followed by a low surrogate, or a low surrogate not preceded by a high surrogate it will throw an InvalidSurrogatePairException – this does mean if you’re blindly truncating or concatenating strings which could contain pairs and then encoding them you need to be aware that you may chop or create an invalid surrogate pair.

I’m tempted to refer to this as the James Hart release as it was him who expressed a need for all of this!

Technorati Tags:

author: Barry Dorrans | posted @ Friday, July 23, 2010 11:04 AM | Feedback (3)

Upcoming changes to AntiXSS


So this sprint I’ve been playing around with AntiXSS. This makes me very nervous, changing something that quite a few folks internally and externally depend on is a heavy burden! One of the most popular requests was “Can you support language X?” and now the answer is probably yes. I say probably, because we’re covering the UTF-16 code tables – if you wanted support for Byzantine Musical Notation (really, it exists) then you’re out of luck.

Now there’s a little problem in all of this – Unicode doesn’t have a concept of language, it has code tables. If you’re lucky your language will be self contained in a single code table, for example, the characters for Armenian are contained in the code table  between 0x0530 and 0x058F. If you’re unlucky then they may be scattered all over the place. There’s no easy way to take a language and decide what code tables it needs and languages can be a geo-political nightmare so I’m afraid you’re going to have to learn your Unicode tables. The next version will contain five enums which contain every Unicode code table. You can then combine the enum values to safe list your desired characters via

UnicodeCharacterEncoder.MarkAsSafe(LowerCodeCharts lowerCodeCharts,
                                   LowerMidCodeCharts lowerMidCodeCharts,
                                   MidCodeCharts midCodeCharts,
                                   UpperMidCodeCharts upperMidCodeCharts,
                                   UpperCodeCharts upperCodeCharts)

I apologise in advance for the horrible method signature, however marking code tables as safe should be a once per application initialisation call. Once you safe list your language its characters will be left alone and not converted into their &#xxxx; values, which gives a rather nifty speed boost (one of the major sticking points for using AntiXSS has been the performance hit). The default safe code tables are Basic Latin, C1 Controls and Latin1 supplement, Latin Extended A, Latin Extended B, Spacing Modifier Letters, IPA Extensions and Combining Diacritical Marks. Obviously the fun characters in Basic Latin, < > & and " are always going to be encoded to their entity values.

The safe list applies to all four *ML methods, HtmlEncode, HtmlAttributeEncode, XmlEncode and XmlAttributeEncode. HtmlAttribueEncode will also escape the space character and the apostrophe to their unicode character values, XmlEncode will change the apostrophe to &apos; and XmlAttributeEncode escapes the space and changes the apostrophe.

One added bonus of the new safe lists is that it will fix a problem with .NET 4.0, AntiXSS and UpdatePanels. If you replace the .NET 4.0 encoder with AntiXSS, as Phil detailed and then use update panels things can get a little confused -  the __EVENTVALIDATION field gets encoded, and not decoded, so / turns into &47; and all heck breaks loose. As the Basic Latin safe listing has been widened to leave more characters alone because they’re simply not dangerous the / character will remain a / and your update panels will work as expected.

Technorati Tags:

author: Barry Dorrans | posted @ Monday, July 19, 2010 11:35 AM | Feedback (0)

Another new inspector for the SRE, ResponseInspector


When I started off discussing where I would take the Security Runtime Engine with the Developer Security MVPs Raffaele Rialdi asked if there would be a way to inspect raw requests and responses. Whilst I can’t do requests, as I don’t see them until ASP.NET has parsed them I can do responses, via ASP.NET’s filter mechanisms so, despite him tagging someone else as me on Facebook I started to look at how best to do this and came up with  IResponseInspector. The response inspector works slightly differently to the other inspectors – by the time it’s called there is no longer a Request or Response object, instead there’s a byte[] stream, so the interface looks like this:

/// <summary>
/// Defines methods that must be implemented for response inspection.
/// </summary>
public interface IResponseInspector : ISecurityRuntimePlugIn
{
    /// <summary>
    /// Inspects an HTTP response for potential problems.
    /// </summary>
    /// <param name="response">A byte array containing the response to inspect.</param>
    /// <returns>An <see cref="IInspectionResult"/> containing the results of the inspection.</returns>
    /// <remarks>
    /// The response is writable - any changes made to it will be sent to the client.
    /// </remarks>
    IInspectionResult Inspect(ref byte[] response);
}

You’ll notice the response is passed in by reference, meaning you can change the contents of the response parameter and it will change the response before it is finally sent on to the requesting client. This opens up some interesting possibilities; when I was implementing this the technical news was reporting how Blippy, a social media site where you share what you’re buying (why on earth you would want to do that I have no idea) had in fact published credit cards which were then indexed by Google. It’s a pretty rare scenario where you would legitimately publish full credit card numbers, so implementing a Blippy proof response inspector seemed like a good example, so ….

using System;
using System.ComponentModel.Composition;
using System.Text;
using System.Text.RegularExpressions;

using SecurityRuntimeEngine;

/// <summary>
/// A response inspector to halt any response which contains a credit card number.
/// </summary>
[Export(typeof(IResponseInspector))]
public sealed class CreditCardResponseInspector : IResponseInspector, IConfigurablePlugIn
{
    /// <summary>
    /// The configuration section name,
    /// </summary>
    private const string ConfigSectionName = "sreCCInspectorSettings";

    /// <summary>
    /// A regular expression for credit card number detection.
    /// </summary>
    /// <remarks>
    /// Original regex sourced from http://www.regexlib.com/REDetails.aspx?regexp_id=1288
    /// </remarks>
    private readonly Regex cardRegex = new Regex(@"/*(^|[^0-9])((4[0-9]|5[12345]|6[0245])[0-9]{2}[^0-9]?[0-9]{4}[^0-9]?[0-9]{4}[^0-9]?[0-9]{4}|3[47][0-9]{2}[^0-9]?[0-9]{6}[^0-9]?[0-9]{5})([^0-9]|$)/*", 
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.Multiline); /// <summary> /// The encoding for the response. /// </summary> private readonly UTF8Encoding encoding = new UTF8Encoding(); /// <summary> /// Internal, strongly typed settings. /// </summary> private CreditCardInspectorSettings internalSettings = new CreditCardInspectorSettings(); /// <summary> /// Gets the list of excluded paths for the plugin. /// </summary> /// <value>The list of excluded paths for the plugin.</value> public ExcludedPathCollection ExcludedPaths { get { return this.internalSettings == null ? null : this.internalSettings.ExcludedPaths; } } /// <summary> /// Gets the configuration section name for the plug-in. /// </summary> /// <value>The configuration section name for the plug-in.</value> public string ConfigurationSectionName { get { return ConfigSectionName; } } /// <summary> /// Gets or sets the settings for the plug-in. /// </summary> /// <value>The settings for the plug-in.</value> public BasePlugInConfiguration Settings { get { return this.internalSettings; } set { this.internalSettings = value as CreditCardInspectorSettings; } } /// <summary> /// Inspects an HTTP response for potential problems. /// </summary> /// <param name="response">A byte array containing the response to inspect.</param> /// <returns> /// An <see cref="IInspectionResult"/> containing the results of the inspection. /// </returns> /// <remarks> /// The response is writable - any changes made to it will be sent to the client. /// </remarks> public IInspectionResult Inspect(ref byte[] response) { try { string responseAsString = this.encoding.GetString(response); Match match = this.cardRegex.Match(responseAsString); if (match.Success) { return new ResponseInspectionResult(); } } catch (DecoderFallbackException) { } catch (ArgumentException) { } return new ResponseInspectionResult(InspectionResultSeverity.Continue); } }

What we’re doing is taking the response byte array in our Inspect method, converting it to a string and then running a regular expression over it to look for patterns that may be a credit card. The regular expression took some work, and lots of emails on one of the internal security mailing lists and raised eyebrows and questions of “Exactly why are you wanting this?” (note to self “For evil” is not a good response to that question. The response needs to be converted into a string because regular expressions don’t run over byte arrays. If the regular expression finds a match a ResponseInspectionResult() is created, which has a default InspectResultSeverity of halt. When the SRE detects that return value it stops processing by throwing an exception and the page is never rendered. Of course this may be too severe for your needs, instead you could use the regular expression to replace credit card strings with XXXX-XXXX-XXXX-XXXX or some such and then use the SRE’s logging to log what happened, assuming of course you check your logs …

You’ll see this in the next code drop, along with a major change to AntiXSS which will allow you to safe list languages tables from the Unicode UTF-8 specification. Safe listing the languages your web site expects means that the characters in the language ranges will not be converted into their hex equivalents, speeding up encoding. Dangerous characters such as < > " and & will always be encoded, as you would expect.

In the next code drop you’ll see a bunch of sample inspectors which may give you some ideas of your own … and I have a couple of interesting ones to come myself …

author: Barry Dorrans | posted @ Wednesday, July 14, 2010 6:35 PM | Feedback (0)

The SRE Preview is now available on CodePlex


The WPL site on CodePlex now has the May CTP code only release for the Web Protection Library and a Word document introducing the new extensibility points for the Security Runtime Engine.

I haven’t released binaries because it’s just a preview, it is in no way ready for production and I want to discourage you even thinking of that. So why did I make the source available? Simple – feedback. This represents a rewrite of the Security Runtime and a new way for you to easily write plug-ins for it. Rather than simply decide what’s best for our users I wanted to throw out our ideas and thoughts early and give you the change to download, play and comment/rave/rant about what I’ve done and where I want to go with it.

So what’s missing? Well there are no tests – they do exist, but I’m not ready to publish them yet, but they will come. There are no inspectors or logging plug-ins – because I want you to work through the tutorial, then look through the code, think about how you would use them or write your own rather than use the ones that will come as part of the released WPL as a starting template.

For the next sprint we’ll probably set the SRE to one side and work on all those outstanding AntiXSS encoding bugs (although I did address one – you can now pass a flag to HtmlEncode indicating if you want to use named entities) and take a look at what we can do with the HTML sanitization to try to get it to meet your needs. This sprint will probably see the start of a rewrite of how encoding works in order to make it more maintainable as we move forward, however this obvious needs to be done carefully and code reviewed to make sure I don’t open any security holes. The sprint after that will be gathering your feedback and looking at any changes we need to make to the SRE because of it, then I aim to deliver a beta and after that a full blown binary release.

So please have a play and do leave feedback on the WPL discussion forum on CodePlex.

author: Barry Dorrans | posted @ Thursday, May 27, 2010 6:11 PM | Feedback (0)

Further work on WPL PlugIns


(And yes, I did mean PlugIns – darned FXCop rules)

After a couple of weeks of experimentation with code I think I have the plug-in model complete now. As suggested by Travis in the comments on a previous post as many parameters as possible are now using System.Web.Abstractions. Right now there are three main interfaces:

/// <summary>
/// Defines methods that must be implemented for request inspection.
/// </summary>
public interface IRequestInspector : ISecurityRuntimePlugIn
{
    /// <summary>
    /// Inspects an HTTP request for potential problems.
    /// </summary>
    /// <param name="request">The <see cref="HttpRequestBase"/> to inspect.</param>
    /// <returns>An <see cref="IInspectionResult"/> containing the results of the inspection.</returns>
    IInspectionResult Inspect(HttpRequestBase request);
}
/// <summary>
/// Defines methods that must be implemented for response header inspection.
/// </summary>
public interface IResponseHeaderInspector : ISecurityRuntimePlugIn
{
    /// <summary>
    /// Inspects an HTTP response's headers for potential problems.
    /// </summary>
    /// <param name="request">The original <see cref="HttpRequestBase"/> for this response.</param>
    /// <param name="response">A <see cref="HttpResponseBase"/> to inspect.</param>
    /// <returns>An <see cref="IInspectionResult"/> containing the results of the inspection.</returns>
    /// <remarks>
    /// <para>This method is called at the point in the ASP.NET pipeline where you may still edit response headers
    /// via <see cref="HttpResponse.AppendHeader"/>.</para>
    /// <para>Accessing the <see cref="HttpResponse.Headers"/> property is only supported with the IIS 7.0 integrated pipeline mode 
    /// and at least the .NET Framework 3.0. When you try to access the Headers property and either of these two conditions 
    /// is not met, a PlatformNotSupportedException is thrown.</para>
    /// </remarks>
    IInspectionResult Inspect(HttpRequestBase request, HttpResponseBase response);
}
/// <summary>
/// Defines methods that must be implemented for page inspection.
/// </summary>
public interface IPageInspector : ISecurityRuntimePlugIn
{
    /// <summary>
    /// Inspects an HTTP page for potential problems.
    /// </summary>
    /// <param name="page">The <see cref="Page"/> to inspect.</param>
    /// <returns>An <see cref="IInspectionResult"/> containing the results of the inspection.</returns>
    IInspectionResult Inspect(Page page);
}

You can also see these interfaces also rely on ISecurityRuntimePlugin, which looks like this:

/// <summary>
/// Defines methods and properties that must be implemented for any plugin to the Security Runtime engine.
/// </summary>
public interface ISecurityRuntimePlugIn
{
    /// <summary>
    /// Gets the list of excluded paths for the plugin.
    /// </summary>
    /// <value>
    /// The list of excluded paths for the plugin.
    /// </value>
    ExcludedPathCollection ExcludedPaths
    {
        get;
    }
}

Each and every plug-in must support an exclusion method for paths. Now I’m aware that rewriters complicate things – attribute based exclusion is not going away but path based exclusion will allow you to wrap the SRE and any plug-ins around sites for which you do not have the source for or do not have the time to edit the source to exclude by attributes.

You may notice there’s no raw inspectors for requests or responses – they’re in my list of things to do, but as none of the customer requests need them right now they’ll probably turn up in the next sprint. One of the feature requests for this sprint was ClickJack protection, by always adding the X-FRAME-OPTIONS header to every response. So this becomes rather easy to implement – I had bet Frank my PM I could do it in a couple of lines of code. It took about 5 lines because of the excluded path property. Darnit. The code for the simplest version of this looks something like

[Export(typeof(IResponseHeaderInspector))]
public sealed class ClickJackResponseHeaderInspector : IResponseHeaderInspector
{ public ExcludedPathCollection ExcludedPaths { get { return null; } } public IInspectionResult Inspect(HttpRequestBase request, HttpResponseBase response) { if (response != null) { response.AppendHeader("X-FRAME-OPTIONS", "DENY"); } return new ResponseInspectionResult(InspectionResultSeverity.Continue); } }

There’s none of the usual hoops to jump through to write an HttpModule because you’re not writing one – the SRE is the module and will take of running your code within the appropriate ASP.NET pipeline events, which is why there’s that interesting [Export] attribute - the WPL plug-in functionality is provided by the Managed Extensibility Framework with is part of .NET 4.0 but is also available on CodePlex for .NET 3.5. Given there’s so little code to write (for something this simple anyway) your plug-ins should be very testable (and I’m planning to publish the unit tests for the SRE when we actually release). Of course a simple example should as the ClickJack inspector is unrealistic. Really you would want it to be configurable, after all you can set the header to one of two values – so in reality it’s a little bigger as there is another interface to implement, IConfigurablePlugIn (the name may change):

/// <summary>
/// Defines methods and properties that must be implemented for plugin configuration.
/// </summary>
public interface IConfigurablePlugIn
{
    /// <summary>
    /// Gets the configuration section name for the plugin.
    /// </summary>
    string ConfigurationSectionName
    {
        get;
    }

    /// <summary>
    /// Configures the current instance of the plugin.
    /// </summary>
    /// <param name="configurationSection">The configuration section for the module.</param>
    void Configure(ConfigurationSection configurationSection);
}

This interface requires you to expose a section name for your configuration section and will then extract that section during the SRE initialization and pass it down to your plug-in to do with as you please. This approach lends itself to more testability instead of the usual approach to writing custom configuration sections where you assume the section name exists and hard code it into your ConfigurationSettings class. You may be wondering why I didn’t put the ExcludedPaths property in that interface – there was a couple of reasons, 1) I wanted to enforce its availability for all plug-ins and 2) you may not want to source the path exclusions from a configuration file, you may want them from somewhere else, for example a SQL database. A settings class for the ClickJack inspector looks as follows

internal sealed class ClickJackInspectorSettings : BasePlugInConfiguration
{
    /// <summary>
    /// The property name for the plugin directory attribute.
    /// </summary>
    private const string HeaderValueProperty = "headerValue";

    /// <summary>
    /// An instance of the internal settings.
    /// </summary>
    private static ClickJackInspectorSettings internalSettings;

    /// <summary>
    /// Gets the settings.
    /// </summary>
    /// <value>The configuration settings settings.</value>
    public static ClickJackInspectorSettings Settings
    {
        get
        {
            return internalSettings ?? (internalSettings = new ClickJackInspectorSettings());
        }
    }

    /// <summary>
    /// Gets the header value to insert.
    /// </summary>
    /// <value>The header value to insert.</value>
    [ConfigurationProperty(HeaderValueProperty, IsRequired = false, DefaultValue = ClickJackHeaderValue.Deny)]
    public ClickJackHeaderValue HeaderValue
    {
        get
        {
            return (ClickJackHeaderValue)this[HeaderValueProperty];
        }
    }

    /// <summary>
    /// Gets the excluded paths for the plugin.
    /// </summary>
    /// <value>The excluded paths for the plugin.</value>
    [ConfigurationProperty(DefaultExcludedPathsProperty, IsDefaultCollection = true, IsRequired = false)]
    public override ExcludedPathCollection ExcludedPaths
    {
        get
        {
            return this[DefaultExcludedPathsProperty] as ExcludedPathCollection;
        }
    }

    /// <summary>
    /// Configures from configuration section.
    /// </summary>
    /// <param name="configurationSection">The configuration section.</param>
    public override void ConfigureFromConfigurationSection(ConfigurationSection configurationSection)
    {
        internalSettings = configurationSection as ClickJackInspectorSettings;
    }
}

You can see there’s a base class available, BasePlugInConfiguration – this provides ConfigureFromConfigurationSection, which is called for IConfigurablePlugins, and helps provides DefaultExcludedPathsProperty, which is the same string literal used for the SRE configuration excluded paths so you can use the same configuration syntax if you wish. So now our configurable plug-in looks like this

[Export(typeof(IResponseHeaderInspector))]
public sealed class ClickJackResponseHeaderInspector : IResponseHeaderInspector, IConfigurablePlugIn
{
    private const string ConfigSectionName = "sreClickJackPluginSettings";

    public ExcludedPathCollection ExcludedPaths
    {
        get
        {
            return ClickJackInspectorSettings.Settings.ExcludedPaths;
        }
    }

    public string ConfigurationSectionName
    {
        get
        {
            return ConfigSectionName;
        }
    }

    public IInspectionResult Inspect(HttpRequestBase request, HttpResponseBase response)
    {
        if (response != null)
        {
            response.AppendHeader("X-FRAME-OPTIONS", ClickJackInspectorSettings.Settings.HeaderValue == ClickJackHeaderValue.SameOrigin ? "SAMEORIGIN" : "DENY");
        }

        return new ResponseInspectionResult(InspectionResultSeverity.Continue);
    }

    public void Configure(ConfigurationSection configurationSection)
    {
        if (configurationSection != null)
        {
            ClickJackInspectorSettings settings = new ClickJackInspectorSettings();
            settings.ConfigureFromConfigurationSection(configurationSection);
        }
    }
}

That’s not too bad – if only I could have static properties or methods on interfaces or abstract classes I could reduce the code even further. Oh and I’d like a pony. Hopefully you can see where I’m going with this, I know at least one MVP is champing at the bit to write his own plugin. We’re deciding when/how to let you have a look at this – I’m leaning towards pushing the code onto CodePlex at the end of the sprint so you can download and play, but I may hold off a binary release until we think we’re good to go. As usual all thoughts/feedback welcome and all of the above may change drastically – but I really hope not.

author: Barry Dorrans | posted @ Monday, May 03, 2010 5:56 PM | Feedback (2)

Book Errata – Erratum #3


A couple of mistakes in chapter 8;

On page 197 the command to create a user within a database needs more explanation, so the whole paragraph should read

Adding a user to a database

Just because a login exists and can connect to SQL Server it doesn’t gain access to any databases. You must first grant an account access to the database. You can do this with the following SQL command:

USE [exampleDatabase]
GO
CREATE USER Olle FOR LOGIN Olle;
GO

This command creates a user within the database it is run in, in this example you first switch to the database exampleDatabase and then create a user Olle within for the SQL login account Olle. The user you create in a database does not have to have a name that matches with the actual login. If you want to create a user for a Windows login already granted access to SQL then you use the full Windows login details in the command, for example

CREATE USER NetworkService FOR LOGIN [Puck\Network Service];

This command creates a user NetworkService for the Network Service account on the machine Puck, assuming you have already granted that Windows account access to the SQL server as described previously in “Connecting without Passwords”. You can use square brackets, [ and ] to enclose user names or account names if they contain spaces.

However adding a user to a database is only the first step, these new user accounts cannot do anything without some further work.

On page 199 I got the permissions order the wrong way around; the paragraphs after the create command should read

As you can imagine, salary is sensitive data, and you would not want to allow anyone who has not been authorized to view this data. If you cannot use stored procedures, you can use views to limit access. First, you remove permissions on the table itself from everyone in the Public role using the following command:

DENY SELECT ON employee TO Public

Then you specifically grant table permissions to those who are allowed access (the Accounting role, for example, for ad-hoc reporting) using the following command:

GRANT SELECT ON employee TO Accounting

Technorati Tags: ,,

author: Barry Dorrans | posted @ Tuesday, April 27, 2010 2:59 PM | Feedback (1)

On facebook’s lack of privacy, buzz et al.


Quick thought - I'm beginning to think this sharing of PI is like free love in the 60s. Those of us who are older just don't understand it.

And I’ll bet, just like free, love in 10 years time some of the practitioners will be stuck with consequences they can never get rid of.

author: Barry Dorrans | posted @ Monday, April 26, 2010 6:21 PM | Feedback (0)

The Web Protection Library, plugins and naming


So now our fit and finish sprint is finished (my PM, Frank, has published the results which demonstrate that, well, fit and finish is never, errr, finished) I’ve been doing some thinking and experimenting. Two things came out of the MVP summit this year, 1) we want logging which isn’t the Enterprise Library and 2) we want to write our own WPL plugins (more specifically a particular Developer Security MVP wanted to write a SQL Injection detector for MySQL).

This week was scheduled to be a lazy week, as we work around planning meetings for sprint 2 so I started experimenting with suitable interfaces and with MEF. MEF is incredibly easy, although having an accent does mean your colleagues give you strange looks when you saying you’re playing with MEF and start recommending drug counselors. Mistaken hearing and the rest of the meth jokes aside I’m thinking of having the following interfaces available for plugins

  • IRequestInspector
  • IResponseInspector
  • IPageInspector
  • ILogger

The inspectors would look something like the following;

/// <summary>
/// Defines methods that must be implemented for request inspection.
/// </summary>
public interface IRequestInspector
{
    /// <summary>
    /// Inspects an HTTP request for potential problems.
    /// </summary>
    /// <param name="request">The <see cref="HttpRequest"/> to inspect.</param>
    /// <returns><c>true</c> if further processing of this request should stop, otherwise <c>false</c>.</returns>
    bool Inspect(HttpRequest request);
}
/// <summary>
/// Defines methods that must be implemented for page inspection.
/// </summary>
public interface IPageInspector
{
    /// <summary>
    /// Inspects an HTTP page for potential problems.
    /// </summary>
    /// <param name="page">The <see cref="Page"/> to inspect.</param>
    /// <returns><c>true</c> if further processing of this page should stop, otherwise <c>false</c>.</returns>
    bool Inspect(Page page);
}
/// <summary>
/// Defines methods that must be implemented for response inspection.
/// </summary>
public interface IResponseInspector
{
    /// <summary>
    /// Inspects an HTTP response's headers for potential problems.
    /// </summary>
    /// <param name="request">The original <see cref="HttpRequest"/> for this response.</param>
    /// <param name="response">A <see cref="HttpResponse"/> to inspect.</param>
    /// <returns><c>true</c> if further processing of this response should stop, otherwise <c>false</c>.</returns>
    /// <remarks>
    /// <para>This method is called at the point in the ASP.NET pipeline where you may still edit response headers
    /// via <see cref="HttpResponse.AppendHeader"/>.</para>
    /// <para>Accessing the <see cref="HttpResponse.Headers"/> property is only supported with the IIS 7.0 integrated pipeline mode 
    /// and at least the .NET Framework 3.0. When you try to access the Headers property and either of these two conditions 
    /// is not met, a PlatformNotSupportedException is thrown.</para>
    /// </remarks>
    bool InspectHeaders(HttpRequest request, HttpResponse response);

    /// <summary>
    /// Inspects an HTTP response for potential problems.
    /// </summary>
    /// <param name="request">The original <see cref="HttpRequest"/> for this response.</param>
    /// <param name="response">A <see cref="HttpResponse"/> to inspect.</param>
    /// <returns><c>true</c> if further processing of this response should stop, otherwise <c>false</c>.</returns>
    bool InspectBody(HttpRequest request, HttpResponse response);
}

Having a single HttpModule for the WPL SRE means I can perform some better caching, centralise the exclusion code and at some point look at parallelising some of the checks via PLinq.

However there are a couple of things I’m dithering about; the first is the interface naming – Inspector does not seem right as they can stop processing of the request or the response or change the request, the response or any ASP.NET WebForms page – but WCF has MessageInspectors which can also stop processing or message with the request or the response. Inspector seems a little passive, Inquisitor seems, well, more amusing. Twitter suggestions including IGatekeeper (or should that be IGateKeeper), ISentinal, IDemiGod (thanks for that Andrew *grin*), ISecurityInspector, IFilter, IIntermediate, IMonitor and carrying on the inquisitor theme, ITorqemada (Andrew again). The other thing I’m mulling over is the return value, should it be true to indicate processing should halt (and an exception thrown) because a problem was found or should it be true to indicate everything is fine and processing should continue.

I am enjoying MEF though – it’s incredibly easy, works in medium trust and means I can support multiple loggers and remove the dependency on the Enterprise Library to boot. It does have a drawback though, I should really compile two versions, one for 3.5 using the CodePlex source for MEF (which is still in beta mode) and one for 4.0 where MEF is built into the framework and thus remove an unnecessary assembly. As I was going to compile a specific 4.0 version of the AntiXSS library to support IHtmlString it’s not much of an extra hardship. Now if I could automate it …

So here’s your first chance to influence the plugin model for WPL – speak now or forever hold your peace :)

Technorati Tags: ,,,

author: Barry Dorrans | posted @ Wednesday, April 21, 2010 10:34 PM | Feedback (12)