Impersonation in SharePoint : An Extreme Overview

Impersonation is the process of executing code in the context (or on behalf) of another user identity.You may want to use an existing  SPUser identity , or you want assume the Application Pool identity.Also, sometimes you might want to use impersonation  to make use of existing Windows user accounts and their permissions.

Before you decide for a particular approach for Impersonation , it’s important that you understand the difference between the SharePoint Security Context and the Windows Security Context. In a SharePoint webpart or application , apart from the accessing the protected resources inside SharePoint ( Lists,Libraries,files etc), you may need to  access the external resources ( Shared Folder, Sql Server etc) as well.  Since the access to these external resources is not controlled by SharePoint,  a correct impersonation approach should be adopted  to make successful calls to external systems.

Also, You should keep in mind not to use  Impersonation or Elevation of privilege to by pass the security—always use it to work with the security model your site needs.

SharePoint Sites are configured to run under the account of the requesting user. If you look at the web.config file for any SharePoint Web application, you will find below entry.

<identity impersonate="true" />

With this setting, every request is instructed to run under the Windows security context of the current user. It means that when your custom webpart or page  try to access an external resource (such as a file system , database , or Web service)  it runs under the Windows account of the requesting user.

[Note: In plain ASP.NET website, By default,  impersonation is disabled. So, All code is executed using  w3wp process account - which is Application Pool Account configured in IIS.]

In case of Classic Mode Authentication (Windows), the user identity is same in Windows and SharePoint security context. However,If you are using Claims Based Authentication, Windows security context may take on the identity of the IUSR_MACHINENAME account, or the account is specified in the IIS. This can deny access when accessing external resources because the IUSR_MACHINENAME account typically will not have rights to those resources. In such cases, you may have to use the Secure Store Service (SSS)

Now, Let’s explore the ways to impersonate in SharePoint and see when to apply each technique effectively.

1. Using SPSecurity.RunWithElevatedPrivileges

In SharePoint,  it’s very popular practice to run code with RunWithElevatedPrivileges.This is commonly used to perform an action on behalf of a user with insufficient rights.

Below is an example.

SPSecurity.RunWithElevatedPrivileges(delegate()
{
   using (SPSite elevatedSite = new SPSite(SPContext.Current.Site.Id))
   {
      using (SPWeb elevatedWeb = elevatedSite.OpenWeb(SPContext.Current.Web.Id))
      {
          // Perform administrative actions by using the elevated site and web objects. 
          // elevatedWeb.CurrentUser.LoginName gives SHAREPOINTsystem 
          // WindowsIdentity.GetCurrent().Name gives Application pool Windows account(ContsoAdmin1)
          // Hence, Both SharePoint Security context and Windows Security context are changed.
      }
   }
});

Considering Windows Security Context , The code inside SPSecurity.RunWithElevatedPrivileges block  runs under the Application Pool Account of your web application.This is the account under which the  worker process(w3wp) runs. Besides running the Web application, this Application Pool account is used as the Windows account that connects to the SharePoint Content and Configuration databases.So the code is authenticated by Application pool acount when it attempts to access external resources, such as the local file system or a SQL Server database.

Considering SharePoint Security Context, The code inside SPSecurity.RunWithElevatedPrivileges block  runs under the SHAREPOINTSYSTEM account . SHAREPOINTSYSTEM is an identity to which SharePoint maps internally. Any updates you do via Sharepoint object model (SPListItem,SPFile) reflects the modified or created by SHAREPOINTSYSTEM

SHAREPOINTSYSTEM is not recognized by the Windows security subsystem. As said earlier, this account is mapped to application pool account.

It is important to know few important aspects when using RunWithElevatedPrivileges:

1) Elevation of privilege occurs only if new SPSite created inside the block :

You have to create new SPSite  and SPWeb objects inside the SPSecurity.RunWithElevatedPrivileges block using either url or guid. If you don’t do so or try to use SPSiteSPWeb from SPContext.Current, You will get Access Denied error on using SPSecurity.RunWithElevatedPrivileges even. Also , never forget to dispose of your objects.

For example, below code does not elevate the privilege and is wrong way to use SPSecurity.RunWithElevatedPrivileges:

SPSecurity.RunWithElevatedPrivileges(delegate()
{
      SPSite elevatedSite = SPContext.Current.Site;

      using (SPWeb elevatedWeb = elevatedSite.OpenWeb())
      {
         // Performing administrative actions here will give Access Denied exception.
      }
   }
});

2) Improper usage may cause Security Risk :

SharePoint  objects ( SPList,SPFile etc) that are created or accessed by using the elevated SPSite (created inside RunWithElevatedPrivileges block) and SPWeb  retain the permissions they were created with. Hence, you should not return these objects outside the RunWithElevatedPrivileges  block otherwise it can lead to security issues.

SPList taskList=null;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
      SPSite elevatedSite = SPContext.Current.Site;

      using (SPWeb elevatedWeb = elevatedSite.OpenWeb())
      {
           taskList = elevatedWeb.Lists["Tasks"]
      }
   }
});
//This code will succeed even outside the block as it is accessed via elevated SPWeb. Hence Security Risk.
taskList.Delete();

Here is the explanation for this behavior -

When you create an SPSite object, it is persisted by an underlying SPRequest object. The SPRequest  remembers which user created the SPSite object. The SPRequest is shared by all objects that are accessed via that SPSite object.

When an SPSite object is created in a RunWithElevatedPrivileges block, the SPRequest object records that the current user is the System Account. For example, If  a SPList object is accessed via elevated SPSite object ( like SPSite.RootWeb.Lists["Tasks"] ) , it shares the same SPRequest object and  is actually an “elevated” object.If this SPList object is passed outside of the RunWithElevatedPrivileges block, it retains its underlying SPRequest object and continues to be elevated. So now if your code further uses this SPList object, you may have a security leak.

4)  RunWithElevatedPrivileges changes Windows Security Context as well

For example, if you change an application pool account of a web application from ContsoAdmin1 to ContsoAdmin2 , the code running using SPSecurity.RunWithElevatedPrivileges block :

  • Still acts and is audited as the SHAREPOINTSYSTEM account inside Sharepoint  (Sharepoint Security Context).
  •  Reflects the changed windows user when a call is made outside SharePoint  (Windows Security Context).

So inside RunWithElevatedPrivileges block , any call to external systems like DB or WebServices will be made by windows account of the application pool. It succeeds or not depends on the permissions that windows account have on that external system.

[ Note: If the external system is on another server, calls will not succeed even if application pool account has permissions. This is due to double - hop issue in NTLM authentication. You have to configure  KERBEROS authentication for that or use Secure Store Service ]

3)   RunWithElevatedPrivileges  does not work when HTTPContext is null :

RunWithElevatedPrivileges  don’t work when HTTPContext (SPContext to be more specific) is null. So, you will not have elevation of privilege when using RunWithElevatedPrivilege in Console Application, WorkFlow , Timer Job or Event handlers not initiated by a request in browser.

4)  RunWithElevatedPrivileges  does not work for a Sandbox Solution:

RunWithElevatedPrivileges works only if  you deploy the component(webpartpage) as Farm Solution. It does not work for Sandbox Solutions.        (For more Sandbox Solution limits, Please check here)

5)  Some Write Operations may fail even

If the method passed to RunWithElevatedPrivileges includes any write operations, the call to RunWithElevatedPrivileges should be preceded by a call to either SPUtility.ValidateFormDigest() or SPWeb.ValidateFormDigest(). Otherwise, the operation may not succeed.

6) To use SPSecurity.RunWithElevatedPrivileges and still retain the CurrentUser’s identity

If you run code within SPSecurity.RunWithElevatedPrivileges block and create new objects, such as list items within a list, the user automatically assigned as author or editor is SHAREPOINT\system. However, You may need the user to be the owner of an item with his or her current credentials.

To achieve this, you must first retrieve the real credentials, and then elevate the privileges.

Below example shows how to deal with this issue of two identities.

private void UpdateItem(SPList RestrictedList, bool IsAnonymous)
{
SPUser user =SPContext.CurrentWeb.CurrentUser;
Guid siteID = RestrictedList.ParentWeb.Site.ID;
Guid webID = RestrictedList.ParentWeb.ID;
Guid listID = RestrictedList.ID;
SPSecurity.RunWithElevatedPrivileges(() =>
{
using (SPSite site = new SPSite(siteID))
{
using (SPWeb web = site.OpenWeb(webID))
{
web.AllowUnsafeUpdates = true;
SPList elevatedList = web.Lists[listID];
SPListItem item = elevatedList.Items.Add();
SPUser systemUser = web.AllUsers[@"SHAREPOINT\system"];
SPFieldUserValue currentUser = new SPFieldUserValue( item.ParentList.ParentWeb, user.ID, user.Name);
if (!IsAnonymous)
{
item["Author"] = currentUser;
item["Editor"] = currentUser;
} else {
item["Author"] = systemUser;
item["Editor"] = systemUser;
}
item.Update();
}
}
}
);
}
}

The code can access the RestrictedList that the current user can’t access normally. This prevents the user from accessing the list by entering the URL directly.

Within the delegate, a new list item is created. The IsAnonymous parameter determines whether the  Author and Editor list fields take the system account or the current user.

Using above way :

  • You can decide to set the user’s data or leave the item in an anonymous state.
  • You can manipulate the Author and Editor field values as required.

2. Using SPUserToken

Another way to elevate the privilege or Impersonate is by using an SPUserToken object. The first step is get the token for the user to be impersonated in SharePoint by using SPUser.UserToken . Then use this token to the SPSite constructor to create a new impersonated security context.

This is most recommend way and best practice  to perform impersonation in context of SharePoint. However,When using SPUserToken, you need ensure that the user exists whom you are impersonating, and that user has the proper permissions. In production scenarios, you may not know the user in advance and this technique may not work

Below is the example to impersonate SHAREPOINTSYSTEM account.

SPWeb oWeb = SPContext.Current.Web;
SPUserToken token = oWeb.AllUsers[@"SHAREPOINTSYSTEM"].UserToken;
using (SPSite elevatedSite = new SPSite(oWeb.Site.ID, token))
 {
    using (SPWeb elevatedweb = site.OpenWeb())
     {
      // Perform administrative actions by using the elevated site and web objects.
      // elevatedWeb.CurrentUser.LoginName gives SHAREPOINTsystem
      // WindowsIdentity.GetCurrent().Name gives current logged-in username i.e. SPContext.Current.Web.CurrentUser.LoginName.
      // Hence,Only SharePoint Security context is changed while Windows Security context is not changed.
     }
 }

When a user request is authenticated, it runs under the context of an SPUser object and carries a security token, SPUserToken. When you create  a SPSite, an instance of the SPUserToken and the SPUser also get created. When your code attempts to access resources inside SharePoint,  this user’s security token is checked against ACLs to determine whether it should grant or deny access.

It is important to know few important aspects when using SPUserToken approach:

1) Windows Security Context is not changed

If you see the code above, WindowsIdentity.GetCurrent().Name is same as the Name of current user making the request which is SPContext.Current.Web.CurrentUser.

So  any call to external systems like DB or WebServices will be made by windows account of the current user. It succeeds or not depends on the permissions that the user have on that external system.

Note: If the external system is on another server, calls will not succeed even if current user  has permissions. This is due to double - hop issue in NTLM authentication. You have to configure  KERBEROS authentication for that or use Secure Store Service ]

2) Tokens have expiry time

The tokens time out after 24 hours, so they can be used in the code that needs to impersonate users in the case of workflow actions or asynchronous event receivers occurring within 24 hours.After the  SPUserToken object is returned to the caller, it is the caller’s responsibility to not use the token after it is expired.

[ Note: The token timeout value can be set by using the Windows PowerShell console or stsadm.

   stsadm -o setproperty -propertyname token-timeout -propertyvalue 720

]

3. Using Win32 API 

We see that none of the above ways, SPUserToken and RunWithelevatedPrivileges, work when we need to impersonate systems out side SharePoint like Console or Windows Applications.

If you have Console or Windows Application and want to  impersonate the call to SharePoint web service or Object Model , you must programmatically create a WindowsIdentity object for the caller. Create a WindowsIdentity object either by using a logon token returned from the Win32 LogonUser API, or by using the WindowsIdentity(userPrincipalName) constructor that takes a single parameter of a user principal name (UPN)

Please note that you should avoid using this technique in SharePoint components like webpart or application pages.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Threading;
using System.Web;
using System.Security.Principal;
using System.Runtime.InteropServices;
namespace AmitKumawat.ConsoleApp
{
 class Program
 {
 // Declare signatures for Win32 LogonUser and CloseHandle APIs
 [DllImport("advapi32.dll", SetLastError = true)]
 static extern bool LogonUser(
 string principal,
 string authority,
 string password,
 LogonSessionType logonType,
 LogonProvider logonProvider,
 out IntPtr token);
 [DllImport("kernel32.dll", SetLastError = true)]
 static extern bool CloseHandle(IntPtr handle);
 enum LogonSessionType : uint
 {
 Interactive = 2,
 Network,
 Batch,
 Service,
 NetworkCleartext = 8,
 NewCredentials
 }
 enum LogonProvider : uint
 {
 Default = 0, // default for platform (use this!)
 WinNT35, // sends smoke signals to authority
 WinNT40, // uses NTLM
 WinNT50 // negotiates Kerb or NTLM
 } 

 static void Main(string[] args)
 {
 string username;
 string password;
 string domain;
Console.Write("Enter username:");
 username = Console.ReadLine();
 Console.Write("Enter domain:");
 domain = Console.ReadLine();
 Console.Write("Enter password: ");
 password = Console.ReadLine();
IntPtr token = IntPtr.Zero;
 WindowsImpersonationContext impersonatedUser = null;
try
 {
bool result = LogonUser(username, domain, password, LogonSessionType.Network, LogonProvider.Default, out token);
if (result)
 {
 WindowsIdentity id = new WindowsIdentity(token);
// Begin impersonation
 impersonatedUser = id.Impersonate();
Console.WriteLine("Identity after impersonation : " + WindowsIdentity.GetCurrent().Name);
// Call to Sharepoint Web services or object model
 }
 else
 {
 Console.WriteLine("LogonUser failed: " + Marshal.GetLastWin32Error().ToString());
 }
 }
 catch
 {
}
 finally
 {
 // Stop impersonation and revert to the process identity
 if (impersonatedUser != null)
 impersonatedUser.Undo();
 // Free the token
 if (token != IntPtr.Zero)
 CloseHandle(token);
 }
// Verify the old process identity
 Console.WriteLine(String.Format("Identity after Undo: " + WindowsIdentity.GetCurrent().Name));
 Console.Read();

 } 

 }
}

There are other ways of impersonation which is not so common but I see people use it sometimes( like used here).

  •  Windows API RevertToSelf function .
  •  Impersonate(IntPtr) method with zero passed as the parameter.

These ways are not recommended for the components inside SharePoint  like Web parts or Pages. Below is the reason why :

We now know that the SharePoint request must impersonate the calling user’s identity (<identity impersonate=”true” /> in web.config). SharePoint web applications are configured to impersonate the calling user automatically. If you try to suspend this impersonation by using above ways, your code may fail or behave abnormally.

Summary

Lets summarize the important points from above discussion.

•  You should avoid using SPSecurity.RunWithElevatedPrivileges  for elevation of privilege of SharePoint objects. Instead, use SPUserToken to impersonate SPSite with a specific account, as shown previously.If you want make network calls under the application pool identity  or you don’t have a valid and known SPUser to retrieve SPUsertoken then SPSecurity.RunWithElevatedPrivileges is the only choice.

• If you need to use SPSecurity.RunWithElevatedPrivileges, it is must to dispose all objects in the block. Do not pass SharePoint objects out of the RunWithElevatedPrivileges block.

•  If you want to impersonated in application outside SharePoint, the only option is to use WIN 32 API or WindowsIdentity.Impersonate(token).

12 Responses to “Impersonation in SharePoint : An Extreme Overview”
  1. Kris says:

    also it’s worth noting that SPSecurity.RunWithElevatedPrivileges will not work with LINQ to SharePoint – you have to resort to SPQuery…

    http://msdn.microsoft.com/en-us/library/ff798485.aspx

  2. Sean says:

    hi Amit

    big thanks for this article – it really helped me resolve a “double hop NTLM” issue, where a proxy in SharePoint was not forwarding credentials to IIS box on other machine.

    note: in your article you say the following:

    [ Note: If the external system is on another server, calls will not succeed even if application pool account has permissions. This is due to double - hop issue in NTLM authentication. You have to configure KERBEROS authentication for that or use Secure Store Service ]

    however, I found that using SPSecurity.RunWithElevatedPrivileges() along with following credentials, resulted in successful request / response to the other IIS box:

    req.Credentials = CredentialCache.DefaultCredentials;
    req.ImpersonationLevel = TokenImpersonationLevel.Delegation;

    I would have expected this to fail (due to double hop issue). It must be how the AppPool account is configured ? OR how SharePoint works with elevated priviledges ?

    • Amit Kumawat says:

      Its good that your problem is solved.

      Can you elaborate more on it? what you were trying to access? are you accessing other server directly or via SharePoint server?

  3. Mario Gonzalez says:

    Hello Amit,

    I think this is a great article, and it definitely helps me better understand SharePoint impersonation.

    My question is, how can I use impersonation on anonymous users, so that they can submit an infopath form, but as a System Account.

    When an anonymous user accesses my site (infopath form) and they submit, they get an access denied error. When I elevate privlidges to system account, they can submit fine, but then workflows don’t start because MSFT prevents system actions from starting workflows. Yet, when I impersonate a user, using User Tokens, the impersonation fails and they cannot submit. The user definitely has access to this specific file because we tested it, and that user can submit forms.

    I understand that elevated privildges also changes the windows security context, and that using a user token does not. Yet, if I’m submitting forms to Sharepoint, should that even matter?

    Hopefully my question is clear, and I would appreciate any incite as to how impersonation affects anonymous users.

    Thank you,
    Mario

    • Amit Kumawat says:

      If the workflows depends on ‘created by’ or ‘modified by’ , you can edit those fields as well inside SPSecurity.RunWithElevatedPrivileges block.
      For example :

      item["Created By"] = web.AllUsers[@"contso\sam"];
      item["Modified By"] = web.AllUsers[@"contso\sam"];

      Hope the workflow triggers now.

  4. Mario Gonzalez says:

    *Edit*

    Sorry, I meant to ask – how can I use impersonation on anonymous users, so that they can submit an infopath form, but *NOT* as a System Account.

  5. Joe Reynolds says:

    Hi,
    Seems that I am running into the same issue as Mario. I am using the delegate and setting the item values to my user. Then I fire a systemupdate(false) on the item. That all works fine, it’s when I call the workflow later on that I am having the problem. The workflow moves the item from a list that an anonymous user can see to one that is restricted from anonymous. We are trying to do this to hide the data. Basically it’s a list of email addresses that a sequential workflow iterates against when an item is added to a document library…any thoughts would be appreciated. Thanks,

    Joe…

    • Amit Kumawat says:

      Please try to use the way I added above :

      6) To use SPSecurity.RunWithElevatedPrivileges and still retain the CurrentUser’s identity

  6. Kathy Livingston says:

    Hi,

    I have the same exact need as Joe and have tried creating a event receiver that is using SPSecurity.RunWithElevatedPrivileges() and then changes a field on the list item, and tries to start workflow to copy the list item to anther secured list. The field is not getting updated and the workflow is not running. The web application is using FBA. I wasn’t sure if the event receiver is failing or if FBA is contributing to the problem. I checked the application pool account and made sure it has permissions to both lists. Any suggestions or link to articles would be appreciated.

    Thanks,

    Kathy

    • Amit Kumawat says:

      Please try to use the way I added above :

      6) To use SPSecurity.RunWithElevatedPrivileges and still retain the CurrentUser’s identity

  7. Mags says:

    Nice Article! Thanks.

    Quick question, what if I have an ASP.NET application running on the SharePoint server, and I want to write into SharePoint from the ASP.NET page? Can I still use SharePoint API and Admin token to complete this task?

Leave a Reply

Subscribe

Get every post delivered to your inbox via FeedBurner :

© 2010-2013 Extreme Sharepoint | The content is copyrighted to Amit Kumawat and may not be reproduced on other websites.