Monthly Archives: August 2013

Test your website with multiple Internet Explorer versions

Testing a website with multiple Internet Explorer versions can be frustrating. Because IE is bound to the underlying OS you cannot install multiple IE versions side by side (which can be done with some other browsers), so you need other solutions.

IETester is a free tool which can simulate different IE versions in a single application. Although it supports Windows 8 and IE10 in desktop mode, it couldn’t reach the 1.0 version in years, and has some serious limitations.

The Expression SuperPreview was a good start from Microsoft to provide browser version comparison in a single app, but unfortunately the company stopped its development.

The Internet Explorer Developer Toolbar changes significantly. In IE11, which you can get with Windows 8.1, you find brand new F12 DevTools with new design and enhanced functionality, but unfortunately some features are missing from the compatibility tools. In previous versions – up to IE10 – there is a nice feature in the Dev Toolbar which you can use to change the Browser Mode:

IE10 Developer Toolbar: Browser Mode emulation

This looks nice and fine however there are some practical problems with it. First you can see a Document Mode selector next to it with similar options:

IE10 Developer Toolbar: Document Mode emulation

The difference between the two options is very important however it is not easy to find out what it is. If you don’t know, I strongly recommend reading the Testing Sites with Browser Mode vs. Doc Mode article from the IEBlog.

Another problem with the Browser Mode switch is that it not absolutely perfect. Although you can find most compatibility issues with it, I have seen several bugs and issues that were not spotted by using this switch but revealed only when the page was tested with IE8. To cut the long story short, the Browser Mode switch is not reliable.

Because of all these issues, the IE team decided to remove the Browser Mode switch from the new DevTools.  This is how the Emulation tab looks in IE11:

IE11 Developer Toolbar Emulation tab

As you can see there are some nice new features: you can switch between a Desktop and Windows Phone profile, you can preset not only the resolution but also the orientation, and you can emulate a GPS position as well. Regarding to the browser versions, there is a User Agent string liar option, and there is also a Document Mode switch (which can be Edge or Default), but you cannot switch to a previous browser version.

So what is the solution?

By clicking on the information icon, you can navigate to the Test Across Browsers page of the modern.IE website where you can find two solutions:

1. Microsoft partnered with the people at BrowserStack and offers 3 months free access to BrowserStack services. This offer is valid before January 10, 2014.

2. You can download a set of virtual machines. Just select your host operating system and virtualization platform and you can instantly download free Windows virtual machines preconfigured with the specified IE version:

modern-ie-vms

According to the current Microsoft view – which matches the state-of-art – using virtual machines (on your machine or in the cloud) is the most reliable solution to test your web application in previous IE versions. And now you have instant access to these VMs.

Regarding to the next versions of IE, the recommendation is to follow the web standards. A good start can be Rey Bango and Dave Methvin’s 20 tips for building modern websites while supporting old versions of IE article.

 

Technorati-címkék: ,,

Installing WSUS on Windows Server 2012

Installing Windows Server Update Services on Windows Server 2012 is not a complicated task in theory: because it is now a part of the OS, you even don’t have to download it, just click through the step-by-step installation wizard in Server Manager. If you don’t believe me, take a look at this step-by-step tutorial with screenshots.

For me it was not that easy. I’m obviously an install-failed-error magnet, but according to the tremendous amount of forum posts in the topic, I’m not alone. So here are my solutions.

A kind notice:
The solutions below worked for me, but use them on your own risk!

The server is a fresh WS 2012 install, promoted to a domain controller. I have not found any documentation that would state that this is a non-supported scenario.

NTFS permissions

According to the Prepare for Your WSUS Deployment TechNet article, the NT Authority\Network Service account must have Full Control permissions to the following folders, otherwise the WSUS Administration snap-in may fail:

  • %windir%\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files
  • %windir%\Temp

The beauty is that – as you can read in the doc – the first folder might not exist if IIS is not installed. WSUS requires IIS, and fortunately the installer is smart enough to install the Web Role as well. What’s more, it installs only those IIS components that are required to WSUS, so if you follow the “minimal install” principle, you probably want to let the WSUS installer setup IIS as well. But how to set the folder permissions before starting the installer, if the installer creates the folder?

Role Services

In a sunny Friday afternoon you may reach this step in the install wizard:

WSUS install wizard: Select role services

If you want to click all three components, be prepared to the following error message:

The following features cannot be installed on the same server: Database, WID Database.

The following features cannot be installed on the same server: Database, WID Database.

A little background info:

  • The WID Database (which is checked by default) means that the setup installs the Windows Internal Database, which is a mini SQL Server engine. It has some limitations, but perfectly suitable for WSUS, especially in a single server scenario.
  • The Database (which is not checked by default) means, that the setup creates the WSUS database in an existing SQL Server instance, which can even reside on another computer. If you want to select this option, please read the WSUS database requirements section of the documentation.

The important is that the default two checks are perfect, you don’t need all three.

Restart without results

The installer may run fine for a while, until:

The request to add or remove features on the specified server failed.

The operation cannot be completed, because the server that you specified requires a restart.

The operation cannot be completed, because the server that you specified requires restart.

Of course you can restart your server, but that won’t solve anything. After restarting the server, you have to restart the WSUS installation which will end with the same results again and again.

The solution is to modify your group policy. Start the Group Policy Management Console and edit the Default Domain Controllers Policy. In the Computer Configuration –> Policies –> Windows Settings –> Security Settings –> Local Policies –> User Rights Assignment branch, edit the Log on as a service setting, and add the following accounts: IIS_WPG, NETWORK, NETWORK SERVICE, SERVICE.

After saving the GPO, don’t forget the update the policy:

gpupdate /target:computer

Post-Installation tasks

After the installation wizard completes, you will see – if you really look for it – a Launch Post-Installation tasks link which you must click. This essentially continues the installation.

Until it fails:

Configuration failed. A log file was created at C:\Users\username\AppData\Local\Temp\tmpXXXX.tmp

wsus-post-installation

I encourage you to look into the log file, because it is detailed and very readable. For example:

Config file did not contain a value "ContentDirectory"
Microsoft.UpdateServices.Administration.CommandException: 
A required configuration value was not found in the system.

Yes, it is missing a setting which you probably entered in the graphical wizard! If you are a command line guru there is a tool for you called wsusutil.exe in the C:\Program Files\Update Services\Tools folder, however it fails with the same error.

Both the GUI and the command line tool are missing the value from the C:\Windows\System32\ServerManager\ComponentConfiguration\UpdateServices-Services.xml file. If you open this file, you will find this (I added line breaks for readability):

<?xml version="1.0" encoding="utf-16"?>
<INSTANCE CLASSNAME="ServerComponent_UpdateServices_Services">
<PROPERTY NAME="ContentDirectory" TYPE="string">
</PROPERTY>
<PROPERTY NAME="ContentLocal" TYPE="boolean">
<VALUE>true</VALUE>
</PROPERTY>
</INSTANCE>

If you have eyes for XML, you see immediately, that that ContentDirectory value is definitely missing. No problem, just add it:

<?xml version="1.0" encoding="utf-16"?>
<INSTANCE CLASSNAME="ServerComponent_UpdateServices_Services">
<PROPERTY NAME="ContentDirectory" TYPE="string">

<VALUE>C:\WSUS</VALUE>
</PROPERTY>
<PROPERTY NAME="ContentLocal" TYPE="boolean">
<VALUE>true</VALUE>
</PROPERTY>
</INSTANCE>

It you run Notepad as administrator, you will be able to save the changes. Click the Launch Post-Installation tasks link again, it will run longer.

Database

For me it ran longer, but failed later. It created another log file in the Temp folder, this time with the following error:

Fatal Error: SqlException (0x80131904): Invalid object name ‘SUSDB.dbo.tbSchemaVersion’.

This means something is wrong with the database. If you read the log you can see, that the database exists, the installer could connect to it, however the specified table is missing.

I checked the C:\Windows\WID\Data folder where I found the SUSDB.mdf and SUSDB_log.ldf files, so the database really existed, probably because it was created by a previous installation attempt. However its 2112 KB size was suspicious, because it closely matched to the size of the model.mdf. My guess was that the database was created, but it is still empty.

My idea was to delete the database and hopefully the installer will create it again. Deleting the files was surprisingly easy, but when I checked the error.log file in the Log folder after restarting the service, it because obvious that the database server is missing the files.

So the correct solution is to connect to the database server and drop the database. If you don’t want to install SQL Server Management Studio, you can go with the SQLCMD command line tool which is available as a very small standalone installer from here: Microsoft Command Line Utilities 11 for SQL Server

If you don’t read the System Requirements section the MSI installer will warn you that it requires the ODBC Driver 11 for SQL Server as well.

Downloading these two MSI files is not obvious from Internet Explorer at all. First it aggressively blocks the popup windows, and second:

Security Alert: Your current security settings do not allow this file to be downloaded.

wsus-ie-msi-download-error

If you don’t want to fight with the browser, you can download this 7MB content on another computer and move it to the server via Remote Desktop.

Now you have SQLCMD in this folder:

C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\110\Tools\Binn

You will also need the connection string to WID:

np:\\.\pipe\MICROSOFT##WID\tsql\query

I prefer using SQLCMD with a command file, because that makes editing and re-executing SQL commands easy. So you can create a file called wsus.sql with this content:

select name from sys.sysdatabases
drop database susdb
select name from sys.sysdatabases

And run it from the command prompt:

sqlcmd -S np:\\.\pipe\MICROSOFT##WID\tsql\query -i c:\temp\wsus.sql

With this you’ve dropped the WSUS database, but thankfully the WSUS installer will create it again with the correct table structure. After completing the install wizard and the post-installation tasks you can start the WSUS Administration console and run the server configuration wizard.

The WSUS server is installed now, but you still have to configure the clients and setup SSL.

 

Technorati-címkék: ,,

Mitigating DoS attacks in ASP.NET

According to a widely accepted view, there is no effective countermeasure against (Distributed) Denial of Service attacks. End of story.

That’s kinda true, especially if the attack is based on sending numerous small requests to the webserver. However, websites can also be DoS’ed by sending huge HTTP requests, which can cause memory issues on the server. Actually the same can happen when non-malicious users upload files that are much bigger than expected.

You can mitigate this threat by setting the maximum accepted size for a HTTP request. It is important to understand that implementing such checking in an ASP.NET event handler is far too late, because when that handler is called, the request is already arrived to the server. Thankfully both ASP.NET and IIS provides endpoints in the earlier phases of the request pipeline.

ASP.NET provides the maxRequestLength attribute which you can configure in web.config. The default value is 4096 and that means 4096 kilobytes:

<httpRuntime targetFramework="4.5" maxRequestLength="4096"/>

So by default ASP.NET aborts all requests which are larger than 4MB with a HttpException:

HttpException (0x80004005): Maximum request length exceeded.

Unfortunately this exception is far too general, you cannot use any of its properties (beside the localized error message) to identify this special issue, but this workaround works well:

void Application_Error( object sender, EventArgs e )
{
  HttpRuntimeSection section = ConfigurationManager.GetSection( 
"system.web/httpRuntime" ) as
HttpRuntimeSection; int maxRequestLength = section.MaxRequestLength; if( this.Request.ContentLength > maxRequestLength * 1024 ) { this.Server.ClearError(); // Log and redirect to a friendly error page etc. } }

IIS provides the Request Filtering module which (beside many other useful features) also allows you to set the maximum allowed request length in the maxAllowedContentLength attribute. The default is 30000000 here and it means about 28.6MB, because it is measured in bytes:

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="30000000" />
    </requestFiltering>
  </security>
</system.webServer>

When this limit is reached, IIS issues a response with HTTP Error 404.13:

iis-error-404-13

You can handle this error as well in global.asx, but this time you need to implement the Application_EndRequest event handler:

protected void Application_EndRequest( object sender, EventArgs e )
{
  if( this.Response.StatusCode == 404 && this.Response.SubStatusCode == 13 )
  {
    // Log, redirect, etc.
  }
}

 

Technorati-címkék: ,,,,,

MIME type sniffing in C#

If you create a webpage where your users can upload files to your website, you probably want to filter what type of files can be uploaded. If you want to resize the user’s avatar, then the uploaded file must be an image file, or if you want to extract data from the uploaded XLSX file, then it must be really an Excel file.

The classic solution to this problem is to examine the extension of the uploaded file. Please always keep in mind that the file name which contains the file extension is posted to the webserver in the HTTP request, so you can trust it only as much as you trust the rest of the request. Not at all.

A better solution is to read the raw contents of the file, and look for the bytes that characterize the gives file type. These bytes are called the file type signatures, and you can find them on Gary Kessler webpage with additional links to articles, databases and tools.

For us the question is how to implement this in C#? A natural solution is to open the file, seek to the given offset, read the required number of bytes and compare them with the expected signature. This works perfectly as long as the vendor doesn’t changes the file format.

A bit more “official” solution is using the FindMimeFromData Win32 API function which serves as the basic of the MIME type sniffing feature of Internet Explorer. Using P/Invoke, you can call this function from .NET as you can see it in the following example:

using System;
using System.Runtime.InteropServices;

/// <summary>
/// Helper class to detect the MIME type based on the file header signature.
/// </summary>
public static class MimeSniffer
{
    /// <summary>
    /// Internet Explorer 9. Returns image/png and image/jpeg instead of 
///
image/x-png and image/pjpeg. /// </summary> private const uint FMFD_RETURNUPDATEDIMGMIMES = 0x20; /// <summary> /// The zero (0) value for Reserved parameters. /// </summary> private const uint RESERVED = 0; /// <summary> /// The value that is returned when the MIME type cannot be recognized. /// </summary> private const string UNKNOWN = "unknown/unknown"; /// <summary> /// The return value which indicates that the operation completed successfully. /// </summary> private const uint S_OK = 0; /// <summary> /// Determines the MIME type from the data provided. /// </summary> /// <param name="pBC">A pointer to the IBindCtx interface. Can be set to NULL.</param> /// <param name="pwzUrl">A pointer to a string value that contains the URL of the data. Can be set to NULL if <paramref name="pBuffer"/> contains the data to be sniffed.</param> /// <param name="pBuffer">A pointer to the buffer that contains the data to be sniffed. Can be set to NULL if <paramref name="pwzUrl"/> contains a valid URL.</param> /// <param name="cbSize">An unsigned long integer value that contains the size of the buffer.</param> /// <param name="pwzMimeProposed">A pointer to a string value that contains the proposed MIME type. This value is authoritative if type cannot be determined from the data. If the proposed type contains a semi-colon (;) it is removed. This parameter can be set to NULL.</param> /// <param name="dwMimeFlags">The flags which modifies the behavior of the function.</param> /// <param name="ppwzMimeOut">The address of a string value that receives the suggested MIME type.</param> /// <param name="dwReserverd">Reserved. Must be set to 0.</param> /// <returns>S_OK, E_FAIL, E_INVALIDARG or E_OUTOFMEMORY.</returns> /// <remarks> /// Read more: http://msdn.microsoft.com/en-us/library/ms775107(v=vs.85).aspx /// </remarks> [DllImport( @"urlmon.dll", CharSet = CharSet.Auto )] private extern static uint FindMimeFromData( uint pBC, [MarshalAs( UnmanagedType.LPStr )] string pwzUrl, [MarshalAs( UnmanagedType.LPArray )] byte[] pBuffer, uint cbSize, [MarshalAs( UnmanagedType.LPStr )] string pwzMimeProposed, uint dwMimeFlags, out uint ppwzMimeOut, uint dwReserverd ); /// <summary> /// Returns the MIME type for the specified file header. /// </summary> /// <param name="header">The header to examine.</param> /// <returns>The MIME type or "unknown/unknown" if the type cannot be recognized.</returns> /// <remarks> /// NOTE: This method recognizes only 26 types used by IE. /// http://msdn.microsoft.com/en-us/library/ms775147(VS.85).aspx#Known_MimeTypes /// </remarks> public static string GetMime( byte[] header ) { try { uint mimetype; uint result = FindMimeFromData( 0,
null,
header,
(
uint) header.Length,
null,
FMFD_RETURNUPDATEDIMGMIMES,
out mimetype,
RESERVED );
if( result != S_OK ) { return UNKNOWN; } IntPtr mimeTypePtr = new IntPtr( mimetype ); string mime = Marshal.PtrToStringUni( mimeTypePtr ); Marshal.FreeCoTaskMem( mimeTypePtr ); return mime; } catch { return UNKNOWN; } } }

This function is reliable, however it recognizes only the 26 most frequent file types. That’s not a small number, a more painful problem is that it recognizes all Office files as ZIP compressed files (which is true by the way).

With some googling you can find other projects in the net, for example the FileTypeDetective on CodePlex recognizes fewer file types, but detects the Office file formats specifically. And you can see how it works in the source code.

Whichever solution you choose, don’t forget that you have just introduced an external dependency into your project, moreover you can’t know how future-proof the solution is.

 

Technorati-címkék: ,,