DCSIMG
May 2007 - Posts - Doron's .NET Space

May 2007 - Posts

Why client-side validation is a lie

You probably heard it a million times. It is one of the golden rules of web-development. Do not rely solely on client-side validation to ensure you get the data that you expect from the browser. Anyone can send you a crafted request that bypasses your script validation. Always validate on the server as well. And yet, I see a lot of commercial sites that ignore this rule, and it is easy as hell to break their logic. In fact, with the following simple steps I will show, you can bypass any javascript-only validation.

Consider the following TestValidation.htm page:

1 <html > 2 <head> 3 <title>Validation Lies To You</title> 4 <script> 5 var valid;
6 function validate() {
7 valid = document.getElementById('Text').value != "";
8 if (!valid)
9 alert('Not Valid!');
10 return valid;
11 }
12 </script> 13 </head> 14 <body> 15 <form method="post" action="save.aspx" onsubmit="return validate();"> 16 <input id="Text" type="text" /> 17 <input type="submit"/>
18 </form> 19 </body> 20 </html>

This html page contains a form, a text box and a button. The validate function checks if the form is valid (in this case that the text box contains a value, but it can do any other validation as well). If it is valid, the form will submit to save.aspx, if it is not - an error message is displayed.

In order to bypass this validation we don't have to craft an http-request or play with the script options of our browser. What we need to do is this:

  • Install Firebug. This is a great add-on to Firefox that can help you debug web-sites and javascript easily. And also bypass client side validation.
  • Point Firefox to our TestValidation.htm. Hit submit. Err, javascript alert. Can't submit. What can we do?
  • Open the Firebug console (via the Tools->Firebug menu or the Firebug icon on the status bar).
  • Go to the script tab in the Firebug console and place a breakpoint on line 8 in the code (the "if" statement).
  • Now hit the submit button, the breakpoint will hit and the page will halt.

This is where we are:

  • We want to change the value for the "valid" variable. We go to the console tab (which is somewhat like the VS debugger "immediate" window) and enter "valid=true".
  • Now we go back to the script tab and hit F5. The form posts like a charm.

Now you could say I cheated a little by making "valid" a global variable, and indeed I couldn't find in Firebug a way to change the value of a local variable of a function (the console seems to affect only global DOM elements, unlike the VS debugger "immediate" window). But even if it were a local variable I could write in Firebug the following (before clicking the button):

document.forms[0].onsubmit = function(){return true;}

Or simply:

document.forms[0].submit()

The form would have submitted perfectly in both cases, ignoring the client validation.

So we have arrived at the following conclusions:

  1. Client-side validation is not security. Always validate on the server side as well. ASP.NET's default validators do this for you, but if you use CustomValidator remember to handle its Server_Validate event.
  2. Firebug is an awesome tool - I wish I had an equivalent for IE (for the meantime I'll have to settle for IE Dev Toolbar).

Happy coding!

Posted by dorony | 1 comment(s)

VS2005 Web Project: Assembly referencing issue

We have encountered an interesting issue regarding assembly referencing from an ASP.NET web project in Visual Studio 2005. A developer in my team did the following: he added to a web-site a reference to a GAC installed assembly, lets call it Gac.dll. This GAC installed assembly had a file reference to another assembly which was not installed at the GAC, we shall call it Lib.dll, and we shall assume it is located in c:\lib.

Lets assume Gac.dll has the following code in it:

namespace Gac { public class GacObject { public void UseLib() { new Lib.LibObject().DoSomething(); } } }

And the developer in my team had code similar to this:

protected void Page_Load(object sender, EventArgs e) { new Gac.GacObject().UseLib(); }

At this point, Boom. Runtime exception:

Could not load file or assembly 'Lib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b51b3d0471344dda' or one of its dependencies. The system cannot find the file specified.

Now, in order to understand why this happens, we first have to understand a very important thing:

An assembly does not store the locations of the DLLs it is referencing in its manifest.

That is, the reference in gac.dll to lib.dll would look like this inside the assembly manifest:

.assembly extern Lib { .publickeytoken = (B5 1B 3D 04 71 34 4D DA ) // ..=.q4M. .ver 1:0:0:0 }

No where does it say that we can find Lib at c:\lib. So when on Page_Load we are calling the UseLib method, the CLR has to load the Lib assembly at this point. For that to happen it needs to find it first. The CLR would look for the assembly in several places, including the Bin directory and GAC, but it won't find Lib.dll in neither. And there's no reason for it to know to search in c:\lib. Hence, an exception is thrown to our faces.

Now, why was it important to mention that this is a web project? Won't the same thing happen for, say, a console application? Well, no. Say that we create a console application called MyApp, and reference Gac.dll in it, in which we will have the following code:

class Program { static void Main(string[] args) { new Gac.GacObject().UseLib(); } }

Running this will work like a charm. Not because the CLR acts differently for console applications, it is actually because the way Visual Studio compiles this project. In all projects which are not web sites, compiling in VS2005 means running the MSBuild script that is in our .csproj file. This script is actually smart enough to know that since Gac.dll is referencing Lib.dll, we will need it in our output directory (in MyApp case - bin\debug). Therefore it will copy Lib.dll and any other assemblies that Gac.dll is using (that are not installed in the GAC, that is) to this directory. Hence, when we run our program, the CLR quickly finds Lib in our bin\debug directory and everything works.

But when we compile our web site project, this does not happen, as it doesn't run the standard MSBuild script (we don't even have a .csproj file, remember?). It will actually work for assemblies that are not in the GAC - if you try and add a reference to nonGac.dll which is also referencing Lib.dll, Lib.dll will be automatically added to the site's bin directory. Not on compile time, mind you. Visual Studio will do this at the moment we add the reference to nonGac.dll. For some unknown reason, the same doesn't happen for a Gac.dll which is in the GAC, and therefore we get an exception at the moment we try to load Lib.dll.

Once we understand the problem, the solution is quite simple. We have to tell the CLR where to find Lib.dll. There are several things we can do:

  1. Manually add a reference to Lib.dll to the site's Bin directory.
  2. Add Lib.dll to the GAC (best option, IMO).
  3. Tell the runtime where to search for the dll, using a configuration option.
  4. Switch to a Web Application Project and get all the standard MSBuild features.

Hopefully this wasn't too confusing. The subject of assembly referencing and binding can give you quite a hell in .NET, so it is critical to have a good understanding of what is going on.

Posted by dorony | with no comments
תגים:

Anonymous Delegates or DisposableActions?

One of the most useful features of .NET 2.0 is anonymous delegates. They allow you to create "wrappers" for code which run before and after the code, handle exceptions in it, and decide whether to run it or not. Consider the following method:

public static void WrapCall(Callback callback) { try { callback(); } catch (Exception x) { ExceptionManager.Publish(x); throw; } }

Which will be called like this:

WrapCall( delegate() { DataAccess.UpdateSomething(); EmailSender.SendSomething(); });

This ensures that the exception wrapping and publishing code is handled in one and only one place, and that is the wrap call method. The user does not have to worry about publishing exceptions to log.

Another use of anonymous delegates can be in the following method:

public class CallManager { public static void EnableLoggingForCall(Callback callback) { if (Logger.EnabledForCurrentThread) { callback(); } else { Logger.EnabledForCurrentThread = true; callback(); Logger.EnabledForCurrentThread = false; } } }

This method job is to ensure that the logger is enabled for a specific method execution. We want to make sure that if it is already enabled, we do nothing, but if it is not, we enable it and then turn it back off (since we turned it on it is our responsibility to turn it off). This is how we'll call it:

CallManager.EnableLoggingForCall( delegate() { new CriticalAction().Run() });

But anonymous delegates do not have the prettiest syntax in the world, they can be a bit hard to grasp at first, and you can expect an issue if you write code like this:

CallManager.EnableLoggingForCall( delegate() { base.Run(); });

Because of the way anonymous delegates are implemented, you can expect a compilation warning about this code. You'll be happy to know, then, that for this scenario we can find an alternative for delegates.

Meet DisposableActions

In this post Oren Eini describes a pattern called DisposableAction. In a similar manner to anonymous delegates, it allows you to ensure certain code is run both before and after the user's code. In some scenarios, we can use it instead of delegates. Our second example, implemented with DisposableAction, will be used like this:

using (CallManager.EnableLoggingForCall()) { new CriticalAction().Run(); //base.Run() will also work }

And the implementation:

1 public static IDisposable EnableLoggingForCall() 2 { 3 if (Logger.EnabledForCurrentThread) 4 return new DisposableAction(delegate { }); 5 6 Logger.EnabledForCurrentThread = true; 7 return new DisposableAction( 8 delegate 9 { 10 Logger.EnabledForCurrentThread = false; 11 }); 12 }

So what happened here? As described in Oren's post, a disposable action runs the action attached to it when the Dispose method is called on it. Since the using statement ensures the object it is using gets disposed, we know that lines 3-6 will run before our critical action executes, and that line 10 will run after it executes. This way we have achieved the same thing as we did with an anonymous delegate, but with a simpler syntax and without strange side-affects.

Note that this pattern can only work for the second case I showed here: needing to run some piece of code before and after a block of code is executed. If we had to catch an exception (as we did in the WrapCall example) or have the option not to run the block of code at all (because we want to get the result from the cache, or whatever) we would have to use anonymous delegates. The code in line 10 has no control on the action execution itself, nor can it know if an exception has occurred there.

Still, I find this a very elegant pattern, and I highly recommend using it instead of delegates, when appropriate.

Posted by dorony | with no comments
תגים:,

NUnit vs. VSTS: VSTS Wins

As I've said before, our team has begun doing unit testing. At first we used NUnit and NCover for this purpose, although we have VSTS licenses. This was for the following reasons:

  1.  NUnit is faster than VSTS (and that test panel really slows down the IDE... Like it isn't slow enough!).
  2. NUnit has more features than VSTS (although for now we're using only the basics anyway) and less bugs.
  3. NUnit has more support and lots of googleable knowledge. It is definitely the more mature solution.
  4. NUnit feels more "clean" to me. You don't have to create a special test project for it, and it doesn't add any weird solution items. You just create a library project and can start testing.

We are now changing back to VSTS testing, for the following reasons:

  1.  Team Foundation Server natively supports it.

OK, so that's only one reason, but it's a pretty damn good one. Since we are already using TFS for our source control and spent quite some time learning it and setting it up, it makes the most sense for us to use it as our continuous-integration server. That's where we'll do our nightly builds, code analysis, etc.

I find this (having to choose the less mature tool) rather annoying, but what comforts me is that thanks to Test Driven.NET the daily work is hardly affected, and you don't have to use that annoyingly slow test window (unless you're really eager to get pretty GUI for your tests).

Life is full of compromises, I guess.

Posted by dorony | with no comments
תגים:,

Frustration

Yesterday I tackled this extremely annoying bug, which kept me annoyed for about 10 hours in a row. We are using a GIS software called ArcGIS Server, which one of its main uses is to supply maps - In our case, images passed to a web-control which comes with the software. Everything worked nice and dandy until we installed our new version on a new production server. The maps would not display and all we got to see is WHITE.

At the following hours I tried, among others, the following approaches:

  • Recreating the cache for the maps (X 10 times)
  • Moving the output directory around and playing with the permissions for it (X 5 times)
  • Uninstalling and Reinstalling ArcGIS Server (X 1 time)
  • Banging my head on the desk (X 20 times)

When all failed, I pointed my local machine to the server and moved into debug mode, stripping the map page into its most basic functionality, which still didn't work. No errors appeared in the log. The images did seem to get created on the server, but for some reason I couldn't see them in the client. Don't know why it took me this long, but at about 23:00 o'clock it finally came to me to try using Fiddler in order to understand what's going on in the network level. That's when I saw this:

prodserver/ags_dfdjf.png: Sorry, unable to resolve DNS "prodserver".

  • Bang head on the desk (X 100 times)

Stupid, stupid, stupid. Should have pointed the virtual directory for the output images to prodserver.com. Why oh why didn't I notice this sooner? To God the answers.

Bloody url extensions. Who needs them anyway.

Posted by dorony | with no comments
תגים: