Wednesday, March 26, 2008

I had to comment on this article over at Slashdot:

http://developers.slashdot.org/article.pl?sid=08/03/25/0145208&from=rss

I don't have a feel for how people in France feel about concurrency programming, but it certainly seems like a hot topic among .Net developers in the USA these days.  The Task Paralell Library, Polyphonic C#, the inreased interest in functional programming languages because of the benefits functional programming can bring to concurrency; I would say interest in this aspect of programmnig is ever-rising.

This reminds me, if I ever get my workstation running, to finish my next concurrency in .NET article.

Wednesday, March 26, 2008 10:38:36 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Yesterday when I got home I plugged the new mobo in, and wonder of wonders it would post.

Last night I had birthing classes, had to go get our daughter from Grandma's after than, so at 11pm I sat down to finish building the workstation.  Vista kept treating me to bluescreen/stop errors after copying files.  Doing some research I found that many motherboards + Vista64 have an issue booting with 4gb of ram installed, so I eventually took some RAM out and got windows installed.  There was a hotfix for this issue but I kept having hard lockups in Vista.  Long story short: I flashed the BIOS after about 4 Vista lockups, Vista insisted on using it's "standard VGA driver" instead  of the Vista 64 driver that came with my 8800 GT.  I'll have to see tonight if Vista is happy with the new BIOS and/or the NVIDIA unified drivers when I try that. 

Stupid computers...

Wednesday, March 26, 2008 9:35:46 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Tuesday, March 25, 2008

I've always been a proactive person:

"Rochester researchers showed for the first time that a natural antioxidant found in grape skins and red wine can help destroy pancreatic cancer cells by reaching to the cell's core energy source, or mitochondria, and crippling its function. The study is published in the March edition of the journal, Advances in Experimental Medicine and Biology.

The study also showed that when the pancreatic cancer cells were doubly assaulted -- pre-treated with the antioxidant, resveratrol, and irradiated -- the combination induced a type of cell death called apoptosis, an important goal of cancer therapy."

http://www.scienceblog.com/cms/mounting-evidence-shows-red-wine-antioxidant-kills-cancer-15734.html

The part about "Pre-treated" is especially precious.  Now please excuse me, I'm going to go pre-treat my cells against cancer.

Tuesday, March 25, 2008 5:04:03 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback

It would appear the UPS man has dropped off my replacement, time for attempt #2.

Tuesday, March 25, 2008 1:23:53 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, March 23, 2008

I thought that a nice feature for my KlipschCorner System Profiles Area might be a deep zoom for the people who upload very high res photos.  I quickly realized I do not currently see any way to automate the creation of the Deep Zoom .bin files needed by the MultiScaleImage control.  One step beyond automation, it ought to be available in a server-side Silverlight API for the many people in hosted environmnets where they may not be able to install Deep Zoom Composer and shell execute an .exe. 

Saturday, March 22, 2008 11:58:13 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Saturday, March 22, 2008

I am now one NewEgg hat and $50 better off than I was the other day, but my replacement part still has not shipped.  So much for having it overnighted to me.  In addition to the claimed system glitches that kept this from shipping when it was supposed to, their systems showed on Friday that the part was boxed but no tracking number had made it out yet.  This morning, I find an email notification that my $$ has been refunded for the part.  I'm not sure if this is additional goodwill or another mistake, but I'll have to wait until Monday to find out.

Saturday, March 22, 2008 5:21:52 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, March 21, 2008

http://www.forbes.com/opinions/2008/03/19/yaron-campaign-finance-oped-cx_ybr_0321yaron.html

I encourage you to leave comments on Forbes as well.

Friday, March 21, 2008 10:16:01 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, March 20, 2008

So, I set about to RMA my motherboard on Wedesday.  To make a long story short, they gave me a UPS shipping lable and the transit times plus their RMA processing time put the round trip at something like 3 weeks for me.  This is complete BS since by then my window to return/exchange the other parts will be gone.  I shouldn't have to wait a month for something I bought to be made right.  I called NewEgg's customer support to explain this to them, and lo and behold without the slightest bit of resistance they agree to overnight me a new one without making me wait on the whole RMA Process.  I left them a strongly positive note at Reseller ratings and I should be in Quad Core Heaven Friday night or Saturday.

There's an important item to not here.  A DOA Asus motherboard is not the fault of NewEgg, but I am more directly their customer than a customer of Asus.  A company cannot directly pin their customer's satisfaction on the performance of their own vendors.  A layer of indirection is needed.

Thursday, March 20, 2008 11:02:03 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, March 18, 2008

It would appear, at least on my machine, that I can add "No script debugging" to my list of IE8 complaints.  Despite making the necessary Internet Options changes I can't hit breakpoints in javascript in VS2008 any longer.  It's a beta browser and I don't really need this feature, but be warned.

Tuesday, March 18, 2008 9:19:01 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Securing ClickOnce Deployments

 

I have written in the past (http://www.damonpayne.com/PermaLink,guid,da078e56-eb5b-4616-8605-13c38d54efc3.aspx) about some of the plumbing involved in ClickOnce and Client Application Services geared towards preventing unauthorized operation of a client application.  At the time I noted that there is at least one issue with this level of “security”.  While it does keep unauthorized users from running your application, it doesn’t protect your code.  As has been lamented elsewhere, a ClickOnce application deployed on the Internet does not have any supported means of keeping people from getting your code.  Even with obfuscation this may be undesirable: we don’t want people getting a hold of the application manifest file and subsequently the assemblies.

A while back I started down a few paths to secure the code and the Application Deployment Manifest.  I have a pretty decent partial solution for protecting your code.  Read on.

Step 1: Security by Obscurity?

Your ClickOnce deployment consists of a deployment manifest, the executable, and all of the referenced assemblies and content files required to run your Windows Forms or WPF application.  By turning off directory browsing and not having any publicly available links to your .application file you can discourage casual spelunking.   I’m not very satisfied with this as the only aspect of a code protection solution. 

It’s fairly unlikely than an uneducated attacker (outside job) could guess the names of the assemblies that make up your application.  If only you could lock down the .application file…

Eliminating the Obvious Suspects

To get the obvious thoughts out of the way:

1)      You cannot protect a ClickOnce application deployment manifest using Forms authentication.  The client side aspects of ClickOnce that get installed with .NET 2.0 can’t handle Forms authentication.

2)      Windows Authentication is at best a poor option here, and not a real option for most people.  The performance of “Remember me” in an NTLM challenge form is flaky and is not supported in the Internet zone.

3)      ClickOnce does not support HTTP Basic authentication schemes.

An Idea

One nice thing about ClickOnce is that, like Silverlight, it’s really a Client Side technology.  You don’t NEED to be running Windows Server and IIS in order to benefit from these technologies.  However since I do know some Microsoft server side technologies I had an idea.

I wanted to associate a Product Key of some kind with a ClickOnce deployment.  Product Keys are not the final word on application licensing but it is a means of accountability.  Look at Windows Genuine Advantage: you can’t keep people from sharing CD keys but if you see the same CD key being used 1,000 times from different hosts all over the nation, you can shut it down.  Given a test deploy URL:

http://localhost:5785/CarSpot/MyApp.application

It would be nice to be able to do something like this:

http://localhost:5785/CarSpot/MyApp.application?ProductKey=ZZZZ-ZZZZ-ZZZZ-ZZZZ

… and have it be meaningful.

HttpHandler Attempt 1

To keep people from getting at my .application file, I thought a simple IHttpHandler implementation configured to intercept requests for the .application extension could be the answer. 

Web.config and sample code:

<add verb="GET,HEAD" path="*.application" type="ClickOnceHandler"/>

    public void ProcessRequest(HttpContext context)

    {

        string key = context.Request.QueryString["key"];

        if (ValidateKey(key))

        {

            //Go ahead and give up the .application file

        }

        else

        {

            throw new ApplicationException("Invalid or missing product Key");

        }

    }

I had a notion of allowing users to sign up for the application online by entering a privately exchanged product key in a web page:

The code behind for the install button is simple:

Response.Redirect("Publish/MyApp.application?key=" + _prodKeyTxt.Text);

Sadly, the solution is not this simple.  The Client side ClickOnce libraries exhibit some bizarre behavior.  Using Fiddler and the debugger, this is what I see:

1)      The .application file is requested and the HttpHandler validates the product key.

2)      Mysteriously, ClickOnce requests the entire URL of the .application file again: http://localhost:5785/CarSpot/Publish/MyApp.application?key=123; The HttpHandler validates the key and writes the contents of the .application file to the Response.

3)      Mysteriously (and infuriatingly) ClickOnce requests the .application file again: http://localhost:5785/CarSpot/Publish/MyApp.application ; note the missing piece of the query string.

I was unable to dig up any reason for what ClickOnce is doing here, but I do know that without getting that product key my scheme is ruined.  Looking at the .application file there’s a likely explanation:

<deploymentProvider codebase="http://127.0.0.1:5785/CarSpot /Publish/MyApp.application" />

This is where I left it for a long time, while dealing with some other things at work.

HttpHandler Attempt 2

The next, step, I felt, was to dynamically alter the application deployment file so that the provider codebase as recorded by the client included the product key on the URL.  I added some code to do this:

                XmlDocument doc = new XmlDocument();

                doc.Load(path);

                //Get around the issue of the query strong not being

                doc.DocumentElement["deployment"]["deploymentProvider"].Attributes["codebase"].Value += "?key=123";

However, this introduces another issue.  When you choose to give your ClickOnce application Full Permissions (Otherwise why not go the XBAP route?) you will find that you are signing it.   Look what Visual Studio did for me:

<publisherIdentity name="CN=CORNZOR\Damon" issuerKeyHash="" /><Signature Id="StrongNameSignature" {nothing to see here}</Signature></asmv1:assembly>

Yes, my laptop is named cornzor.  At any rate, this signature is there for your clients’ protection: if a man in the middle alters the XML payload of the .application file it will not verify and ClickOnce will refuse to do anything further.  Since I’m not a man in the middle I assumed there must be a way to dynamically do what I want.

http://msdn2.microsoft.com/en-us/library/acz3y3te.aspx

http://msdn2.microsoft.com/en-us/library/bb384246.aspx

I started out with the two links above, researching how the manifests are generated, and initially experimenting with manually generating un-signed deployment manifests from the command line.  After an infuriating XML character encoding setback, these articles did lead me to the answer.  Now I can protect my .application file in an appropriate fashion.

HttpHandler Complete

The articles above give us the tools we need to put the product key in the application manifest for each client and re-sign on the fly.

    /// <summary>

    /// Execute whatever business logic will make this a valid program key

    /// </summary>

    /// <param name="key"></param>

    /// <returns></returns>

    protected bool ValidateKey(string key)

    {

        return key.Equals("123");

    }

    /// <summary>

    /// Create a new signature in the deployment manifest

    /// </summary>

    /// <param name="path"></param>

    /// <param name="pfxPath"></param>

    protected void ReSign(string path, string pfxPath)

    {

        string mageArgFormat = "-Sign {0} -CertFile {1} -ToFile {2}";

        string mageExe = @"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\mage.exe";

        string mageArgs = string.Format(mageArgFormat, path, pfxPath, path);

        ProcessStartInfo info = new ProcessStartInfo();

        info.Arguments = mageArgs;

        info.FileName = mageExe;

        info.UseShellExecute = false;

        info.RedirectStandardError = true;

        info.RedirectStandardOutput = true;

        Process p = Process.Start(info);

        p.WaitForExit();

        using (StreamReader stdOutReader = p.StandardOutput)

        {

            string stdOut = stdOutReader.ReadToEnd();

            //Error handling here

        }

 

        using (StreamReader stdErrReader = p.StandardError)

        {

            string stdErr = stdErrReader.ReadToEnd();

            //Error handling here

        }

    }

