Updating Controls From Worker Threads

I thought this was an issue that has been hashed over enough times, but the other day I was asked this question and I’m still amazed that many folks are unfamiliar with this concept.

In WinForms applications, sometimes its desirable to execute a long running method in a worker thread. For instance, you may be retreiving data over a web service that has a heavy load and usually takes several seconds to execute.. When the work has completed, it’s typical that you would want to update the user interface: be that a grid, a tree view control, or some other container that displays the results of your call to the long running process.  Sounds simple enough right?

Before you get in a hurry, consider one thing.  Data created on one thread is owned by that thread as long as it is local data.  This can be overcome though by marshaling the data across threads.  This is a function of Thread Local Storage implemented through managed code. Virtual address space of a process is shared across all threads.  While the data of a thread is unique, it can be “shared” or copied but you need to tell the application to copy the data to a the UI thread before updating the UI with the data.  This can be done using Control.Invoke().

Take a look at what happens when you don’t use this method.  Createa  new Windows Forms project, add a TreeView control (tvResults) and two buttons.( btnNormalExe and btnThreadExe ).  Add the following methods on the form:

public void WorkerThread() {
    Cursor.Current = Cursors.WaitCursor;
    // Simulate Long Process (5 seconds)
    Thread.Sleep( new TimeSpan( 0,0,0,5,0 ) );

    UpdateResults();
    Cursor.Current = Cursors.Arrow;
}

public void UpdateResults() {
  // Update UI
  tvResults.Nodes.Add(
    String.Format( "UI Updated on Thread ID {0} at {1}", 
       AppDomain.GetCurrentThreadId().ToString(),
       DateTime.Now.ToString() ) );
}

For btnNormalExe add the following code:

 

private void btnNormalExe_Click(object sender, System.EventArgs e) {
  WorkerThread();
}

For btnThreadExe add the following code:

private void btnThreadExe_Click(object sender, System.EventArgs e) {
  Thread t = new Thread( new ThreadStart( WorkerThread) );
  t.Start();
}

Now run the form and click on the normal execution button first. Once the method completes, click on the thread execution button.  You’ll receive an exception when the threaded execution completes:

An unhandled exception of type 'System.InvalidOperationException' 
occurred in system.windows.forms.dll
Additional information: The action being performed on this control 
is being called from the wrong thread. You must marshal to the 
correct thread using Control.Invoke or Control.BeginInvoke to 
perform this action.

Just as the exception says, we are going to modify our call to make sure we get back on the UI’s thread before updating it.  In this way, the form’s thread owns the data, not the worker thread. Modify the UpdateResults() method as follows:

public void WorkerThread() {
  Cursor.Current = Cursors.WaitCursor;
  // Simulate Long Process (5 seconds)
  Thread.Sleep( new TimeSpan( 0,0,0,5,0 ) ); 

  //UpdateResults(); 
  this.Invoke( new TreeViewUpdater( UpdateResults ) ); 
  Cursor.Current = Cursors.Arrow;
}

Notice that when you execute both the normal execution button and the threaded execution button the tree view control updates with the same thread ID. This is because the call to control.Invoke puts the call back on the form’s thread.

I will cover this in more depth in the weeks to come, to shed more light on what’s happening in the background.

OOP and Security

A new article has been posted at http://www.codemilitia.com/blogs/tobin.titus/articles/10.aspx

(begin excerpt)

A brief discussion broke out at work today surrounding collections and how they should be exposed in a client class. The proper OO way to expose a collection as a property is to provide a get accessor only. The get accessor should most likely always return an instance. Here is an example:

public class GoodClient {
   private TrustedCollection _data = null;
   public GoodClient() {}
   public TrustedCollection Data {
      get {
         if( _data == null ) {
            _data = new TrustedCollection();
         }
         return _data;
      }
   }
}

The get accessor checks for a null private member and creates a new instance if needed. This follows good encapsulation design.

So what does this have to do with security?

Lets consider that your collection’s “Add” and “Insert” methods might do some data validation on the parameters passed to the method before adding an item to the collection. Let’s look at an example of a class that does some validation:

public class TrustedCollection : CollectionBase {
   public TrustedCollection() { }
   public virtual void Add( DateTime date ) {
      DateTime now = DateTime.Now;
      DateTime is21 = new DateTime( now.Year - 21, now.Month, now.Day );
      if( date > is21 ) {
         throw new ArgumentException(
            "The person must be 21 years old.", "date" );
      }
      List.Add( date );
   }
}

Remember, the first rule of application security is that all input is evil. If your client depends on this validation to occur, it had better not provide a set accessor. Let’s look at how we can defeat this by using an inherited class and a set accessor.

(end of excerpt)

Continue

Home Network Paranoia

Some have called me paranoid, but I have a slogan of “Friends don’t let friends use wireless networks.”  The reason for this is that wireless networks compromise the need for physical access to a network to perform any attack on the internal network. 

Well, I of course, do have exceptions to my wireless rule.  I’m planning on getting a new PDA with 802.11 capabilities, I’d like to be able to access the internet from it.  As such, I’ve decided go add a wireless router to my home network.  Before I did such, I wanted to make sure that my devices and laptop would only have access to the internet from the wireless network, and not to my internal network.  I don’t any stranger standing in the woods of my back yard able to access my TaxCut and MS Money files through a wireless hack, and considering the Feds can do it in 3 minutes now, I think my paranoia is justified. 

I’m not a network security guru. I used to be a network administrator, but that was over 6 years ago and hardware was much different then.  As such, I’m publishing my network layout and asking for comments or suggestions or holes that anyone might see. 

network

As you can see, my internet access comes in through a cable modem which connects to a VoIP-capable router (yes, I use Vonage).  The reason for using this router as my opening router is somewhat physical.  In my garage, where I terminated all of my network runs, I also terminated a cable line and phone lines at a patch panel.  Since the VoIP router also provides data ports, it’s perfect for acting as a distribution for both my planned wireless router and my 8 port routing switch.  The outgoing voice line patches into a telephone patch panel distribution that supplies the house telephone runs.  The 8 port router serves as the first layer of defense for my data network It then provides access to the whole house through the patch panel distribution point where I ran all of my data lines to. (I luckily got to do all my own structured wiring while the house was being built).

Its important to note that I do not allow access from the wireless network across the internal side of the VoIP router, and I again block packets originating from the wireless router at the 8 port.  The 8 port router and the VoIP router does, however have some rules for open ports that my wife needs to play games, and that I need for various services I have running on my internal network.  Because of this, I’ve added another Cisco PIX firewall in my upstairs office to prevent any inbound requests to my file server, my development PC, and of course the computer I use for family record keeping. The file server is behind the firewall, but I have rules set up to allow access to it from the other house PC’s.

In any case, as I look at my network, I start to realize how imperfect it is.  I’m looking for advice from anyone on how to make it more secure but still provide the needed functionality to our standard home PC’s and to our private personal-data machines.

ASP.NET 2.0 Quickstarts Available

For those of you that don’t know, the ASP.NET 2.0 SDK quickstarts are available.  Obviously, these are just an overview, but it helps to get yourself up and running fairly quickly with features new and old.

Check them out at http://quickstarts.asp.net/QuickStartv20

File uploading in a web environment

A recent question was asked on the ASP.NET forums:

“i’m building a page to let people upload images to my site. they are supposed to see the images after uploading them.
i wondered if somebody can take ane “exe” file, change the extension to “jpg”, upload it, and then run it by pressing the “view picture” button. will my server run this exe file? if yes, how can i check if it image (“jpg” and “gif”) or something else?
great thanks…”

The question actually encompasses a very frequent request that users have. The fact that someone knew to ask about this before just implementing the upload feature proves that the mentality toward securing applications has been changed slightly from “Lets implement as many features as we can” to “How will implementing this feature affect the security of my application”.  This mentality, I feel, is spurred by several factors such as Microsoft’s increased Trustworthy Compupting initiative.

Since I feel its good to reward people for thinking about security first, I will do my best to answer this question for the user here on my blog. In the next few days, I will be launching a series of posts relating to uploading files in a web environment, the security issues, and better ways to implement the feature.  I’ll start by defining the functionality that is ultimately wanted, provide some insight into the various ways to implement this functionality and the security issues surrounding each approach, and then move on to designing a solution and perhaps even provide some sample code.

For now, let’s focus on the functionality and delve a bit into some various ways to implement this feature.

In general, the developer wants users to be able to upload image files to a website, and let someone or several people view the images after they are uploaded. This feature is implemented in the very software that is used to run this blog — Community Server.  Over the years, this feature has been implemented in more ways than I care to count.  Some folks store the uploaded file as is to the file system.  Others have stored the binary data as a BLOB in a database.  Others have used hybrids of this mechanism.  For this portion of the article, we will focus on a file-based approach and follow up with a database approach.

