Saturday, February 6, 2010

Implementing MoveCorresponding in .Net

I started my professional career coding in COBOL and fell in love with two sweet commands:

  • Move Corresponding – which moved data between two structures (objects) if the column name and types corresponded
  • Perform Corresponding – which effective gave me a form of inheritance

I have often in later years wished that there was the equivalent in Basic, Visual Basic, C# etc., especially when I have to type 50 lines of C# code where I could have just typed one line in COBOL. In COBOL it simply invoked a comparison in the compile that wrote out the explicit instructions. In .Net, we do it on the fly, so it is not as machine efficient.

 

Well, it turns out that it is easy to write an extension for it. Whether to use the extension is a debatable topic.

  • It reduces the number of lines of code greatly
  • It causes the code to be at a higher level
  • It allows rapid code evolution because as new properties are added, there is no need to go back and update earlier code – it happens automatically.
  • On the minus side, you are not micromanaging the code

So for better or worst, here is an example of such an extension – enjoy!

using System.ComponentModel;
public static int MoveCorresponding(this object source, object dataItem)
{
    int ret = 0;
    PropertyDescriptorCollection sprops = TypeDescriptor.GetProperties(source);
    PropertyDescriptorCollection dprops = TypeDescriptor.GetProperties(dataItem);
    bool excludeId;
    if (dataItem is Control)
        excludeId = true;
    else
        excludeId = false;
    foreach (PropertyDescriptor sdesc in sprops)
        if (!excludeId || String.Compare(sdesc.Name, "Id", true) != 0)
        {
            PropertyDescriptor ddesc = dprops.Find(sdesc.Name, true);
            if (ddesc != null && !ddesc.IsReadOnly && ddesc.PropertyType == sdesc.PropertyType)
                try
                {
                    if (sdesc.GetValue(source) != ddesc.GetValue(dataItem))
                    {
                        ddesc.SetValue(dataItem, sdesc.GetValue(source));
                        ret++;
                    }
                }
                catch
                {
                    //Exceptions here are either value issues or 
                    // state issues (for example EnableTheming on web controls )
                }
        }
    return ret;
}

Friday, February 5, 2010

Dongle for testing and supporting multiple languages (cultures) Web Sites

If you ever tried testing a web-site in different languages you know the pain of having to monkey with browser settings. The trick below shows an easy way to test different languages without changing browser settings.  The mechanism is to add a parameter to the request that will toggle the request to a different culture and persist it via a cookie.

In Global.asax

This location is needed because it must happen before any processing starts or you may end up with a mixed language page.  It also makes it very clean because it applies to every page and request.

 

void Application_BeginRequest(object sender, EventArgs e)
{

    string ci = "ci";
    if (!String.IsNullOrEmpty(Request[ci]))
    {
        try
        {
            
            System.Globalization.CultureInfo requestedCulture = System.Globalization.CultureInfo.GetCultureInfo(Request[ci]);
            System.Threading.Thread.CurrentThread.CurrentCulture = requestedCulture;
            System.Threading.Thread.CurrentThread.CurrentUICulture = requestedCulture;

            HttpCookie cookie = new HttpCookie(ci, requestedCulture.Name);                
            cookie.Expires = DateTime.Now.AddMonths(3);                
            Response.Cookies.Add(cookie);
        }
        catch (Exception exc)
        {
            ErrorLogging.AddError(exc);
        }
    }
    else if (Request.Cookies[ci] !=null)
    {
        try
        {
            if (string.IsNullOrEmpty(Request.Cookies[ci].Value))
            {
                System.Globalization.CultureInfo requestedCulture = System.Globalization.CultureInfo.GetCultureInfo(Request.Cookies[ci].Value);
                System.Threading.Thread.CurrentThread.CurrentCulture = requestedCulture;
                System.Threading.Thread.CurrentThread.CurrentUICulture = requestedCulture;
            }
        }
        catch (Exception exc)
        {

            ErrorLogging.AddError(exc);
        }
    }
}

Further more, on a login page you could ask what language they wish and set the cookie from this value. The nice thing is that when they return to the site next time they will get the page in the requested language (assuming the same browser) because the cookie is persisted.

Testing different languages is now trivial,  http://localhost/home.aspx?ci=fr-CA  will set the site to French-Canadian for the next 3 months unless you change the language. You can also give the user a choice of language without having to detect and use the browser settings.

Thursday, February 4, 2010

An easy fix to the single question/answer shortfall with the Membership Provider

Using question/answer to recover a forgotten password is a normal practice on the web. Unfortunately the canned Membership providers use only one question and answer.  In reality, you have the answer being a PASSWORD – a very very weak password very easy to attack with a dictionary attack.

 

There is an easy fix without having to write your own membership provider. You stuff as many questions as you want into the existing field using a tab (\t) as a delimiter. Tabs can never be entered from a single line text box so there is no risk of a question or answer containing a tab (\t).

 

The extensions below allows you to have as many questions as you wish using the same old providers!

 

/// <summary>
/// Delimited is a tab. This cannot be entered in a web text box
/// </summary>
private static char[] MembershipUserQuestionSeperator = { '\t' };

/// <summary>
/// Gets the questions to be asked
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public static string[] GetQuestions(this MembershipUser user)
{
    return user.PasswordQuestion.Split(MembershipUserQuestionSeperator, StringSplitOptions.RemoveEmptyEntries);
}

/// <summary>
/// Retrieves the password via a set of questions
/// </summary>
/// <param name="provider"></param>
/// <param name="username"></param>
/// <param name="answers"></param>
/// <returns></returns>
public static string GetPassword(this MembershipProvider provider, string username, string[] answers)
{
    var answer = new StringBuilder();
    for (int i = 0; i < answers.Length; i++)
    {
        answer.Append(answers[i]);
        if (i < answers.Length - 1)
            answer.Append(MembershipUserQuestionSeperator[0]);
    }
    return provider.GetPassword(username, answers.ToString());
}

public static bool ChangePasswordQuestionAndAnswer(this MembershipUser user, string password, string[] questions, string[] answers)
{
    if (questions.Length != password.Length)
    {
        throw new ArgumentException("Mismatched numbers of questions and answers");
    }

    var question = new StringBuilder();
    var answer = new StringBuilder();
    for (int i = 0; i < answers.Length; i++)
    {
        answer.Append(answers[i]);
        question.Append(questions[i]);
        if (i < answers.Length - 1)
        {
            answer.Append(MembershipUserQuestionSeperator[0]);
            question.Append(MembershipUserQuestionSeperator[0]);
        }
    }
    return user.ChangePasswordQuestionAndAnswer(password, question.ToString(), answer.ToString());
}

For the Sql Membership Provider, you should increase the column size, such as shown below.

 

Alter Table dbo.aspnet_Membership Alter Column PasswordQuestion nvarchar(max)
Go
Alter Table dbo.aspnet_Membership Alter Column PasswordAnswer nvarchar(max)
Go

The default length is 256 characters for questions which will usually handle at least 5 typical questions. The default length is 128 characters for answers, enough for 11 answers of 10 characters each.

 

How many questions should you have?

The typical person has a working vocabulary of some 1000 words. Taking  a-zA-Z0-9 + special characters, we have ~100 characters. So 6 characters is 10^12 odds ( (10^2)^6). To get the same security with answers, it means 4 questions ((10^3)^4). So questions and answers should use four questions.

Wednesday, February 3, 2010

Controlling AspX page access from the database

Roles and sitemaps do not always suffice for controlling access to pages. Often pages are not in the Sitemap and using roles assumes that you are doing coarse permissions or have a simple permission structure.

 

The class below allows a list of allowed pages to be cached in a user’s Session and then a user’s permission may be determined from this list. The PageList should be set during authorization (i.e. post login) and removed on logout. Using a session object means you avoid database hits and thus can scale better (you can web-farm the load).

 

/// <summary>
/// Utility class that may be used to determine if a user has permission to a specific page.
/// </summary>
public static class Page
{
    /// <summary>
    /// The list of pages that the user may access. Only the pages starting "~" 
    /// are filtered.
    /// </summary>
    public static List<String> PageList
    {
        get
        {

            if (System.Web.HttpContext.Current != null)
            {
                return (List < String >) System.Web.HttpContext.Current.Session["_PagePermissions_"];
            };
            return null;
        }
        set
        {
            List < String >data = value;
            if (data != null)
            {
                for (int i = 0; i < data.Count; i++)
                {
                    data[i] = data[i].ToLower();
                }
            }
            System.Web.HttpContext.Current.Session["_PagePermissions_"] = data;
        }
    }

    /// <summary>
    /// May the current Session user access this URL. If there is no List(Null), then ALL pages
    /// are assumed accessible (because Unauthorized or Login pages would be denied access).
    /// An empty list results in all being denied
    /// </summary>
    /// <param name="relativeUrl">A Relative URL starting with ~</param>
    /// <returns>true if allowed or the relative url does not start with a ~ or there is no list</returns>
public static bool HasAccess(string relativeUrl)
{
    char[] sep = { '?' };
    var data=relativeUrl.ToLower();
    if (relativeUrl.StartsWith("~") && PageList !=null )
    {
        // We need to split off parameters i.e. default.aspx?id=434
        var parts = relativeUrl.Split(sep, StringSplitOptions.RemoveEmptyEntries);
        foreach (var item in PageList)
        {
            if (String.Compare(item, parts[0]) == 0)
            {
                return true;
            }
        }
        return false;
    }
    else
    {
        return true;
    }
}

