When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, July 23, 2008

Examining ASP.NET's Membership, Roles, and Profile - Part 12

By Scott Mitchell


A Multipart Series on ASP.NET's Membership, Roles, and Profile
This article is one in a series of articles on ASP.NET's membership, roles, and profile functionality.

  • Part 1 - learn about how the membership features make providing user accounts on your website a breeze. This article covers the basics of membership, including why it is needed, along with a look at the SqlMembershipProvider and the security Web controls.
  • Part 2 - master how to create roles and assign users to roles. This article shows how to setup roles, using role-based authorization, and displaying output on a page depending upon the visitor's roles.
  • Part 3 - see how to add the membership-related schemas to an existing database using the ASP.NET SQL Server Registration Tool (aspnet_regsql.exe).
  • Part 4 - improve the login experience by showing more informative messages for users who log on with invalid credentials; also, see how to keep a log of invalid login attempts.
  • Part 5 - learn how to customize the Login control. Adjust its appearance using properties and templates; customize the authentication logic to include a CAPTCHA.
  • Part 6 - capture additional user-specific information using the Profile system. Learn about the built-in SqlProfileProvider.
  • Part 7 - the Membership, Roles, and Profile systems are all build using the provider model, which allows for their implementations to be highly customized. Learn how to create a custom Profile provider that persists user-specific settings to XML files.
  • Part 8 - learn how to use the Microsoft Access-based providers for the Membership, Roles, and Profile systems. With these providers, you can use an Access database instead of SQL Server.
  • Part 9 - when working with Membership, you have the option of using .NET's APIs or working directly with the specified provider. This article examines the pros and cons of both approaches and examines the SqlMembershipProvider in more detail.
  • Part 10 - the Membership system includes features that automatically tally the number of users logged onto the site. This article examines and enhances these features.
  • Part 11 - many websites require new users to verify their email address before their account is activated. Learn how to implement such behavior using the CreateUserWizard control.
  • Part 12 - learn how to apply user- and role-based authorization rules to methods and classes.
  • Part 13 - see how to create a login screen that allows Admin users to log in as another user in the user database.
  • Part 14 - learn how to create a page that permits users to update their security question and answer.
  • Part 15 - the Membership API does not provide a means to change a user's username. But such functionality is possible by going directly to the user store, as this article illustrates.
  • Part 16 - the Membership system includes the necessary components for enforcing expiring passwords. This installment shows how to implement such a policy.
  • Part 17 - see how to display important, unread announcements to users when they sign into the website.
  • Part 18 - often, applications need to track additional user information; learn how to capture this information in a database and see how to build pages to let users update their own information and to display this information to others.
  • (Subscribe to this Article Series! )

    Introduction


    Several of the earlier installments in this article series examined how to apply authorization rules in order to prohibit particular users, roles, or classes of users from accessing particular resources. For instance, Part 2 showed how to define URL-based authorization rules in web.config for roles. With just a bit of XML markup, it is possible to block particular users or roles from visiting certain web pages. Just installments also looked at using the LoginView control, which displays different markup based on whether the user is authenticated or not (and can also be used to display different markup based on the currently logged in user's role). There are also programmatic techniques you can use to determine the identity of the currently logged on user and what roles she belongs to.

    The URL-based authorization, LoginView control, and programmatic techniques can be used in tandem to ensure that a user does not visit a page or perform some operation if she is not authorized. But what if you forget to implement one of these safeguards? For example, imagine that you have a web page that includes a button that, when clicked, perform some task that is only intended for administrators. You could put this button in a LoginView control or you could use programmatic techniques to ensure that only users in the appropriate role (say, Admin) saw the button. But what if sometime later you, or another developer, removed this check by accident? The net result would be that any user visiting the page could perform the administrator-only operation! Whoops!

    To reduce the likelihood of such security mishaps, the .NET Framework includes capabilities for declaratively asserting permissions (via attributes) on methods and classes. In a nutshell, you can add such attributes to ASP.NET pages, their code-behind classes, and your business logic and data access layers. With these attributes in place, your visitors will be barred from performing unauthorized actions, regardless of whether there are any security holes in the user interface. Read on to learn more!

    - continued -

    Using the PrincipalPermissionAttribute Class to Declaratively Define Authorization Rules


    The .NET Framework includes a class named PrincipalPermissionAttribute that enables developers to apply principal permissions to code using declarative syntax. This attribute can decorate either classes or methods and its properties spell out what users or roles can access the class or method it decorates. The PrincipalPermissionAttribute class has the following important properties:
    • Action - defines the authorization check that is to be performed. There are a number of different Action options. The one you'll use most often is Demand, which requires that all callers in the call stack have the permission specified by the other properties.
    • Authenticated - a Boolean value that indicates whether the caller must be authenticated or not.
    • Name - the name of the caller. An ASP.NET web application this is typically the username of the currently logged in user.
    • Role - the name of the role the caller must belong to.
    The Action property is mandatory, but the other ones can be used in various combinations. For example, to declare that a particular method can only be accessed by users in a particular role, you would use the following syntax:

    // C#
    [PrincipalPermission(SecurityAction.Demand, Role="roleName")]
    public void SomeMethod(...)
    {
       ...
    }


    ' VB
    <PrincipalPermission(SecurityAction.Demand, Role:="roleName")> _
    Public Sub SomeMethod(...)
       ...
    End Sub

    Note: the PrincipalPermissionAttribute class is located in the System.Security.Permissions namespace. You may need to import this namespace into the class files or ASP.net pages that use the PrincipalPermissionAttribute class.

    You can also combine the Authenticated, Name, and Role properties in a single attribute declaration. Doing so requires that the caller have all security permissions outlined. For example, to require that the user have the name Scott and along to the role Admin, use the following:

    // C#
    [PrincipalPermission(SecurityAction.Demand, Name="Scott", Role="Admin")]
    public void SomeMethod(...)
    {
       ...
    }


    ' VB
    <PrincipalPermission(SecurityAction.Demand, Name:="Scott", Role:="Admin")> _
    Public Sub SomeMethod(...)
       ...
    End Sub

    To allow access for a caller that satisfies one or more conditions use multiple PrincipalPermissionAttributes. For instance, the following declarations grant access to the method if the caller is user Scott or is in the role Admin:

    // C#
    [PrincipalPermission(SecurityAction.Demand, Name="Scott")]
    [PrincipalPermission(SecurityAction.Demand, Role="Admin")]
    public void SomeMethod(...)
    {
       ...
    }


    ' VB
    <PrincipalPermission(SecurityAction.Demand, Name:="Scott")> _
    <PrincipalPermission(SecurityAction.Demand, Role:="Admin")> _
    Public Sub SomeMethod(...)
       ...
    End Sub

    Declaratively Applying Authorization Rules to Methods


    The code available for download at the end of this article includes a simple business logic layer implemented as a single class named UserAPI.vb in the App_Code folder. This class has three dummy methods:
    • SomeAdminAction - a method meant only to be called by users in the Admin role
    • SomeAuthenticatedAction - a method meant only to be called by authenticated users
    • SomeActionForScottOrSam - a method meant only to be called by users Scott or Sam
    To help enforce these authorization rules it is good practice to add PrincipalPermissionAttributes to each method. The following code implements these attributes and ensures that the methods can only be executed by those users or roles that have permission.

    Imports System.Security.Permissions

    Public Class UserAPI
       <PrincipalPermission(SecurityAction.Demand, Role:="Admin")> _
       Public Sub SomeAdminAction()
          ' ...
       End Sub

       <PrincipalPermission(SecurityAction.Demand, Authenticated:=True)> _
       Public Sub SomeAuthenticatedAction()
          ' ...
       End Sub

       <PrincipalPermission(SecurityAction.Demand, Name:="Scott")> _
       <PrincipalPermission(SecurityAction.Demand, Name:="Sam")> _
       Public Sub SomeActionForScottOrSam()
          ' ...
       End Sub

    End Class

    These attributes can also be added to methods and event handlers in the code-behind classes of ASP.NET web pages.

    Declaratively Applying Authorization Rules to Classes


    In addition to protecting particular methods, you can use PrincipalPermissionAttribute to protect an entire class. Simply add the attribute immediately above the class declaration. The demo for download at the end of this article uses this functionality to prohibit anonymous users from visiting the WhoIsOnline.aspx page. This is accomplished by adding a PrincipalPermissionAttribute attribute to the class declaration in the page's code-behind file:

    Imports System.Security.Permissions

    <PrincipalPermission(SecurityAction.Demand, Authenticated:=True)> _
    Partial Class WhoIsOnline
       Inherits BasePage

       ...
    End Class

    What Happens When a Declarative Security Violation Occurs?


    If an unauthorized caller executes a method or evokes a class that is decorated with a PrincipalPermissionAttribute attribute, a SecurityException exception is thrown. If there is no error handling the exception will bubble up to the ASP.NET runtime and will be handled there. Depending on how your website is configured this may display the "yellow screen of death" (see the following screenshot) or a custom error page.

    The yellow screen of death shown when an unauthorized caller invokes a method or class protected by a PrincipalPermissionAttribute.

    This exception can be gracefully handled in a number of ways. You can surround the call to the protected methods and classes with a Try...Catch block, performing some action in the event of a SecurityException. The following code snippet and screenshot are from the code available for download at the end of this article, and show a simple Try...Catch block implementation. Specifically, if a SecurityException is raised when calling SomeActionForScottOrSam a message is displayed in a Label control (Message).

    Try
       Dim api As New UserAPI()
       api.SomeActionForScottOrSam()
    Catch sException As System.Security.SecurityException
       Message.Text = "You attempted to perform an operation that you lack authorization for."
    Catch ex As Exception
       Throw ex
    End Try

    the website available for download from the end of this article has three user accounts: Scott, Jisun, and Sam. If Jisun logs on to the site, visits the page ~/UsersOnly/Default.aspx, and clicks the button "Perform some operation that only Scott and Sam can do" the above code is executed. The SomeActionForScottOrSam method includes two PrincipalPermissionAttribute attributes that permit only users Scott and Sam. Consequently, when Jisun calls this method a SecurityException is thrown. The appropriate Catch block executes, displaying the message shown in the screenshot below.

    The following message is displayed to the unauthorized user; in this case, user Jisun has clicked the 'Perform some operation that only Scott and Sam can do' button.

    Another option is to automatically redirect users to a particular web page in the event of a security exception. This can be accomplished by creating an event handler for the application's Error event in Global.asax. The Error event is raised whenever an unhandled exception bubbles up to the ASP.NET runtime. Here we can determine what type of exception was raised and take action based on that knowledge. The following event handler redirects the user to a page named Unauthorized.aspx whenever an unhandled SecurityException percolates up to the ASP.NET runtime.

    Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
       Dim err As Exception = Server.GetLastError()
       If TypeOf err Is System.Security.SecurityException Then
          Response.Redirect("~/Unauthorized.aspx?page=" & Server.UrlEncode(Request.RawUrl))
       End If
    End Sub

    With this event handler in place in the event of an unauthorized access the caller is automatically shown the following screen.

    Use the PrincipalPermissionAttribute Attribute as a Last Resort
    Keep in mind that the purpose of the PrincipalPermissionAttribute attribute is to serve as a final authorization check. You should still use your URL-based authorization rules in web.config along with user interface elements, such as the Login and LoginView controls. The PrincipalPermissionAttribute attribute is designed to protect your business logic from unauthorized callers should there be a security hole or oversight in the presentation layer.

    If you download the demo at the end of this article you will no doubt notice the lack of preemptive checks in the presentation layer. For instance, there are buttons to perform actions that are only available to authenticated users, yet these buttons are displayed to both authenticated and anonymous users. Likewise, there are buttons to perform actions are only available to users in the Admin role, yet these buttons are shown to all, regardless of their role. This design was done purposefully so as to illustrate the behavior of the PrincipalPermissionAttribute attributes when an unauthorized user is encountered.

    Conclusion


    A well-designed website automatically prohibits users from visiting pages they are not authorized to see and hides user interface elements that perform actions that the currently logged on user cannot perform. However, it is easy to overlook a particular user interface element and accidentally show it to all users even though it performs an action that's reserved for a few. It's good practice to decorate your methods and classes with appropriate PrincipalPermissionAttribute attributes. These attributes provide a declarative means for defining what callers can access what classes and methods, and serves as a final check in case there are security holes or oversights in the presentation layer.

    Happy Programming!

  • By Scott Mitchell


    Further Reading


  • Forms Authentication, Authorization, Membership, and Roles Tutorials (includes VB & C# versions!)
  • Tip/Trick: Adding Authorization Rules to Business and Data Layers using PrincipalPermissionAttributes
  • Security Practices: ASP.NET 2.0 Security Practices at a Glance
  • User-Based Authorization (VB version) | (C# version)
  • Gracefully Responding to Unhandled Exceptions
  • Attachments


  • Download the code used in this article

    A Multipart Series on ASP.NET's Membership, Roles, and Profile
    This article is one in a series of articles on ASP.NET's membership, roles, and profile functionality.

  • Part 1 - learn about how the membership features make providing user accounts on your website a breeze. This article covers the basics of membership, including why it is needed, along with a look at the SqlMembershipProvider and the security Web controls.
  • Part 2 - master how to create roles and assign users to roles. This article shows how to setup roles, using role-based authorization, and displaying output on a page depending upon the visitor's roles.
  • Part 3 - see how to add the membership-related schemas to an existing database using the ASP.NET SQL Server Registration Tool (aspnet_regsql.exe).
  • Part 4 - improve the login experience by showing more informative messages for users who log on with invalid credentials; also, see how to keep a log of invalid login attempts.
  • Part 5 - learn how to customize the Login control. Adjust its appearance using properties and templates; customize the authentication logic to include a CAPTCHA.
  • Part 6 - capture additional user-specific information using the Profile system. Learn about the built-in SqlProfileProvider.
  • Part 7 - the Membership, Roles, and Profile systems are all build using the provider model, which allows for their implementations to be highly customized. Learn how to create a custom Profile provider that persists user-specific settings to XML files.
  • Part 8 - learn how to use the Microsoft Access-based providers for the Membership, Roles, and Profile systems. With these providers, you can use an Access database instead of SQL Server.
  • Part 9 - when working with Membership, you have the option of using .NET's APIs or working directly with the specified provider. This article examines the pros and cons of both approaches and examines the SqlMembershipProvider in more detail.
  • Part 10 - the Membership system includes features that automatically tally the number of users logged onto the site. This article examines and enhances these features.
  • Part 11 - many websites require new users to verify their email address before their account is activated. Learn how to implement such behavior using the CreateUserWizard control.
  • Part 12 - learn how to apply user- and role-based authorization rules to methods and classes.
  • Part 13 - see how to create a login screen that allows Admin users to log in as another user in the user database.
  • Part 14 - learn how to create a page that permits users to update their security question and answer.
  • Part 15 - the Membership API does not provide a means to change a user's username. But such functionality is possible by going directly to the user store, as this article illustrates.
  • Part 16 - the Membership system includes the necessary components for enforcing expiring passwords. This installment shows how to implement such a policy.
  • Part 17 - see how to display important, unread announcements to users when they sign into the website.
  • Part 18 - often, applications need to track additional user information; learn how to capture this information in a database and see how to build pages to let users update their own information and to display this information to others.
  • (Subscribe to this Article Series! )



  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article