Monday 13 December 2010

Configuration Transforms in ASP.Net Web Deployment Projects

In a recent web application project, we have many different environments where we need to regularly deploy our application such as 'development', 'testing', 'staging', and 'live'. For most, the standard publishing in Visual Studio 2010 works wonders, and I have been giddily using new build configurations and publishing profiles with fantastic results. In short, I just choose the build configuration to match the environment, choose the corresponding publishing profile, and hit go... (it used to take me ages to copy files across, change configuration settings etc...)

One problem I had with this was for the live deployment. Although the configuration transforms were working great for publishing, for the live environment we also wanted to apply the minify tasks to the CSS and JavaScript files (See my other post on post build tasks and minification).

Web Deployment Projects
Currently, web deployment projects don't apply the configuration transforms, so I had to find another way to apply this. Since I already had post build tasks in place for the minification, I figured this was the way to go to apply the transformations. After finding this question I was almost there.

Using the above post, I was able to identify the task used to transform the configuration files in the V10.0 publishing assembly. Unfortunately once the task has run it leaves the files locked by the process so in addition I had to add a couple of additional tasks to copy the files to a temporary location first, and copy back across to the project folder when done. Once this was complete I setup a task to delete the redundant files for other build configurations and remove the temporary file if possible (the temp file appeared to still be locked by the process so may not delete in some cases. However, this directory will be overwritten by the standard process the next time you build anyway).

The build task configuration is below. If you right click on the web deployment project and select 'Open Project File' you should be able to paste this in at the bottom (I have excluded the Minify task for brevity).

<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />

<Target Name="AfterBuild">
<!-- Copy the config transform file to a temp directory (to stop it being locked by the process) -->
<Copy SourceFiles="$(Configuration)\web.$(Configuration).config" DestinationFolder="TempBuildDir\" />
<!-- Delete the config files from the deploy folder (to be replaced by the transformed files) -->
<ItemGroup><ConfigFiles Include="$(Configuration)\\**\*.*.config" /></ItemGroup>
<Delete Files="@(ConfigFiles)" />
<!-- Transform the copied files and write to the deploy folder -->
<TransformXml Source="$(SourceWebPhysicalPath)\Web.Config" Transform="TempBuildDir\Web.$(Configuration).config" Destination="$(Configuration)\Web.config" StackTrace="true" />
<!--Then remove the temp files folder (locked by the transform process...)-->
<RemoveDir Directories="TempBuildDir\" ContinueOnError="true" />
</Target>

Conclusion
This enables the web deployment project to build the website with the correct configuration AND minified JavaScript and CSS files. I can then build the package and distribute via the web deploy tool in IIS7.

Monday 6 December 2010

Penetration Test Checklist

During a recent web application release, we subjected our new site to penetration testing. Some of the things they came back with were the standard school boy errors (like failing to encrypt the cookies) but some were more interesting and that I haven't come across before. I will attempt to outline some below, more as a reference for my future self but if its any use for someone else then great.

Encrypting cookies
In the past, this has stung me because you can only include it when you have a certificate installed for the website (which meant I could only make the setting change after the certificate was installed and application deployed).

The easiest way to sort this is to include it in the configuration file, and with the new configuration transforms its easier than ever.

<authentication mode="Forms" xdt:Transform="Replace">
<forms loginUrl="~/Login.aspx"
defaultUrl="~/Default.aspx"
protection="All"
name="MyWebApp"
requireSSL="true" />
</authentication>

Clickjacking Mitigation
To help prevent clickjacking on pages that don't need to be framed, the new X-FRAME-OPTIONS header can be included to tell browsers not to allow framing. Again the easiest way to do this is through the web configuration file (this only applies to IIS7 within the system.webServer node)

<httpProtocol>
<customHeaders>>
<clear />
<add name="X-FRAME-OPTIONS" value="DENY" />
</customHeaders>
</httpProtocol>

Details of the different options to use can be seen in EricLaws blog combatting clickjacking with X-Frame-Options.

Server Http Header
For some reason, IIS loves to tell the world about itself for every page request. Although this doesn't necessarily pose a threat, it does make it easier for hackers to see which server you are using and narrow down the exploits to try against your website.

Various approaches can be taken to remove this, there is a free tool available from Microsoft called UrlScan which can block specific Http requests and works on IIS 5.1 and above. On IIS7 you can create a custom module (or build into your application) which replaces the Server header with your own. You can then include the custom module as a reference within your application and load it using the configuration file, or compile to an assembly in the GAC and apply to all applications on your server.

Remove 'X-AspNet-Version header'
ASP.Net by default serves up the http header X-AspNet-Version with the version information. Again although this doesn't pose a direct risk, it does give more information to potential hackers to let them focus on particular exploits.

To remove this header, just change the configuration file as follows:

<system.web>
<httpRuntime enableVersionHeader="false" />
</system.web>

This can be added to the configuration transform as before so it only applies to your live deployment, though personally I don't see a problem with just removing it for all environments.

Remove 'X-Powered-By'
Similar to the X-AspNet-Version header, ASP.Net sometimes (depending on the web server) serves an X-Powered-By http header which advertises that the application is ASP.Net (sometimes even if it isn't). To remove this in IIS6 there's a great post showing the steps required. For IIS7 you can use the configuration file (or do it manually following the steps on the link above). In the <system.webServer> node add the following:

<httpProtocol>
<customHeaders>
<clear />
<remove name="X-Powered-By" />
</customHeaders>
</httpProtocol>

X-AspNetMvc-Version
For ASP.Net MVC applications, an additional 'X-AspNetMvc-Version' header is included. This can be removed by adding the following code to the Application_Start event in the Global.asax file:

MvcHandler.DisableMvcResponseHeader = true;

HTTP OPTIONS
The OPTIONS method allows users to tell what sort of http options (GET/POST/etc...) are available without actually executing the action. Again this (some say) gives potential hackers more information about your application that can be exploited. To turn off the OPTIONS verb, you can deny access to unauthorised users with the <authorization> node.

<authorization>
<deny users="?" />
<deny verbs="OPTIONS" users="*" />
</authorization>

Conclusion
So that's it, of course your website will have to be properly secured using the usual development and configuration steps. These additions won't secure your website as such, but they will help to hide the extra information that can be used against you and your application.

Tuesday 26 October 2010

SqlMembershipProvider requires a database schema compatible with schema version 1

Whilst recently working with the ASP.Net Membership tools, I decided to script a copy of an existing database with the same Schema. This, I thought, would save a bit of time and get me working quickly. Unfortunately my application experienced the following error when calling the Membership.ValidateUser() method:

System.Configuration.Provider.ProviderException: The 'System.Web.Security.SqlMembershipProvider' requires a database schema compatible with schema version '1'. However, the current database schema is not compatible with this version. You may need to either install a compatible schema with aspnet_regsql.exe (available in the framework installation directory), or upgrade the provider to a newer version.

After a bit of research, I found the following blog post Schema Changes in the latest ASP.NET V2.0 bits which seemed to fit with what I was doing. Unfortunately running the aspnet_regsql.exe tool with the option to "Configure SQL Server for application services" didn't fix it for me.

After reading this forum post I looked (quite rightly) into the aspnet_SchemaVersions table. Sure enough the data was empty so I ran the tool trying various configurations detailed on the msdn page.

Using the visual studio command prompt, I ran the following command. This sets up the membership, role provider, and profile tables on a database called My_Membership located at localhost using integrated credentials:


aspnet_regsql.exe -E -S localhost -A mrp -d My_Membership

This successfully populated the data as expected. Unfortunately this didn't resolve my issue, I was still getting the exact same error!

After taking a step back and assessing what I had changed, I realised that the only actual difference was the application name (specified in the web config). With this in mind I was able to check through the database and noticed that there was an aspnet_Applications table with the old application name in (but not my new version). After adding in my new application name it all worked fine... Phew!

Typically, once I solved it I stumbled across this scott gu blog entry Always set the "applicationName" property when configuring ASP.NET 2.0 Membership and other Providers which goes through pretty much the same issue. If only I had seen that first.

In summary. If you get this error, check that the application name matches an entry in the aspnet_Applications table. Or better still, run through the proper setup steps...

Wednesday 6 October 2010

Web Deployment Project - Post Build Events

In my current setup, I have a few web deployment projects which I use to easily compile my ascx user controls for shared use within other projects. With this setup I have cascading references to core web controls projects, to more specifically used web controls projects up the references chain.

With each project comes a standard assembly for the code behind, and an assembly which contains the compiled ascx information. Note: I am using the 'Merge all control outputs to a single assembly' option within the web deployment project property pages.

To save me having to include all of the projects within my main visual studio solution, I copy the compiled assemblies for each of these projects into a shared dependencies folder which can be referenced by many of my web applications. It was getting a bit of a pain to copy the files across to the dependencies folder each time I wanted to roll out a change, so I wanted to find a way to automate the process.

Deployment Projects Post Build Tasks
Within the web deployment projects, you can specify tasks to be run after build, these can include all of the macros from the standard post build tasks (see Web Application Project->Properties->Build Events->Edit Post Build->Macros >>) plus some addition tasks specific to the web deployment projects.

It is well documented that you can copy the compiled website output from the deployment project to another location, however I didn't want the whole website, I needed the merged assembly only.

Using naming conventions, I set the merged assembly to match the parent assembly name but with a '.compiled' addition e.g. 'myWebAssembly.dll' for the main project and 'myWebAssembly.Compiled.dll' for the web deployment project output.

With this in mind, I was able to put together a quick rule to identify the assemblies I wanted to copy using wilcards.

<Target Name="AfterBuild">
<ItemGroup>
<MySourceFiles Include="$(OutputPath)\**\*myWebAssembly*.dll"/>
</ItemGroup>

<Copy SourceFiles="@(MySourceFiles)" DestinationFiles="@(MySourceFiles->'..\Dependencies\%(Filename)%(Extension)')" />
</Target>

Minify JavaScript and CSS files
Within my compiled web controls projects, many of the script and CSS files used are added as embedded resources. One of the problems with this was that I wanted to minify the files when used as a dependency, but keep the originals, comments included, for the project itself. Luckily I found this great blog entry on automatically compressing embedded resources that goes through how to minify the embedded resource files and replace the originals within the assembly itself.

Note: To view the project file, unload your current web application project, then right click and select edit [myproject].csproj

<Import Project="$(MSBuildExtensionsPath)\Microsoft\MicrosoftAjax\ajaxmin.tasks" />
<PropertyGroup>
<ResGenDependsOn>
MinifyJavaScript;
$(ResGenDependsOn)
</ResGenDependsOn>
</PropertyGroup>
<Target Name="MinifyJavaScript" Condition=" '$(ConfigurationName)'=='Release' ">
<Copy SourceFiles="@(EmbeddedResource)" DestinationFolder="$(IntermediateOutputPath)" Condition="'%(Extension)'=='.js'">
<Output TaskParameter="DestinationFiles" ItemName="EmbeddedJavaScriptResource" />
</Copy>
<AjaxMin JSSourceFiles="@(EmbeddedJavaScriptResource)" JsSourceExtensionPattern="\.js$" JsTargetExtension=".js" CssSourceFiles="@(CSS)" CssSourceExtensionPattern="\.css$" CssTargetExtension=".css" JSLocalRenaming="CrunchAll" />
<ItemGroup>
<EmbeddedResource Remove="@(EmbeddedResource)" Condition="'%(Extension)'=='.js'" />
<EmbeddedResource Include="@(EmbeddedJavaScriptResource)" />
<FileWrites Include="@(EmbeddedJavaScriptResource)" />
</ItemGroup>
</Target>

Unfortunately this step seemed to be ignored when building through the web deployment project. I got around this by creating separate post build tasks, one for the web application project, and one for the web deployment project. I changed the web deployment project to only be responsible for the merged assembly using the naming convention above:

<Target Name="AfterBuild">
<ItemGroup>
<MySourceFiles Include="$(OutputPath)\**\*myWebAssembly.Compiled*.dll"/>
</ItemGroup>

<Copy SourceFiles="@(MySourceFiles)"
DestinationFiles="@(MySourceFiles->'..\Dependencies\%(Filename)%(Extension)')" />
</Target>

Post Build Tasks
Once I had updated the after build task above to only target the merged assembly, I then needed a way to copy the assembly from the main web project, including the minified embedded resources.

I added the following post build task to the main web application project through (Web Application Project->Properties->Build Events->Edit Post Build)

COPY $(TargetPath) ..\..\Dependencies\$(TargetFileName) /Y

This copies the assembly built for the current project into the same dependencies folder, and is fired when building the web deployment project (through the build dependencies).

Conclusion
In the end, my projects are building the assemblies, minifying the JavaScript and CSS, and copying to the dependencies folder ready to be referenced by my other projects. Just a few less things to worry about, which is always good in my book.

Further Reading
MS Build Task Reference
VS 2005 Web Deployment Projects
FAQ in ASP.NET Setup & Deployment
Web Deployment Projects: copy build output to a network share
Minify JavaScript and CSS files as Embedded Resources

Tuesday 28 September 2010

*** HR originated: -2147024774 in Debug View

If any of you use Debug View and Visual Studio 2010, you may have noticed some random output such as:

[7160]
[7160] *** HR originated: -2147024774
[7160] *** Source File: d:\iso_whid\x86fre\base\isolation\com\copyout.cpp, line 1302
[7160]
[7160]
[7160] *** HR propagated: -2147024774
[7160] *** Source File: d:\iso_whid\x86fre\base\isolation\com\enumidentityattribute.cpp, line 144
[7160]
[7160]
[7160] *** HR originated: -2147024774
[7160] *** Source File: d:\iso_whid\x86fre\base\isolation\com\copyout.cpp, line 1302
[7160]
[7160]
[7160] *** HR propagated: -2147024774
[7160] *** Source File: d:\iso_whid\x86fre\base\isolation\com\enumidentityattribute.cpp, line 144
[7160]
[7160]
.... etc....

Turns out this is a known defect/ general annoyance with Visual Studio 2010. The current 'workaround' is to just add a filter to ignore its output. If you use Debug View a lot like I do then hit Ctrl + L and paste the following into the exclude list:

* HR*; *d:\iso_whid*

Friday 24 September 2010

AjaxControlToolkit MaskedEditExtender 2 Digit Date Issue

I have been using the AjaxControlToolkit for a few years now, and one of the most used controls (for me) has to be the Calendar extender. I usually implement this in addition to the Masked Edit Control to create a date input that you can either type into or select from the calendar.

Recently it was reported to me that one of my applications using this was not working, after investigation it turned out that the users were entering a 2 digit year into the date box. There is a 'Century' setting on the extender that is meant to deal with such scenarios but unfortunately it doesn't seem to work (though I failed to test this correctly and it also got through our entire testing team and UAT!).

A work item seems to have been logged on codeplex for this but despite the comments there doesn't seem to be a resolution. Though by what some of the guys have commented I think they have resolved it for themselves individually.

So, with no other option I set about fixing the issue for myself. First I downloaded the project from the codeplex site ajaxcontroltoolkit.codeplex.com

I had to updgrade the project since I'm working in Visual Studio 2010. The upgrade went OK but it complained about a missing file for ajax minifier tasks. I found a great blog post on this and installed the ajax javascript minifier (not really anything to do with Ajax, its just a JavaScript/CSS minifier).

The initial build got Error The "JSSourceFiles" parameter is not supported by the "AjaxMin" task. Verify the parameter exists on the task, and it is a settable public instance property.

Confusingly, I then found another version of the minifier here aspnet.codeplex.com/releases/view/40584. Installing this version (V4) seems to have sorted the issue.

Finally the project was building so I got into the code. First, I made the following change to the 'ConvFmtDate' function:

I removed the line:

while (Y.length < m_mask[this.get_CultureDateFormat().indexOf("Y")].length)
{
Y = "0" + Y;
}


And Added:


// If the year is 2 digit
if (Y.length <= 2)
{
// Get the current century
var century = this.get_Century().toString();

// Check that a value has been provided
if (!century)
{
// Otherwise get the current century
var currentDate = new Date();
century = currentDate.getYear().toString();
}

// Construct the year from the first 2 digits of the century and the yr entered
Y = century.substring(0, 2) + Y;
}


Unfortunately this solved only half of the problem. Putting in a 2 digit date would still pad with 0 (giving 0076 for 76) but once the text box regained focus the date would suddenly correct itself to 1976.

On investigation, there's an _onBlur() event that is fired. If the text has changed from the previous value it fires the _fireChanged() method (which creates the HTML event for change on the text box). Somewhere down the line this ends up calling the _GetDateElementText method.

Within the _GetDateElementText method, there's a piece of code that checks if the year value is less than 4 and pads it out with zeroes. Basically I needed to change this to do the same as the previous function.

I added a new method to the object to format the year string with either the century value set or the current century:


, FormatYearCentury: function (input)
{
// Get the current century
var century = this.get_Century().toString();

// Check that a value has been provided
if (!century)
{
// Otherwise get the current century
var currentDate = new Date();
century = currentDate.getYear().toString();
}

// Construct the year from the century and the yr entered
return century.substring(0, 4 - input.length) + input;
}


I then replaced the following lines (within the _GetDateElementText method):


if (Y4)
{
if (aux != "" && aux.length < 4)
{
while (aux.length < 4)
{
aux = "0" + aux;
}
m_arrDate[this.get_CultureDateFormat().indexOf("Y")] = aux;
}
}


With my updated get year method:


if (Y4)
{
if (aux != "" && aux.length < 4)
{
// Format the year with the requested or current century
aux = this.FormatYearCentury(aux);

m_arrDate[this.get_CultureDateFormat().indexOf("Y")] = aux;
}
}


I then went back to the ConvFmtDate function and replaced the new code with a call to my new FormatYearCentury function.


var Y = parseInt(m_arrDate[this.get_CultureDateFormat().indexOf("Y")], 10) + '';

// If the year is 2 digit or less
if (Y.length <= 2)
{
Y = this.FormatYearCentury(Y);
}


The only thing left to do was to build the sample website and copy the assembly to my project.

This may not be right, and to be honest I never enjoy digging around in other peoples code, especially with no comments. But it seems to work for me, and if it can save someone else the headache then its all good.

Monday 15 February 2010

Invalid character error from ScriptResource.axd

Ok so I had this weird 'Sys is undefined' error in ASP.Net Ajax client side scripts.

It was only happening in IE which was a bit frustrating and only for certain users so I had a sneaking suspicion that it was a client side issue.

Since this is such a generic error (loosely translated to 'it didn't work') there are many posts and suggestions about this plastered over the forums, unfortunately none that I found seemed to solve my problem so I had to resort to trying to figure it out for myself.

After tying myself in knots trying to work out what was going on I finally stumbled on the solution to my particular issue. It turned out that (for me anyway) it was a combination of a couple of problems. Firstly I had to turn off ScriptCombining on the ToolkitScriptManager (again it worked fine for me but not for all users), AND I also had to turn off script compression.

IE didn't seem to be able to decompress the scripts client side and so was bailing out at the first character.

For reference, to disable compression it was this quick entry in the web.config file.

<system.web.extensions>
<scripting>
<scriptResourceHandler enableCompression="false" enableCaching="true"/>
</scripting>
</system.web.extensions>

So, if IE is giving you random loading errors for your Ajax.Net scripts, try disabling compression, and/or turning off CombineScripts (its on by default so you will have to specifically set it to false).