BusinessRx Community

Dedicated to the advancement of software, technology and the people who devote their lives to it.

Welcome to BusinessRx Community Sign in | Join | Help
in Search

BusinessRx Reading List

These blog entries are written by industry experts and leaders. We consider this content to be a good read for any software developer or web technologist.

Browse by Tags

All Tags » .NET » Security » ASP.NET   (RSS)

  • JSON Hijacking and How ASP.NET AJAX 1.0 Avoids these Attacks

    Recently some reports have been issued by security researchers describing ways hackers can use the JSON wire format used by most popular AJAX frameworks to try and exploit cross domain scripts within browsers.  Specifically, these attacks use HTTP GET requests invoked via an HTML <script src=""> include element to circumvent the "same origin policy" enforced by browsers (which limits JavaScript objects like XmlHttpRequest to only calling URLs on the same domain that the page was loaded from), and then look for ways to exploit the JSON payload content.

    ASP.NET AJAX 1.0 includes a number of default settings and built-in features that prevent it from being susceptible to these types of JSON hijacking attacks.  Below are some details of how these attacks are mitigated:

    ASP.NET AJAX Web Methods do not enable HTTP GET requests by default

    Script files loaded via an HTML <script src=""> element within a browser can only be retrieved via HTTP GET verb requests.

    By default ASP.NET AJAX's web services layer does not allow web methods to be invoked via the HTTP GET verb. For example, assume a developer writes a web service method like below:

    [WebMethod]
    public StockQuote[] GetQuotes(string symbol) {

    }

    ASP.NET will only allow the above GetQuotes method to be called via the HTTP POST verb, and will reject all attempts to invoke the method via an HTTP GET verb.

    To make an ASP.NET AJAX web-method callable via HTTP GET-access, a developer must explicitly attribute each method using ASP.NET's ScriptMethod attribute (and set the UseHttpGet property to true):

    [WebMethod] 
    [ScriptMethod(UseHttpGet
    =true)] 

    public StockQuote[] GetQuotes(string symbol) { 


    Although this type of modification is easy to make, it requires a developer to intentionally GET enable a web service. ASP.NET AJAX web services can never be non-deliberately GET enabled, and the ASP.NET AJAX documentation explicitly recommends against GET enabling web-service end points for a number of reasons (risk of url tampering being one of them).

    Note: the ASP.NET AJAX "UpdatePanel" control, as well as the other server controls that ship with ASP.NET AJAX 1.0, do not use HTTP GET and instead use HTTP POSTs when doing asynchronous postbacks.

    ASP.NET AJAX Content-Type Header Validation

    There is built-in layer of protection that ASP.NET enforces for both GET and POST based ASP.NET AJAX web methods, which is that regardless of the HTTP verb being used, ASP.NET always requires that the HTTP Content-Type header is set to the value application/json. It this content type header is not sent, ASP.NET AJAX will reject the request on the server.

    Using the stock quote method shown earlier, an HTTP trace of an ASP.NET AJAX GET invocation must look like the following:

    GET /StockService/Stock.asmx/GetQuotes?symbol=%22msft%22 HTTP/1.1 
    Accept: */* 
    Accept-Language: en-us,fr;q=0.5 
    Referer: http://xxxxxx/StockService/test.aspx 
    Content-Type: application/json; charset=utf-8 
    UA-CPU: x86 
    Accept-Encoding: gzip, deflate 
    User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2) 
    Host: xxxxxx 
    Proxy-Connection: Keep-Alive 

    Note that even though the above is a GET request, the client-side ASP.NET AJAX JSON stack still inserts a Content-Type HTTP header that tells the server to consider this an AJAX web service request. The server-side web services stack for ASP.NET AJAX 1.0 always checks for this specific content type, and if it is not found it will reject the request.

    If a malicious developer attempted a cross site request forgery attack using HTTP GETs against this web service, they might include a script tag in their page like the following:

    <script type="text/javascript" src="http://contoso.com/StockService/Stock.asmx/GetQuotes?symbol=msft" /> 

    However, browsers will not set the Content-Type to application/json when parsing a <script src=""> element and making the request. As a result when ASP.NET receives a request made from a <script /> include, it will not recognize it as a request to an ASP.NET AJAX web service, and it will result in an error from ASP.NET stating that it does not recognize the requested URL.  This will prevent JSON Hijacking attempts (even if you have the GET verb enabled for a web method).

    Summary

    ASP.NET AJAX 1.0 by default only allows the HTTP POST verb to be used when invoking web methods using JSON, which means you can't inadvertently allow browsers to invoke methods via HTTP GET.

    ASP.NET AJAX 1.0 requires a Content-Type header to be set to "application/json" for both GET and POST invocations to AJAX web services.  JSON requests that do not contain this header will be rejected by an ASP.NET server.  This means you cannot invoke an ASP.NET AJAX web method via a <script src=""> include because browsers do not allow append custom content-type headers when requesting a JavaScript file like this.

    Hope this helps,

    Scott

  • Common Gotcha: Don't forget to <clear/> when adding providers

    Recently I've helped a few people out who were having an issue with how they had added new Membership, Role, and Profile providers within their web.config file.  If you are ever going to add a provider declaration within your web.config file, please read-on to learn how to avoid a common gotcha.

    Symptom:

    You want to configure ASP.NET 2.0 to store your Membership/Role Management/Profile data within a remote SQL database.  To accomplish this you first use the aspnet_regsql.exe utility to provision the appropriate schema within the database.  Rather than override the "LocalSqlServer" connection string within your web.config file, you decide to register a new provider within your web.config file like below (note: the following registration has a bug - so don't copy/paste it):

          <membership>

                
    <providers>
                    
    <add name="AspNetSqlMembershipProvider"
                        type
    ="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
                        connectionStringName
    ="MyDatabase"
                        enablePasswordRetrieval
    ="false"
                        enablePasswordReset
    ="true"
                        requiresQuestionAndAnswer
    ="true"
                        requiresUniqueEmail
    ="false"
                        passwordFormat
    ="Hashed"
                        maxInvalidPasswordAttempts
    ="5"
                        minRequiredPasswordLength
    ="7"
                        minRequiredNonalphanumericCharacters
    ="1"
                        passwordAttemptWindow
    ="10"
                        passwordStrengthRegularExpression
    ="" 
                        applicationName
    ="/" 
                    
    />
                </
    providers>

          
    </membership>

    When registering the above provider you are careful to explictly set the applicationName property, and so avoid another really common gotcha.

    When you run your application on a machine without SQL Express, though, you see some weird behavior.  You might get a SQL error like so:

    An error has occurred while establishing a connection to the server.  When connecting to SQL Server 2005, this failure may be caused by the fact that under the default settings SQL Server does not allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)

    You might also find that the web administration tool has problems connecting with your database and/or the roles/users you create within it aren't correctly saved in the database you configured above.

    Cause of the Problem:

    The root cause of the above problem rests in how the new provider was registered within the web.config file. 

    The <providers> section within the web.config file is implemented as a collection, and so it is possible to register multiple providers at the same time (this is useful when you want to have some users authenticated using one Membership store, and others authenticated using a separate Membership store).

    By default ASP.NET 2.0 registers a set of default SQL Express providers within the root web.config file on your machine that create a SQL Express database within your /app_data directory to store/manage membership/role/profile data when you first access it.  Because this is registered at the machine-wide level, all provider collections by default inherit this registration.  Unless you explictly <clear/> or override the inherited value, your application will have this default membership/role/profile provider registered.

    Because the above web.config file simply added a new provider -- and didn't clear or replace the default provider registration -- the above application now has two Membership providers configured.  When you do a Membership.CreateUser() call in your code, ASP.NET will attempt to create the user in both membership databases.  If you don't have SQL Express installed on your system, the create-user attempt will fail for this database - which leads to the errors and/or weird behavior above.

    How to Fix It:

    Unless you wish to register multiple membership, role or profile databases (which is rare), you should always add an explicit <clear/> directive before your <add/> statements within your web.config file:

          <membership>

                
    <providers>
                    
    <clear/>
                    
    <add name="AspNetSqlMembershipProvider"
                        type
    ="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
                        connectionStringName
    ="MyDatabase"
                        enablePasswordRetrieval
    ="false"
                        enablePasswordReset
    ="true"
                        requiresQuestionAndAnswer
    ="true"
                        requiresUniqueEmail
    ="false"
                        passwordFormat
    ="Hashed"
                        maxInvalidPasswordAttempts
    ="5"
                        minRequiredPasswordLength
    ="7"
                        minRequiredNonalphanumericCharacters
    ="1"
                        passwordAttemptWindow
    ="10"
                        passwordStrengthRegularExpression
    ="" 
                        applicationName
    ="/" 
                    
    />
                </
    providers>

          
    </membership>

    This will ensure that your application doesn't inherit any default provider registrations. 

    Note that you must do this for each provider declaration that you register.  So if you are adding providers for <roles> and <profile>, make sure you add the <clear/> directive in their providers section as well.

    Hope this helps,

    Scott

    P.S. Click here to review other past ASP.NET Tips/Tricks, Gotchas, and Recipes.

  • Tip/Trick: Adding Authorization Rules to Business and Data Layers using PrincipalPermissionAttributes

    Earlier this summer I posted two tutorials on using Windows Authentication with ASP.NET: Enabling Window Authentication within an Intranet ASP.NET Web Application and Implementing Role Based Security within ASP.NET using Windows Authentication and SQL Server.  I also linked to Scott Mitchell's great ASP.NET 2.0 Security, Membership and Roles Tutorials that cover how to use Forms Authentication and the new Membership/Roles APIs in ASP.NET for Internet based web applications.

    These tutorials covers how to implement authentication on your site, which is the process of identifying who an incoming user is.  They also demonstrate how to implement role based management on your site, which allows you to logically group individual users into higher-level roles or groups (for example: "admins", "friends", "subscribers", etc).  The tutorials also demonstrate how to implement authorization rules to grant or deny users/roles access to visit individual pages or URLs within a site (the roles tutorial above also demonstrates how to show/hide menu nodes based on the permissions of the incoming user).

    Adding Security Authorization Rules to Business and Data Layers

    When you authenticate a user within an ASP.NET application, the authenticated user's identity will be automatically flowed throughout that user's request on the server.  What this means is that you don't need to manually pass a user's identity around from method to method or class to class.  This makes it much easier to implement security authorization rules throughout your application.

    One little known feature in .NET is the ability to have the CLR automatically use this identity information to authorize a user's capabilities before instantiating a class, or accessing a method/property on it.  This makes it easy to add clean security authorization rules to your business and data layers without having to write much code.

    All you need to do to implement this is to use the PrincipalPermissionAttribute within the "System.Security.Permissions" namespace and decorate it on the appropriate class or member on it.  For example:

    using System;
    using System.Security.Permissions;

    [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
    public class EmployeeManager
    {
        [PrincipalPermission(SecurityAction.Demand, Role 
    "Manager")]
        
    public Employee LookupEmployee(int employeeID)
        {
           // todo
        }

        [PrincipalPermission(SecurityAction.Demand, Role 
    "HR")]
        
    public void AddEmployee(Employee e)
        {
           // todo
        }
    }

    In the above example, I have added a PrincipalPermission attribute to the "EmployeeManager" class.  By adding it I am requiring that a user must be authenticated (logged-in) before this class can be instantiated during a web request (the Authenticated=true demand enforces this).  I have also then added two additional security demands on the "LookupEmployee" and "AddEmployee" methods.  With the LookupEmployee method I am requiring that the authenticated user for the request is within the "Manager" role in order for the method to be invoked.  With the AddEmployee method I am requiring that the authenticated user for the request is within the "HR" role in order for this method to be invoked and have a new Employee added to the system.

    And now if I accidentally introduce a security hole within my UI tier and have some code-path that allows a non-Manager/HR employee to cause these methods to be invoked, my business tier will automatically prevent this from happening and raise a security exception.  

    The PrincipalPermissionAttribute isn't tied to any specific authentication mode.  It will work with Forms Authentication, Windows Authentication, Passport Authentication, or any custom authentication mode you want to invent.  It will also work with any Role implementation I might use (so if you build or plug-in your own Role Provider in ASP.NET it will just work).

    The PrincipalPermissionAttribute type is implemented in the standard CLR mscorlib assembly that all .NET projects compile against.  So it isn't ASP.NET specific, and can be used within any application type (including Windows and Console applications).  In addition to making it more generically useful, this makes it easier to unit-test business/data libraries built with it.

    Using PrincipalPermissionAttributes within Pages and Controls

    The PrincipalPermissionAttribute can be used on any class within an application.  So in addition to using it within your business and data layers, you can also use it within ASP.NET pages or user-controls you author in your site as well.  For example, to enforce that your "MyPage" page can only be used by those within the "Manager" role, you could add a PrincipalPermission attribute to the code-behind of it (below done in VB):

    Imports System.Security.Permissions

    <PrincipalPermission(SecurityAction.Demand, Authenticated:
    =True, Role:="Manager")> _
    Partial Class MyPage
        
    Inherits System.Web.UI.Page

    End Class

    More on ASP.NET Security

    To learn more about ASP.NET security I'd recommend checking out my ASP.NET Security Resources list as well as my ASP.NET Tips and Tricks Page.

    There are also now two dedicated books on ASP.NET 2.0 Security out there: Stefan Schackow's Professional ASP.NET 2.0 Security, Membership and Role Management book and the new Developing More Secure Microsoft ASP.NET 2.0 Applications book by Dominick Baier (who runs the great http://www.leastprivilege.com/ blog) that just got published this week:

    Hope this helps,

    Scott

    Share this post: Email it! | bookmark it! | digg it! | reddit!
  • Tip/Trick: Patterns and Practices Guidance Explorer for .NET and ASP.NET

    I blogged about the cool new .NET and ASP.NET Guidance Explorer that the Microsoft Patterns and Practices Team has been working on back in June. 

    Since then the team has been hard at work and recently moved the project to be hosted on CodePlex.  You can visit and join the project here.  All source code for the Guidance Explorer UI (which is of course built using .NET and Windows Forms) can also now be downloaded for free from the CodePlex project site.

    The Guidance Explorer provides a database of recommendations and best practices for ASP.NET, ADO.NET, and the .NET Framework that you can easily filter and search.  Particularly useful are some of the checklists you can easily follow for performance and security best practices prior to deployment.

    You can create custom views of the library of just those recommendations you care about.  What is cool is that you can now also easily publish these custom views to your friends or other members of your team:

    You will also be able to subscribe to feeds of guidance either on a local share or on the web:

    Best of all, you can also now create your own recommendations and add them into the library (and share them with others). 

    Download the project (and optionally the full source code) from its CodePlex Project and start using it today!

    Hope this helps,

    Scott

    Share this post: Email it! | bookmark it! | digg it! | reddit!
Powered by Community Server, by Telligent Systems
'