    /// <summary>
    /// May the current Session user access this URL
    /// </summary>
    /// <param name="relativeUrl">An object with a "NavigateUrl" property starting with ~</param>
    /// <returns>true if allowed or the relative url does not start with a ~</returns>
    public static bool HasAccess(object hyperlink)
    {
        return HasAccess(hyperlink.GetProperty("NavigateUrl"));
    }
}

The last function does a little magic, because it allows you to pass in any control with a NavigateUrl property to be checked. The code for GetProperty() is shown below. It’s can be a very helpful extensions.

 

/// <summary>
///  Returns the specicfic property from the object
/// </summary>
/// <param name="dataItem">An object with properties</param>
/// <param name="field">Name of property</param>
/// <param name="defaultvalue">Value to use if missing</param>
/// <returns>the value as a string, or a null if not found</returns>
public static string GetProperty(this object dataItem, string field, string defaultvalue)
{            
    if (String.IsNullOrEmpty(field)) return defaultvalue ?? null;
    PropertyDescriptorCollection props = TypeDescriptor.GetProperties(dataItem);
    if (props.Count > 0)
    {
        if (props[field] !=null &&  null != props[field].GetValue(dataItem))
        {
            return props[field].GetValue(dataItem).ToString();
        }
    }
    return null;
}

/// <summary>
///  Returns the specicfic property from the object
/// </summary>
/// <param name="dataItem">An object with properties</param>
/// <param name="field">Name of property</param>        
/// <returns>the value as a string, or a null if not found</returns>
public static string GetProperty(this object dataItem, string field)
{
    return GetProperty(dataItem, field,null);
}

This allows you to check a control during page load and hide items that they cannot go to. It also allows you to check if they can access the current page.

BigDrive Promotion

BigDrive is almost ready for Beta and we have been working hard on the web site: BigDrive.  I want to get in a shameless plug here to grease the search engine’s wheels, allowing them to find and traverse our site.  BigDrive is a user space interface definition for a file system.  Allowing programmers to write provider and clients that emulate file system on Windows.  It ships with providers for Amazon S3 and Windows Azure Storage, and a single client that integrates into the desktop.  The desktop supports all providers that implement the BigDrive interfaces.

 

{6230289B-5BEE-409e-932A-2F01FA407A92}

Sunday, January 31, 2010

Using Resources / Resx objects on a Web Site

In an earlier post I showed how you can take any Resources object(especially PinnedBufferMemoryStream and UnmanagedMemoryStreamWrapper) and obtain it’s byte[]. Unfortunately on the web this is not sufficient because when the byte[] is sent to a client. The browser expects a mime type header to tell it how to handle the byte[]. A partial solution is to simply map each mnemonic to a specific known mime type. This is a fragile solution.

 

If you are doing localization then a mnemonic such as SiteLogo may be different mime types in different languages, for example:

  • en-US: image is a  .bmp
  • es-Mx: image is a  .png
  • fr-CA image is a  .gif (animated)
  • dk-DK image is a  .tiff

Of course, one could simply specify to human-ware that they must all be the same. In reality, if the web-site is a commercial product, this will usually break down.

 

The practical solution is to use Magic Numbers. Use the magic number to determine the file type, then lookup the mime type based on the file type.  Gary Kessler has a good set of magic numbers AKA File signatures to get you started. In a new file type shows up, then just collect a sample and run a utility to obtain additional file signatures.

Capturing Signatures

We create two structures to capture the file signatures:

struct MagicNumberInternal
{
    /// <summary>
    /// Number of bytes offset
    /// </summary>
    public Int32 Offset;
    /// <summary>
    /// The byte sequence expected there
    /// </summary>
    public Byte[] Offsetbytes;
}

struct MagicNumberSet
{
    /// <summary>
    /// The bytes expected at the start of the file
    /// </summary>
    public Byte[] Startbytes;
    /// <summary>
    /// The bytes expected at the end of the file
    /// </summary>
    public Byte[] Endbytes;
    /// <summary>
    /// The bytes expected at some offset in the file
    /// </summary>
    public MagicNumberInternal[] Offsets;
}

   The next item is defining how to input the magic numbers. I opted for a simple string format as shown below:

  • AA3G – the leading bytes
  • *AA3G – the ending bytes
  • AA3G*AA3G  – trailing and leading bytes
  • *12345:AA3G* – bytes located at an offset of 12345.
  • AA3G|FFEEAC – Alternative leading characters
  • BBCC*12345:AA3G*987654:AA3G*FFEE – leading, ending and two sets of offset bytes.

This information is parsed easily as shown below:

/// <summary>
/// Character to separate sets
/// </summary>
char[] barSep = { '|' };
/// <summary>
/// Character to separate parts:  AA*AA is start end,
/// AA*4563:FF*EE  means start with AA, at 4563 bytes FF is found, ends with EE
/// </summary>
char[] pSep = { '*' };

/// <summary>
/// Delimiter between bytes offset (int) and the bytes (Hex)
/// </summary>
char[] offSep = { ':' };
Queue<MagicNumberSet> _MagicNumberPatterns;

ParsePattern

/// <summary>
/// Parses the magic pattern into a structure.
/// </summary>
/// <param name="pattern">A Magic Number pattern, for example AA*4563:FF*EE  </param>
/// <returns>An structure with the contents ready for use</returns>
MagicNumberSet ParsePattern(string pattern)
{
    var mniQueue = new Queue<MagicNumberInternal>();
    MagicNumberSet ret = new MagicNumberSet() { Startbytes = null, Endbytes = null, Offsets= null };
    var parts = pattern.Split(pSep, StringSplitOptions.None);
    for (var i = 0; i < parts.Length; i++)
    {
        switch (i)
        {
            case 0:
                if (!String.IsNullOrEmpty(parts[0]))
                    ret.Startbytes = parts[0].FromHexToByte();
                break;
            default:
                if (!String.IsNullOrEmpty(parts[i]))
                {
                    var breakdown = parts[i].Split(offSep, StringSplitOptions.RemoveEmptyEntries);
                    switch (breakdown.Length)
                    {
                        case 1:
                            ret.Endbytes = breakdown[0].FromHexToByte();
                            break;
                        case 2:
                            mniQueue.Enqueue(new MagicNumberInternal { Offset = int.Parse(breakdown[0]), Offsetbytes = breakdown[1].FromHexToByte() });
                            break;
                        default:
                            throw new DataMisalignedException(String.Format("The MagicNumber string {0} is invalid", pattern));
                    }
                }
                break;
        }        
    }
    return ret;
}

 

The test to see if there is a match is shown below:

 

IsMatch

/// <summary>
/// Takes some data and see if there are any matches.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
bool IsMatch(byte[] data)
{
    foreach (MagicNumberSet set in _MagicNumberPatterns.ToArray())
    {
        bool pre = set.Startbytes == null || PrefixMatch(data, set.Startbytes);
        bool post = set.Endbytes == null || PostfixMatch(data, set.Endbytes);
        bool mid = true;
        if (set.Offsets != null)
        {
            foreach (var item in set.Offsets)
            {
                if (item.Offset < 1 || item.Offsetbytes == null || OffsetMatch(data, item.Offsetbytes, item.Offset))
                {
                    mid = false;
                }
            }
        }
        if (pre && post && mid)
        {
            return true;
        }
    }

        if (mniQueue.Count > 0)
       {
           ret.Offsets = mniQueue.ToArray();
       }

    return false;
}

Thus, the supports multiple signatures for one file type with complex matching patterns.  An dictionary of <magicNumberSignature, MimeType> is the closing piece of the solution.

Post Script:

Some of the supporting functions are shown below.

 

FromHexToByte

/// <summary>
/// Utility to take 0x2DF8 or 2DF8 into a byte array
/// such as { [2D],[F8] }
/// </summary>
/// <param name="value">A string </param>
/// <returns>an equivalent byte arra</returns>
public static byte[] FromHexToByte(this string value)
{
    Queue<byte> ret = new Queue<byte>();
    var local = value.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ? value.Substring(2) : value;
    for (int i = 0; i < local.Length; i = i + 2)
    {
        ret.Enqueue(Byte.Parse(local.Substring(i, 2), System.Globalization.NumberStyles.HexNumber));
    }
    return ret.ToArray();
}

PostfixMatch

/// <summary>
/// Determines if the bytes match for the first common bytes
/// </summary>
/// <param name="a">a byte array</param>
/// <param name="b">a byte array</param>
/// <returns>true if all of the bytes are the same</returns>
static bool PostfixMatch(byte[] a, byte[] b)
{
var length = a.Length > b.Length ? b.Length : a.Length;
for (int i = 0; i < length; i++)
{
if (a[a.Length - i] != b[b.Length - i])
  {
   return false;
  }
}
return true;
}

OffsetMatch

/// <summary>
/// Determines if the bytes match for the first common bytes
/// </summary>
/// <param name="a">a byte array</param>
/// <param name="b">a byte array</param>
/// <returns>true if all of the bytes are the same</returns>
   static bool OffsetMatch(byte[] a, byte[] b, int offset)
       {
           if (a.Length < b.Length + offset)
           {
               return false;
           }
           for (int i = 0; i < b.Length; i++)
           {
               if (a[i + offset] != b[i])
               {
                   return false;
               }
           }
           return true;
       }

PrefixMatch

/// <summary>
/// Determines if the bytes match at the specified offset
/// </summary>
/// <param name="a">a byte array</param>
/// <param name="b">a byte array</param>
/// <returns>true if all of the bytes are the same</returns>
   static bool PrefixMatch(byte[] a, byte[] b)
       {
           var length = a.Length > b.Length ? b.Length : a.Length;
           for (int i = 0; i < length; i++)
           {
               if (a[i] != b[i])
               {
                   return false;
               }
           }
           return true;
       }

Saturday, January 30, 2010

NEVER use NT Groups to control SQL Server Permissions

About a year I go I looked at the use of Windows Group as a mechanism for controlling access to SQL Server for a PCI-DSS project. PCI requires that all access be done by accounts that uniquely identify the user. Best practices mandate Integrated Security so we must use Windows Users. There were two apparent solutions:

  • Associate each Windows user to appropriate SQL Login
  • Associate each Windows user to a Windows Group that is associated to a SQL Login

Since the expected administrators of the PCI application are not DBA’s and may not be SQL Server knowledgeable, the second approach looked ideal. Their role is to determine who may or may not access the application.

This simple idea had a nasty gotcha:

  • Grant SQL permissions to anyone in the Windows Group.
    • This work perfectly for granting permissions
    • I expected that if a user was removed from the Windows Group, permissions would be immediately removed.

UNFORTUNATELY, if a Windows user was removed from the Windows Group, the user still retained SQL permissions.

Some Bingoogling found that the issue is known in another context and cited on an official Microsoft Site:

Note If you use SQL Server integrated security, keep in mind that if you grant a Windows NT user group access to the SMS site database, this permission is not dynamic. As new users are added to the Windows NT user group, they are not given SQL Server security rights unless you add them individually.

technet.microsoft.com 

 

Hence the apparent behavior of granting permissions to users in the current Windows group (a snapshot) appears to be confirmed. So this preferred approach totally collapses:

  • When the association is originally done, it grants all NT Users the specified permission. The User Group is nothing more than a temporary container.
  • Adding a user to the User Group later will NOT result in permissions being given.
  • Removing a user to the User Group later will NOT result in permissions being revoked.

In practice,  when someone adds a user to the NT Group they will expect it to work. When the user complains, the admin will assume something went wrong and manually add the user’s SQL Permission. When they remove a user from the NT Group, they will also expect it to work – after all, looking under Security, they not see the user, just the NT Group; the user will not complain – and you have a security breach.

 

Bottom line: The Domain/Windows Users must be individually added to and removed from SQL Server (for example mapped to a specific SQL Login). Never use a NT Group account to control permissions is SQL Server. If you are ever called on to audit a system, check if any NT Groups are used – if so, feel free to go berserk (it is totally justified)!

Will it be fixed? Is this a bug?

Including the ability to assign Windows/Domain Group to a SQL Login is the bug.  A Domain/NT Group can consist of Domain/NT groups – so doing a dynamic permission could means every query may require a Remote Domain Server to be contacted and hundreds of groups walked.  This can be a performance devastating overhead. To resolve the group to individual users and grant those users the permission is a logical solution.  The presentation/representation is the bug, not the behavior. The presentation should read “Grant to all current NT Users in this NT Group” as an action, SMS  not show any NT Group tied to a login <—that is the bug! It misleads most users.

 

Another way to view it is this: It is easy to walk all children of a NT Group to get a list of all NT Users under that NT Group and then give those users permissions tied to their NT User account. The reverse is an exploding search problem: To find out in an user has SQL permission, you have to walk all of the NT Groups that they belong;  then the NT groups that those groups belong to, etc until you have exhausted the parentage of every group that the user may belong to OR until you found the needed permission. It is a potential complete enumeration problem – manageable for a small domain with few NT Groups – but with a large corporation it can result in a massive number of groups that must be traced upwards until SQL permission is found or the NT groups are exhausted – a SQL performance buster because every security operation may take minutes to determine if it is allowed.