This method uses mage.exe to re-sign the application manifest after we’ve modified it using the BuildClientApplicationFile method.  There are two things to note about this method.  You will see that I’m referring to mage.exe from the Visual Studio directory here.  Obviously, installing Visual Studio on servers is not ideal.  Mage.exe also ships separately with the SDKs as indicated in the two articles I’ve linked, which is a more palatable install than Visual Studio.  I have not yet tested to see if mage.exe will properly operate on Windows Server 2003 by itself.

The second note on this method is that we are passing in a fully qualified path to a .pfx key file.  I’ve only tested this using the same .PFX key I used when I initially published the application.

    private static void BuildClientApplicationFile(string path, string signedPath, string key)

    {

        XmlDocument doc = new XmlDocument();

        doc.Load(path);

        //Get around the issue of the query string not being persisted by the client

        doc.DocumentElement["deployment"]["deploymentProvider"].Attributes["codebase"].Value += "?key=" +key;

        doc.Save(signedPath);

    }

This method stores a special version of the application deployment manifest for this particular client.  I didn’t keep this in memory due to ClickOnce’s odd habit of re-requesting the manifest many times.  This means I’ve got one copy of the manifest per client sitting on my server.  In my testing I’ve found two side-effects for publishing updates using this method:

1)      If I re-activate from the URL and updates have been published, the client does not get prompted

2)      If I run the program from the start menu, the client does get prompted for updates.

Here is the main HttpHandler method:

    public void ProcessRequest(HttpContext context)

    {

        string key = context.Request.QueryString["key"];

        if (ValidateKey(key))

        {

            //Just in case the client doesn't know what to do with .application

            context.Response.ContentType = "application/x-ms-application";

 

            //The current version deploy URL

            string path = context.Server.MapPath("~/Publish/CarSpot.MyApp.application");

 

            //Check for existing signed manifest for this user:

            //keeping a specially signed copy of the manifest per client

            string signedFileName = key + ".CarSpot.MyApp.application";

            string signedFileServerPath = "~/Publish/" + signedFileName;

            string signedPath = context.Server.MapPath(signedFileServerPath);

 

            if (!File.Exists(signedPath))

            {

                //Load the generated deployment manifest as an XML document to make it easier

                //to edit.

                BuildClientApplicationFile(path, signedPath,key);

 

                string pfx = context.Server.MapPath("~/Publish/MyGeneratedKey.pfx");

                ReSign(signedPath, pfx);

                //Redirect to original path now that we've created a signed one...

                string redirUrl = context.Request.Url.ToString();

                context.Response.Redirect(redirUrl);

            }

            else

            {

                context.Response.WriteFile(signedPath);

                context.Response.Flush();

            }

        }

        else

        {

            context.Server.Transfer("~/Error.aspx?errormessage=Missing or invalid Application key");

        }

    }

I feel confident this is safe, look at the appref-ms file generated from this install and put in the start menu on the client:

http://127.0.0.1:5785/CarSpot /Publish/MyApp.application?key=123#CarSpot.MyApp.application, Culture=neutral, PublicKeyToken=blahblah, processorArchitecture=msil

The key is part of the URL, so it should be passed to the server every time.

Further reading

Now I’ve locked down my deployment manifest, at least you have to have a valid product key to get access to it.  This is a large step towards completely locking down my application.  Since this is ASP.Net code that I control, I can log the remote hosts that use each product key and disable one if it appears to be too promiscuous. 

What about your other files?  If someone somehow figures out that there’s a file called CarSpot.Snark.Zing.dll in the ~/ClickOnce directory?  I have some ideas for this too, along the same lines as what we’ve done here.  You will note that one of the Publish options for ClickOnce is to “Use .deploy file extension”.  Sounds like an HttpModule could be created to handle the .deploy extension.

Microsoft’s behavior regarding ClickOnce baffles me, but here we have a means of securing your deployments.

Tuesday, March 18, 2008 10:05:56 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [3]  |  Trackback