Storing in the File System

The most logical way to store files, it would seem, is in a file system.  From a public internet standpoint, however, one has to consider the trade offs of this approach.  The first problem is that your web user account (most likely the ASPNET account) has to have permission to write to the file system in order to pull this off.  The second issue is that if you write to a file within the scope of your web’s root folder, those files can most likely be accessed through IIS as well (and since this particular user desires for the images to be available to the public, they almost have to be available).  The security concerns are massive.  A user could upload a web-executable script to the server that they could then use as a trojan to gain access to the rest of the server. More advanced hacks could be used to compromise memory buffers and cause an executable to run in the server process (one of the main resons behind the new aspnet_wp running under the context of a low-privileged user).  There are some ways to decrease the risk involved in this method, however.  Some of them involve security through obscurity (not sufficient by itself).  Some of them involve coupling the best of OS security, web security and application security. 

Directory Structure

For starters, consider the fact that you do not want any files that are uploaded to be scriptable on the server side.  So lets set up a directory structure and security for our saved files. Start by creating your main upload directory outside of the web root.  For example, if your web root is found at C:inetpubwwwroot , create your image directory at C:inetpubuploadroot.  So why did we do this?  This prevents anyone from uploading malicious scriptable code to your server that can be executed on the server.  Once you create your directory, make sure you grant read and write permission to ASPNET user (if you are not using impersonation)  or to the Groups and Users which you will allow to upload files.  In my case, I granted the following permissions:

Administrators      Full Control
ASPNET             List Folder Contents, Read, Write
Network Service  List Folder Contents, Read, Write
System                 Full Control

Uploading your File

Once you have set up your directory structure, you’ll need to create your upload page. You only need an HTML file control and a submit button. Once you add your HTML controls, make sure you run them at the server side.

screenshot

Once you’ve set up the page, add the following code to save the file:

private void btnSubmit_Click(object sender, System.EventArgs e) {
 HttpPostedFile file = fleUpload.PostedFile;
 if( file != null ) {
     string serverFilePath = String.Format(   
        @"C:domainstitus.touploadroot{0}",
        file.FileName.Substring( 
        file.FileName.LastIndexOf(@"") ) ); 
        // TODO: Write file validation routine.
        if( ValidateIsJpeg( file ) ) {
           file.SaveAs( serverFilePath );
        }
  }
}

Validating The File

This get’s the job done for uploading, but it still isn’t secure and we haven’t given the user any way to retreive the file.  Now, I’m not an expert in image file types, but I know that each has a format.  I would HIGHLY recommend that you learn the proper methods to validate each file type you intend to accept and write a routine to test the uploaded file against the expected format.  For JPEG files, you can read an online FAQ at http://www.faqs.org/faqs/jpeg-faq/ that will explain the format and give you clues on how to validate this file.   Remember that ALL INPUT IS EVIL and with file uploading, this is particularly important.  However, I’ll leave this implementation up to you since this can get rather complex and will change based on each file type you want to accept. 

In Part #2, we’ll discuss how to retreive images that are stored on the file server using an HttpHandler.

Minutemen Incident — So funny!

msn.com is still set as my home page. This morning there was an article posted that had me chuckling to myself because of the picture at the top of the article.  It was a picture of an ILLEGAL IMIGRANT holding up a T-Shirt. I will never call these people “undocumented workers” so people can help you forget the first thing they do is disrespect our country by breaking the law to get here and disrespecting the many people who stand in line to come here the legal way.

In any case, the funny part was what the T-Shirt said:  “Bryan Barton caught an illegal alien and all I got was this lousy T-shirt.”

I don’t know why it made me laugh so much, but I thought I would share it.

Forms Authentication Cookies in the QueryString

Most folks don’t like their authentication data appearing in the query string, and with good reason. However, one does have to consider some trade offs when enabling cookieless sessions.

A user recently posted the following in an ASP.NET security forum.:

I use Forms Authentication technique in my Web application interactive with mobile devices. The configuration is like the following:

 < authentication mode="Forms" >
   < forms loginUrl="login.aspx" timeout="30" protection="All" path="/" />
 </ authentication >
 < authorization >
   < deny users="?" />
 < /authorization >
 < sessionState mode="InProc" 
      stateConnectionString="tcpip=127.0.0.1:42424" 
      sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" 
      cookieless="true" timeout="20" />

Mangled URLs are used during sessions due to the cookieless=true setting. But why is the authentication cookie (in my case, its name is ASPXAUTH) also carried in the URIs? I know that this cookies is needed to be present in HTTP headers, but what configuration has defined this cookie is embedded in the query string of the URL like this?

http://localhost/myapp/(wcaz3svlxsdfinygyttuiu45)/default.aspx?__ufps=594900&.ASPXAUTH=887134660EC8D9CE8D72

The answer is simple but again requires you to consider the trade-offs when using cookieless sessions. This behavior is largely in part because the SessionStateModule class writes data to the cookies collection using HttpRequest.AddResponseCookie()

The AddResponseCookie() method looks to see if the cookie collection is null (and it will be if you are using cookieless sessions). If it is null, it will then append the asp.net auth cookie to an internal HttpValueCollection. These values are then written to the QueryString when the handler writes the response back to the user.

The fact that this is an cookie doesn’t go away because it’s a “special cookie”. In fact, what good would your cookie do if it was, in fact, written back in the header? The whole reason behind cookieless sessions is to allow sites to work that don’t support client-side cookies. If someone didn’t support cookies, and your authentication cookie was written back in the header instead of the QueryString, your cookie-based forms authentication cookie wouldn’t work either.

Keep this in mind when enabling cookieless sessions and forms authentication together.

String.Format vs StringBuilder.AppendFormat

So here’s a question, what’s the difference between String.Format( string, object[]) and StringBuilder.AppendFormat( string, object[] ) ?

For those of you thate don’t know, String.Format allows you to parameterize your strings, by placing replacement tokens througout a string template.  You can later fill in those tokens with parameterized data by passing it in as an array. Here is an example:

string.Format("I own a {0} and {1} name is '{2}'", 
        "cat", "her", "Ellie" );

Of course this would output the sentence: “I own a cat and her name is ‘Ellie'”.  You can also achieve the same thing by calling StringBuilder.AppendFormat much like the following example:

StringBuilder sb = new StringBuilder();
sb.AppendFormat( "I own a {0} and {1} name is {2}", 
        "cat", "her", "Ellie" );

At first glance, you may be thinking that these are somewhat interchangable with the exception of performance. By now, we all know StringBuilder is usually faster for excessive string manipulation. What you may not know is that string.format calls the StringBuilder.AppendFormat method under the hood.  Additionally, StringBuilder will throw an exception if the number of parameters does not match the number of tokens found in the template string.  Try compiling and running a program that executes the following statement:

[STAThread]
static void Main(string[] args) {
  string x = string.Format(
    "{4} owns a {0} and {1} name is {2}.", "cat", "her", "Ellie" ); 
  Console.WriteLine( x );
  // For those of you that know WriteLine takes format strings... shhhhh
  // I'm trying to make a point here!
}

You will receive the following exception.

An unhandled exception of type 'System.FormatException' 
occurred in mscorlib.dll

Additional information: Index (zero based) must be greater 
than or equal to zero and less than the size of the argument 
list.

This exception is actually thrown by StringBuilder.AppendFormat and the String.Format method bubbles the exception up without providing the consumer with any recourse if the parameters dont’ match up.  However, if you call StringBuilder.AppendFormat yourself, you can place the code in a try catch block, and reap the benefits of a string that has been formatted as best as it can be with the parameters provided. Take the following code, for instance:

[STAThread]
static void Main(string[] args) { 
  StringBuilder sb = new StringBuilder();
  try {
    sb.AppendFormat( "{0} owns a {1} and {2} name is {3}.",
        new object[] {"Tobin", "cat", "her"} );
  }
  catch(FormatException ex) {
    Console.WriteLine("An exception occurred: {0}", ex.Message ); 
  }
  finally { 
    Console.WriteLine( sb.ToString() ); 
  } 
  Console.ReadLine();
}

Notice here that we have caught the exception, reported on it, but can still write the string builder’s text to the screen — or at least the part that was parsed.

Now here’s the questions I pose for all of you:

  1. Since you now know that you can bypass the token-count to parameter-count validation check by using StringBuilder, should you incorporate this into your applications in instances where you don’t know how many parameters you are going to have?
  2. Would you consider this to be a bug, or a reasonable feature and why?

Resume building: An organized approach

Running a retail store from a high-level is a fairly simple concept. As an owner you keep an inventory of your products and you take note of what is selling, and what is not. You may watch for emerging trends and buy new products that relate. When you notice that a product doesn’t sell well, it might be wise not to buy more of that product. When you notice a product is selling off your shelves quickly, you could increase your profitability by stocking up on that item. Trying to “specialize” in too many products can often times cause your business to fail. You end up with too much stock in products that don’t sell, and not enough stock in products that do. This happens mostly because you can only track so many products effectively.

Running your own career should be very similar to running a business. As owner and operator of your career, you should keep track of the skill set you have — taking note of what is needed in the industry and what is not. You watch for emerging technologies and perhaps learn a little about those skills. When you notice a skill is not in demand, perhaps its time to stop “perfecting” that skill. If a skill is high in demand, you would be wise to learn something about that technology. You should pick an area that you like and are comfortable with and stick with it. Trying to keep track of too many skills at once could leave your knowledge a bit too broad in areas where clients and hiring managers want depth. While each person has a different tolerance level, an individual can keep in-depth knowledge with so many technologies at once.

Assessing your Current Inventory


If you are considering taking another position in the near future step back and start your resume from scratch. Take a completely honest assessment of your skills. Put each skill area you have in a list. One-by-one, add your personal skill level assessment beside the skill using one of the following descriptions: Novice, Intermediate, Advanced, and Expert. Use novice only when you feel you have an understanding of the skill but would have to consistently access a resource if you were asked to do a project with that skill. Use intermediate if you know most of a product and rarely have to look for references. Assign yourself a level of advanced if you know the product inside and out but may not understand all of the underlying architecture. Place yourself as an expert if you push the limits of a technology, know how it works under the covers, and could write a book on the topic if asked to.

Determine Market Trends for your Skills


Once you have your list, set it to the side. Start looking for skills that are hot on the market. Check out monster.com in your area, and others across the nation or the world. Determine what companies are looking for. Pour through each job listing and add skills to a list. When you come across a skill that you have already listed, start counting how many times you found it. Look at the dates of the ads to determine if they are still relevant. If you are a Microsoft developer, you could probably get some assistance by looking at the Microsoft Career Center or even at the Microsoft Job’s Blog.

Determine Your Career Specialty


We often get side-tracked in our careers. We are forced to learn things to fill a job duty and we start picking up more and more knowledge in areas that may not be marketable. We tend to stray away from marketable specialties. Should an unfortunate situation arise and you find the need to look for employment again, you may have a skill specialty that is not high in demand. You would then be forced to take yet another position that isn’t optimal for you and you’ll be right back where you started. As an example, I started a dead-end job learning all about printers, printer drivers and printer languages from a small company in Charlotte, NC. I was actually starting to enjoy writing parsers and tools to make development on the company’s product line better. When I finally decided to leave the company to re-align my career, I found that I had lucked out. I wrote my tools in .NET and gained valuable knowledge and skills, otherwise I would have no marketable skills at all. Look at the specialty and determine what skills are core in that area. List those items and your specialty on a piece of paper and keep it near your computer or on the fridge. Look at it every day to help keep you focused on your goal.

Stock Up!


Once you have a list of your skills and a self-assessment of your skill level, compare it with the list you made of the hot, marketable skills needed to get into your specialty. What are you missing? What are you deficient in? What level of skill do you feel you need to be effective in specializing in that skill area? It should be pretty obvious where you fall short, and where you have unneeded skills. Start finding resources to research and learn everything you can about a subject. Set agressive goals to learn about the details of your specialty. Make sure you learn about the skills that are most important to your specialty and you are deficient first.

Keep your goals in mind and try not to wander off target too far. It’s easy to get side-tracked. If you find yourself reading a lot about a different topic consistently, you might consider switching your specialty to that topic or even positioning yourself under both skill sets. For instance, if you decided to specialize in data access and communications, but often find yourself reading about the compact framework, you might consider specializing in data and communications on the compact framework.

Set aside some time each day to pursue these goals. Make it a part of your every day tasks. 30 minutes to an hour a day should keep you on pace. Of course, if you are more aggressive, you can adjust and plan for as much time as you can spare. Don’t spread your learning plan out too thin though. Your skill marketability may be dwindling as you read. It is important that you invest the time to make sure you stock up on this skill properly. Doing this half-heartedly will do you no good.

Use any valuable resources you can get. There are many sites now that offer blogs owned by Microsoft employees. You can search through many of them at the Microsoft Community Blogs Portal. These can be valuable or a waste of time, check out some of these blog lists and look for employees who talk about the topics you want to know about. Get a news aggregator like NewsGator or SharpReader and subscribe to those blogs. It keeps you from having to click on every link every day to see if something new is available. Sometimes the blogs are broken down into categories too. Consider subscribing to only the categories the blogger offers on your specialty. This will keep you from getting caught up in a ton of personal opinions tossed around every day on blogging sites. Search some popular sites with information on your skills. Participate on community forums. Sometimes helping others with a problem will net you some knowledge in the end. Try to use other’s experience and headaches with a technology as your stepping stone to becoming an expert in your area.

Advertise and Market to your Target Audience


When you are ready to seek a job as a specialist in the area you have chosen, do not just spam your resume everywhere. You will still find yourself back in that old familiar position of doing something you didn’t want to do. Go back to your job sites and search for jobs in that category. Don’t just jump into the first job that is offered to you. Make sure that the job being offered truly does fit the job specialty you want before you send out your resume. Tailor your resume to the specific job at the specific company you are applying toward. Make sure you highlight the skills you have against the skills they need and describe in your cover letter how you feel you might best help the company with their needs. Do not just give your resume to a recruiter who thinks he or she has a position to fit your skill set. Most head-hunters just want to fill the seat and collect the check; they will not scrutinize the employer before sending your resume to them.

Targeting your job search will more likely get the career you want, not just any career you can get. Just like a retail store, it does no good to advertise your gun shop at the democratic national convention. It just isn’t a good use of your time or resources. Use the same common sense when advertising yourself and marketing your resume.

A Dose of Reality


Just like running a store, there is always a dose of reality to add to the mix. Things sound very simple when laid out properly. But the devil is always in the details. Sometimes there is a place to draw a line between learning for work, and learning for fun. Don’t pick a specialty you don’t enjoy just because it’s hot and marketable. In the end, you’ll be miserable and you won’t make an effective specialist. There is something to be said for being poor and happy over having money and being miserable getting it. You may choose to pick a specialty that isn’t as hot as the next skill, as long as you get fired up about it. Make yourself happy with your skills first or your employer never will be.

On that note, I have some career planning to do. Drop me a line if you found this article helpful in any way.

Canonicalization issues and File Paths

I keep running into code lately that does some pretty elaborate security testing but in the end leaves a very large issue unaddressed — canonicalization errors related to file I/O.

Michael Howard and David LeBlanc dedicated an entire chapter in Writing Secure Codeto the idea that “All Input is Evil“. The concept is that you should not rely on the fact that input coming from any source — trusted or not is going to be in a format that you accept. You should validate, validate, and validate again even if your I/O function isn’t intended to access anything of great importance.

Canonicalization is the process of breaking something down to it’s most simple form. Let’s take a look at an example:

public FileStream OpenFile( string fileName )
{
string fullPath = string.Format(@”C:inetpubwwwroot{0}”, fileName);
return new FileStream(fullPath, FileMode, FileAccess);
}

You may have ony intended to give this function access to code stored in the default web root. But what would happen if someone called your function like this:

File f = OpenFile(”..\..\windows\system32\config\sam”);

The elipsis at the begining of the path would direct the file to go up a directory and doing it twice would put the user at your root directory, giving someone access to your entire file system.

You could use a regular expression to check for this type of thing right? Well, yes, but it wouldn’t be enough. Your regular expression would test for the “..\” sequence, but what happens if someone encodes the path with %2E instead of the “.”. Would your regular expression be smart enough to check for that? Probably not. There are a ton of ways to represent a path differently and you should use many different forms of validation. However, one of my favorite is to use the DirectoryInfo and FileInfoFullName” property. Creating a new FileInfo instance passing it your intended path will break the path down into it’s actual meaning. Once this is done, you can use the FullName member to obtain the canonicalized path. As a matter of completion, here is some sample code to get you started:

public FileStream OpenFile( string fileName )
{
string fullPath = string.Format(@”C:inetpubwwwroot{0}”, fileName);
string canonicalizedPath = new FileInfo(fullPath).FullName;
return new FileStream(canonicalizedPath, FileMode, FileAccess);
}

Another option available is to use the Path class as follows:

public FileStream OpenFile( string fileName )
{
string fullPath = string.Format(@”C:inetpubwwwroot{0}”, fileName);
string canonicalizedPath = Path.GetFullPath(fullPath);
return new FileStream(canonicalizedPath, FileMode, FileAccess);
}

This performs the same work under the covers without creating a FileInfo instance.

This example does not explain everything that a dilligent coder worried about security should do to secure this method, but it should demonstrate the usefulness of the FullName member in validating file paths.