Why Use LINQ To SQL (Part 2 - Security) - Hilton Giesenow's Jumbled Mind

Hilton Giesenow's Jumbled Mind

the madness that is...

News

This is my little spot in cyberspace where you will find a collection of random (but mostly software-related) thoughts and ideas that are frightening in their shining brilliance (or something like that ;->).
 
Please enjoy your stay and feel free to Contact Me.
 
Microsoft MVP

.Net Links

BlogRoll

Misc. Links

Syndication

Why Use LINQ To SQL (Part 2 - Security)

[This is part 2 of an ongoing series. View Part 1 - Performance Considerations]

A short while ago I posted about the fact that I'm currently working on a real-world enterprise project using LINQ to SQL. I mentioned that the choice of LINQ To SQL was based on a number of criteria (in priority order):

  • Performance
  • Security
  • Development Time and Effort
  • Maintenance Time and Effort
  • Stability

    In the previous post, we discussed the first topic - performance considerations in LINQ To SQL - in some detail. In this post we will continue this evaluation by looking into the topic of security in terms of this new OR/M. As before, we will break down the discussion into a number of specific sub topics.

    A note before we begin: Security is a critical topic, as we're all used to hearing about. However, recall that our focus is on LINQ To SQL, and the impact it may or may not have on security, specifically in an ASP.Net context. While the topics are important, we will not discuss topics like Cross Site Scripting, Cross Site Request Forgery, AJAX Security (the last link, for those who're interested, is to my DevDays 2007 slide deck on ASP.NET AJAX Security). If you're interested in those topics, there are some great links available (1) (2). In terms of LINQ to SQL security though, it's only fair to compare apples with apples, so to speak, so we need to bare in mind we're dealing only with data access layer security.

    Stored Procedures

    We had quite an intense look into performance related issues around Stored Procedures in the last post because LINQ To SQL favours inline (generated) SQL instead. Recall that LINQ To SQL can easily make use of stored procedures for queries and for updates.. Incidentally, one point I intended to mention on the topic is that, in terms of the argument that DBAs can tweak sprocs to improve DB performance, the truth is that arguably the primary and most common gain in performance that a DBA does most regularly relates to maintaining and improving indexing on the database, not managing CRUD sprocs. Sprocs are coming up here again because probably the second most common argument in favour of sprocs is the supposed security they provide. As Jeff Atwood puts it (in a blog post NOT in favour of stored procs, he's just putting forward the arguments we're all used to hearing):

    Stored procedures can be individually secured within the database. A client can be granted permissions to execute a stored procedure without having any permissions on the underlying tables.

    This is one of the two primary security arguments put forward in favour of sprocs. The other relates to parameterised SQL queries (effectively overcoming a SQL injection attack). However, without rehashing another old topic too much, this is completely overcome (from a database CRUD perspective at least) with simply using parameterised queries, so I'm not even going to give this point any more airtime.

    Back to the "individually securing areas of the database" with sprocs argument. Now, in my mind this is more theoretical because I find in practice it holds little water. On the one hand, I quite commonly will hear clients espouse this concern as a reason to use sprocs, then simply go and use an 'sa' or equivalent account for the data access anyway! In addition, the majority of the systems I've seen with sprocs follow something like the following. The have a table (e.g. Customers), then have at least the following sprocs, following a very strictly defined naming convention:

    • spCustomer_Insert
    • spCustomer_Update
    • spCustomer_Delete
    • spCustomer_GetByID
    • spCustomer_GetAll
    • spCustomer_Get... (numerous of these)

    Now, if an attacker was able to get hold of the text of your inline SQL calls, and from there possibly derive your schema and wreak havoc, they could just as easily then see the name or names of these sprocs and quickly derive your common naming convention! This will pretty much give him the same access if he would have had anyway! If you're concerned about internal security (i.e. developers not being able to access the tables directly) the situation is even worse for the same reason. I've been there working on an emergency late at night with NO TABLE ACCESS because we were "NOT allowed to be modifying the database without approval" and I simply called the correct sprocs in the correct order (don't worry, I had approval, we just couldn't get hold of the DBA). If you are legitimately worried about specific operations on specific tables (which you should be), you can just as easily create an account and set the specific CRUD rights on the table. With proper user schema separation in SQL 2005 this allows you to lock down to specific rows and specific column-level security, really giving the granularity that was argued from a sproc perspective.

    SQL Connection

    Once again, this is not a LINQ To SQL-specific issue, but please remember to use a secure database connection string, regardless of your data access technique. If you're not sure how easily it is to do this, you can check out my How Do I: Encrypt My Web.Config File? video on MSDN. Again, please take this as a very important point. It's so important, in fact, that it is (laughably) the only point that Microsoft lists in the MSDN library under Security in LINQ to SQL. To quote 50% of the "article":

    Security risks are always present when you connect to a database, especially if the password is clearly readable in the connection string.

    Application Architecture Security Concerns

    As I've mentioned a few times above, the issues we've discussed above under "LINQ To SQL" security have really been around in various data access approaches and OR/Ms. Arguably, we now come to something more unique to LINQ To SQL. The nature of how LINQ To SQL implements its classes and the datacontext, because of how the validation works and also because they are your business classes, means that you are forced to mix certain aspects of your layering more closely than might otherwise be the case. For instance, if you look at the Product class generated by the LINQ To SQL Designer, you will find the following code shortly after the class definition:

    #region Extensibility Method Definitions
    partial void OnLoaded();
    partial void OnValidate(System.Data.Linq.ChangeAction action);
    partial void OnCreated();
    partial void OnProductIDChanging(int value);
    partial void OnProductIDChanged();
    partial void OnProductNameChanging(string value);
    partial void OnProductNameChanged();
    partial void OnSupplierIDChanging(System.Nullable<int> value);
    partial void OnSupplierIDChanged();
    partial void OnCategoryIDChanging(System.Nullable<int> value);
    partial void OnCategoryIDChanged();
    partial void OnQuantityPerUnitChanging(string value);
    partial void OnQuantityPerUnitChanged();
    partial void OnUnitPriceChanging(System.Nullable<decimal> value);
    partial void OnUnitPriceChanged();
    partial void OnUnitsInStockChanging(System.Nullable<short> value);
    partial void OnUnitsInStockChanged();
    partial void OnUnitsOnOrderChanging(System.Nullable<short> value);
    partial void OnUnitsOnOrderChanged();
    partial void OnReorderLevelChanging(System.Nullable<short> value);
    partial void OnReorderLevelChanged();
    partial void OnDiscontinuedChanging(bool value);
    partial void OnDiscontinuedChanged();
    #endregion

    This code defines the partial methods that will be called at appropriate times during the lifetime of your object. For instance, the OnLoaded gets called when the object is loaded. Similarly, OnValidate gets called when an object is going to be saved to the database. The matching On[Property]Changing and On[Property]Changed methods of course get called during the set of these properties. Aside from the individual tables, the datacontext itself has various methods defined:
    #region Extensibility GetHealthy Definitions
    partial void OnCreated();
    partial void InsertCategory(Category instance);
    partial void UpdateCategory(Category instance);
    partial void DeleteCategory(Category instance);
    partial void InsertProduct(Product instance);
    partial void UpdateProduct(Product instance);
    partial void DeleteProduct(Product instance);
    partial void InsertCustomer(Customer instance);
    partial void UpdateCustomer(Customer instance);
    partial void DeleteCustomer(Customer instance);
    partial void InsertOrder(Order instance);
    partial void UpdateOrder(Order instance);
    partial void DeleteOrder(Order instance);
    partial void InsertOrder_Detail(Order_Detail instance);
    partial void UpdateOrder_Detail(Order_Detail instance);
    partial void DeleteOrder_Detail(Order_Detail instance);
    #endregion
    In order to get these methods working, you need to put the code into partial classes that exist in the same project. For the Product class, this means it must be in the same project as the Product class definition created on the designer. An example of this appears below:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace WhyLinQ
    {
        public partial class Product
        {
    
            partial void OnValidate(System.Data.Linq.ChangeAction action)
            {
                //do some validation logic here
            }
    
        }
    }
    

    This is turn means that the validation code, which is part of the business logic, needs to sit in the same layer as the database access code. From a security perspective, this limits how these two concerns can be physically separated. In my experience, separating the business and data layers is rarely implemented, certainly not for security reasons. It is far more common to split the business and UI layers, for instance behind a service, so this concern is perhaps not a big one.


    With that we've covered the 2nd primary concern in evaluating LINQ To SQL. Next up, and time-permitting, I'll be looking into what concerns we had relating to developer productivity and effort, both in terms of using LINQ as well as in terms of the ramp up it requires.

  • Comments

    cjlotz said:

    Hilton

    Great post!  I'm currently in two minds w.r.t using LINQ to SQL for major projects.  Some people like Mats Helander have blogged about the optimistic concurrency being broken and Jimmy Nillson mentions that there is no support for persisting ValueObjects (I'm a fan of Domain Driven Design).  Wanting to use an ORM, this leads me towards currently favouring NHibernate above LINQ to SQL. I'm not too excited about the Entity Framework as I feel it will need time to mature.  However, the LINQ support is so compelling in LINQ to SQL and still only in development for NHibernate.  

    What are your thoughts on the current short-comings in LINQ to SQL and choosing an alternative .NET ORM like NHibernate?

    Carel

    # February 21, 2008 8:36 PM

    Why Use LinQ To SQL (Part 1 - Performance Considerations)? - Hilton Giesenow's Jumbled Mind said:

    Pingback from  Why Use LinQ To SQL (Part 1 - Performance Considerations)? - Hilton Giesenow&#39;s Jumbled Mind

    # February 22, 2008 7:17 AM

    Chris Rock said:

    Added to my list of LINQ TO SQL Tutorials, Articles and Opinions

    # February 23, 2008 2:27 PM

    hiltong said:

    Hi Carel,

    Both frameworks attempt to address the ORM space. However, they have different goals and at times even different audiences.  There is a lot of crossover, which is why they can be evaluated together, but it is important to assess exactly what your goals and requirements are. For me, I wanted something that was reasonably powerful, catered for everything I needed on my project, and was quick for the various teams to pick up. With any technology there is a learning curve, and I  can see that a number of issues people are facing relate to just learning. Having a designer is a big help though, relative to nHibernate. In short, it depends on what you need, how you plan to use it, and what your team make up is, I guess.

    # February 26, 2008 5:15 PM

    create sql sa user said:

    Pingback from  create sql sa user

    # April 30, 2008 4:08 PM

    martillo said:

    Thank you for this excellent series of articles!

    I've pretty much made the decision to go with LINQ to SQL for a new ASP.Net app. Big deadline looming, so I'm hoping the ramp up time is minimal...

    If you ever have the time I would be very interested to know how you've integrated LINQ to SQL into the architecture of your current application.

    # May 2, 2008 8:07 AM

    hilton said:

    Hi Martillo,

    Thanks for the feedback. We're reaching the end of our LINQ To SQL projects (parts are live already) and we found it to have been a very worthwhile approach. As you  noted though, gettig the architecture right was important, so I'm hoping to put some posts together on it soon. Keep an eye out for them.

    Hilton

    # May 2, 2008 8:28 AM

    Brianary said:

    Great article! This really makes me re-examine my sproc-only development philosophy.

    I do have one question, though:

     "With proper user schema separation in SQL 2005 this allows you to lock down to specific rows and specific column-level security, really giving the granularity that was argued from a sproc perspective."

    What? Column-level security, sure, but row-level security? How, exactly, can you provide row-level security with schema permissions?

    I think row-level security support would be the last nail in the coffin for sproc-only development.

    # May 19, 2008 7:54 PM

    hiltong said:

    Hi Brianary,

    It's possible using a combo of schemas, synonyms and views. There are other, arguably better was to do it though (many ways to skin a cat.. ;>). It's a bit arbitrary, but to some extent so is using Sprocs to limit row access in some ways.

    # May 21, 2008 10:57 AM

    G0ggy said:

    Is this not just full circle to having command/SQL text in code pages/web pages?

    # June 27, 2008 12:16 PM

    hiltong said:

    Hi G0ggy,

    No, for a few reasons this doesn't have the same disadvantages. This code should be put into a backend library anyway, not directly into the code of the website. In addition, there are a bunch of drawbacks of having inlines sql as a string in your project that this doesn't suffer from, like tight schema sensitivity.

    # June 27, 2008 12:57 PM

    G0ggy said:

    So what we're talking about here is the disposal of the stored procedure, which I'm not against, and the enforced use of C#, which I may be againt ;)

    # June 27, 2008 2:11 PM

    hiltong said:

    Well, it doesn't totally remove Sprocs, as they are still available and, in some instances, still preferred. It does mean they're not required though. In addition, it means not just "enforced" C# (or VB.Net ;>) but also that you don't need to switch between C# and TSQL. It means shorted dev times, less maintenance, and less chance for errors, which I'm all for.

    # June 27, 2008 2:42 PM

    MattM said:

    Interesting reading.  Thanks for taking the time.  I'll be looking forward to your remaing articles.  You mentioned you'd used code generation previously.  Have you been able to adapt this to integrate LINQ data access?  

    One thing I do like about code generation for the DAL is consistency in approach and free developers up to focus on the user experience.

    # July 8, 2008 10:52 AM

    hiltong said:

    Hi Matt,

    Thanks for the feedback. I haven't had a decent gap to continue the series, but I'm hoping to do a "post-mortem" follow up now that some of the systems are live and successful. Initially, I didn't give much thought to code gen, relying on the LINQ To SQL-generated class and extending them with partial classes. The last few weeks though I've been combining the two techniques and making some great additions to LINQ To SQL using T4 Templates (see my post on T4 if you're not familiar with the tool - dotnet.org.za/.../t4-template-items.aspx).

    I agree completely with your points, and would add as benefits the significantly reduced dev and maintenance time and reduced chances for badly-crafted Sprocs.

    # July 9, 2008 8:15 AM

    Jep said:

    "they could just as easily then see the name or names of these sprocs and quickly derive your common naming convention! This will pretty much give him the same access if he would have had anyway!"

    This is not entirely or even partially true.  If they (the intruder) had access to all of your sprocs then they would only have access to insert or view data the way you have proscribed in your sprocs.  If you used linq with open permissions (select, insert, etc.) to your objects then the intruder could get access any way they wanted to your objects.  This is one reason for using sprocs - a very good reason.

    # July 29, 2008 4:40 AM

    hiltong said:

    In theory, I'm in 100% agreement with you. In practice though, I find most systems end up with a full set of CRUD sprocs per table, that all follow the same naming and structural conventions. Remember, I'm not saying LINQ To SQL is any better for security, I'm just pointing out its not much worse in practice. Some other points worth stressing: a) LINQ To SQL's approach is similar to a number of other products in defaulting to dynamic SQL, so you need to compare apples with apples, and furthermore b) you do have the choice to be 100% sprocs-only, I just don't personally recommend it for other reasons.

    # July 29, 2008 2:32 PM
    Leave a Comment

    (required) 

    (required) 

    (optional)

    (required) 


    Enter the numbers above: