Wednesday, August 11, 2010

Don't use FormsAuthentication.HashPasswordForStoringInConfigFile()

So, a ninja of my acquaintance told me yesterday that MD5 and SHA-1 hashes were not really considered acceptable for hashing passwords nowadays due to attacks having been developed which make it easier than it should be to find a hash collision. "Federal agencies should stop using SHA-1 for digital signatures, digital time stamping and other applications that require collision resistance as soon as practical," says the National Institute of Standards and Technology.

Now, many .NET developers are probably accustomed to using FormsAuthentication.HashPasswordForStoringInConfigFile() to hash passwords. So it's quite an annoyance that this function only supports two options for hash format: MD5 and SHA-1!

Why is this so? I have no idea. The System.Security.Cryptography namespace contains implementations of the newer SHA-2 hash algorithms.

Let's fire up .NET Reflector and disassemble FormsAuthentication.HashPasswordForStoringInConfigFile()

public static string HashPasswordForStoringInConfigFile(string password, string passwordFormat)
{
    HashAlgorithm algorithm;
    if (password == null)
    {
        throw new ArgumentNullException("password");
    }
    if (passwordFormat == null)
    {
        throw new ArgumentNullException("passwordFormat");
    }
    if (StringUtil.EqualsIgnoreCase(passwordFormat, "sha1"))
    {
        algorithm = SHA1.Create();
    }
    else
    {
        if (!StringUtil.EqualsIgnoreCase(passwordFormat, "md5"))
        {
            throw new ArgumentException(SR.GetString("InvalidArgumentValue", new object[] { "passwordFormat" }));
        }
        algorithm = MD5.Create();
    }
    return MachineKeySection.ByteArrayToHexString(algorithm.ComputeHash(Encoding.UTF8.GetBytes(password)), 0);
}

That's some dodgy code. A hard-coded if-else-if construct to lock it down to only supporting MD5 and SHA-1, and they don't seem to be aware that there is actually an overload HashAlgorithm.Create(string hashName) which takes a hash algorithm name parameter! If they'd just used that, they would have had support for all the SHA-2 hash implementations.

Here's my alternative:

public static string HashString(string inputString, string hashName)
{
    HashAlgorithm algorithm = HashAlgorithm.Create(hashName);
    if (algorithm == null)
    {
        throw new ArgumentException("Unrecognized hash name", "hashName");
    }
    byte[] hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
    return Convert.ToBase64String(hash);
}

Handles all of the available hash formats (and indeed any new ones that might be added in later versions of .NET, if HashAlgorithm.Create(string hashName) is updated), and additionally returns the result Base64 encoded rather than as a hexadecimal number, so it's shorter for the same hash length.

Sunday, December 13, 2009

FCKeditor with ASP.NET. File uploads. Making them work in IE7 and Chrome.

FCKeditor, now known as CKEditor, presumably because the original sounded too risqué, is quite a popular open-source rich text editor for use in web pages. I've used it in quite a few projects and always been pretty happy.

Although the current release is CKEditor 3.0.1, ASP.NET developers are still using FCKeditor 2.6.5, since there isn't any ASP.NET integration in CKEditor 3 yet. And it was with 2.6.5 that I ran into a puzzling problem with file uploads.

File uploading is disabled by default, but if you need to, you can enable it, and allow people editing text to upload files and link to the resulting upload. I'd only tried it once before, for a project that never went ahead and thus never left our test server, and it worked fine there.

However, this time, once it was up on an external staging server.. the upload only worked with Firefox. With IE7 and Chrome, I'd hit the "Send it to the Server" button, and the progress indicator would spin forever, achieving nothing.

So I dug a bit. Was the file being successfully uploaded? Yes, it was. Was an error being returned by the Ajax call? I inspected it with Fiddler and it looked OK - no error, a 200 response code sending back a chunk of Javascript to the browser. So obviously there was something wrong with the Javascript.

Having narrowed it down to this bit of Javascript, and identified that it was being emitted by the function FileWorkerBase::SendFileUploadResponse() in the FCKeditor.NET library, I was able to Google with more confidence, and a solution was found at http://dev.fckeditor.net/ticket/2700

Basically there is something going wrong with the Javascript's attempts to fiddle with document.domain. Removing is, as per that ticket, fixed the problem.

  1. Download the FCKeditor.NET library source from http://sourceforge.net/projects/fckeditor/files/FCKeditor.Net/2.6.3/FCKeditor.Net_2.6.3.zip/download

  2. Open up FileBrowser/FileWorkerBase.cs

  3. Replace the SendFileUploadResponse(int errorNumber, bool isQuickUpload, string fileUrl, string fileName, string customMsg) function on line 110 with the version from http://dev.fckeditor.net/ticket/2700

  4. Rebuild and replace your FredCK.FCKeditorV2.dll !


Thursday, November 26, 2009

Atlassian's Confluence. Tomcat. 64-bit Windows. Making it work as a service.

So I was installing an evaluation copy of Atlassian's "Confluence" wiki, and since we're mostly a Windows shop here, I was doing so on a Windows Server 2008 box.

All went well until I got to step 9 of the installation guide: "Start Confluence automatically on Windows as a Service." I ran into the following problem when I got to the final "net start Confluence" step.

[2009-11-27 14:51:08] [info] Procrun (2.0.5.0) started
[2009-11-27 14:51:08] [info] Running Service...
[2009-11-27 14:51:08] [info] Starting service...
[2009-11-27 14:51:08] [197 javajni.c] [error] %1 is not a valid Win32 application.
[2009-11-27 14:51:08] [994 prunsrv.c] [error] Failed creating java c:\Progra~1\java\jdk1.6.0_17\jre\bin\server\jvm.dll
[2009-11-27 14:51:09] [1269 prunsrv.c] [error] ServiceStart returned 1
[2009-11-27 14:51:09] [info] Run service finished.
[2009-11-27 14:51:09] [info] Procrun finished.

As hinted in the "Start Confluence automatically on Windows as a Service" document, there are issues getting Apache Tomcat to run as a service on 64-bit Windows using a 64-bit JDK. The recommended workaround is to use a 32-bit JDK, but since that's a bit lame, I decided to follow the advice of the issue "Installing Confluence as a service on XP-64 fails due to 32-bit binaries of Tomcat" and try replacing the Tomcat binaries with 64-bit versions.

And here's the bit that got me scratching my head for a while. Various things I googled up directed me to the Tomcat SVN repository to get the 64-bit version, but I'll be damned if I could find a version for x86-64 or x64. Just the old IA-64 Itanium stuff, and "AMD64", which I assumed was some obscure AMD implementation.

More fool me. A trip to Wikipedia's x86-64 article clarified things: AMD64 was the marketing name AMD used for their x64-64 implementation, and those were the binaries I was looking for, for my Xeon server.

If you need them, head on over to Tomcat's SVN repository at http://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk/res/procrun/amd64/ and grab tomcat.exe from there - it worked for me!