Professional ASP.NET Security - Jeff Ferguson
.pdfImpersonation
[assembly:SecurityPermissionAttribute(SecurityAction.ReguestMinimum, UnrnanagedCode=true) ]
We can add it to the code file for a class just under the using statements. This will specify that our assembly requires permission to run unmanaged code in order to function. (See Chapter 13 for more about code access security and permissions)
Within our class, before the method where we want to do the logon, we include an attribute and declaration that will import a function from the Windows security API:
[Dlllmport("C:\\Windows\\System32\\advapi32.dll")]
public static extern bool LogonUser(String IpszUsername, String IpszDomain, String IpszPassword, int dwLogonType, int dwLogonProvider, out int phToken);
As you can see, the LogonUser function comes from advapi32 .dll and takes username, domain, password, logon type and logon provider input parameters along with an output parameter that allows us to access the token following a successful logon. The token will be placed in the variable that we specify as the last parameter when we call LogonUser. A Boolean result is returned to indicate whether the logon was successful.
Once we have imported this function, we can use it in our code like this:
int returnedToken; if(LogonUser(user,machine,password,3,0,out returnedToken))
IntPtr token = new IntPtr(returnedToken);
We have to convert the integer value returned by LogonUser into an IntPtr in order that we can use it with Windowsldentity. Impersonate
Doing the Impersonation
Once we have an account token, we can use Windows Identity. Impersonate to perform the impersonation. The following method will perform an impersonation given a username, machine name, and password.
public static WindowsImpersonationContext Impersonate(string user, |
string |
|
machine, |
string password) { |
|
int |
token; |
|
if(!LogonUser(user.machine,password,3,0,out token)) return null;
IntPtr tokenPtr = new IntPtr(token);
WindowsImpersonationContext context = Windowsldentity.Impersonate(tokenPtr);
return context;
377
If the logon fails, we return null:
if ( ILogonUser (user, machine, password, 3 , 0 , out token) ) return null;
Otherwise, we return the Windowslmpersonationcontext object that is returned by
Windowsldentity . Impersonate:
Windowslmpersonationcontext context = Windowsldentity. Impersonate (tokenPtr) ; return
context;
The Windowslmpersonationcontext object is important because it provides us with the ability to revert back to our original identity by using its Undo method. Here is some code that uses the Impersonate method we just created:
private void |
Page_Load( object |
sender, |
System. |
|
EventArgs |
e) { |
|
|
|
//output |
the |
identity before impersonation |
||
Response. Write ( "Before Impersonation: |
" + |
|||
|
Windowsldentity .GetCurrent () .Name) ; |
|||
//do the |
impersonation |
|
|
|
Windowslmpersonationcontext |
context = |
Impersonate ( "aUser" , |
||
|
"syzygy", "test" ) ; |
|
|
|
//check that |
impersonation has worked |
|
||
if (context |
1= null) |
|
|
{
Response. Write (@"<BR>" ) ;
//output the identity during impersonation Response. Write ( "During Impersonation: " +
Windowsldentity. GetCurrent () .Name) ;
//revert context . Undo ( ) ;
Response. Write (@"<BR>" ) ;
//output the identity after reverting Response. Write ( "After Impersonation: " +
Windowsldentity. GetCurrent () .Name) ;
First we output the current identity by using the static method Windowsldentity .GetCurrent:
Response. Write ( "Before Impersonation: " +
Windowsldentity. GetCurrent () .Name) ;
378
impersonation
We then do the impersonation:
WindowsImpersonationContext context = Impersonate("aUser", "syzygy", "test");
If the impersonation was successful and an impersonation context was returned, we output the current identity again:
if(context != null) { Response.Write(@"<BR>"); Response.Write("During Impersonation: " +
WindowsIdentity.GetCurrent().Name);
We then revert to the original user again by using WindowsImpersonationContext .Undo and output the current identity for a final time:
context.Undo();
Response.Write(@"<BR>");
Response.Write("After Impersonation: " +
WindowsIdentity.GetCurrent().Name);
This code will produce output like this:
>| default - Microsoft Internet Explorer provided by Freeserve
File |
Edit |
View |
Favorites |
Tools |
Help |
Address J jg http://LOCALHOST/security/twidows/temporaryimpersonatio
Before Impersonation: SYZYGYlASPNET
During Impersonation: SYZYGY\aUser
After Impersonation: SYZYGYXASPNET
*j Local intranet
This shows that we have successfully run some code under the identity of aUser.
Problems with Accessing Network Resources
In order to access networked resources such as shared folders, a user must have a 'network interactive' logon type. This is represented in the call to LogonUser with the value 2 (rather than the value 3 we have been using up until now). Unfortunately, using this value seems to cause a problem with Windowsldentity. Impersonate. The solution to this problem is to do the impersonation ourself, with another platform invoke of the Windows API.
The function we want to use is called ImpersonateLoggedOnUser and is found in advapi32 . dll along with LogonUser. We can import it with the following code:
379
tDlllmport("C:\\Windows\\System32\\advapi32.dll")]public staticextern bool ImpersonateLoggedOnUser ( int hToken) ;
Here is an alternative to the Impersonate method we created earlier:
public static bool Networklmpersonate (string user, string machine, string password)
{
int token;
iff! LogonUser (user .machine, password, 3,0, out token) ) return false;
return ImpersonateLoggedOnUser (token) ;
Remember that the Dlllmport and declaration for ImpersonateLoggedOnUser must be included before this method.
If the call to LogonUser fails, we return false; otherwise we return the value returned by calling ImpersonateLoggedOnUser and passing it the token that LogonUser returns.
We cannot use Undo to revert back to the original identity in this case, as we do not have a WindowsImpersonationContext object. We have to use another Windows API call, this time to the RevertToSelf function. This is imported as follows:
[Dlllmport("C:\\Windows\\System32\\advapi32.dll")]public static extern bool RevertToSelf ();
Here is some code that demonstrates our new method and how we revert:
private void Page_Load( object sender, System. EventArgs e) { Response. Write (Windowsldentity . GetCurrent () .Name) ;
Response. Write (@"<BR>" ) ;
if (Networklmpersonate ( "aUser" , "syzygy", "test")) { Response. Write (Windowsldentity .GetCurrent () .Name) ; RevertToSelf(),-Response. Write (@"<BR>" ) ; Response. Write
(Windowsldentity .GetCurrent () .Name) ;
This is very similar to the code we used earlier to demonstrate Impersonate.
380
Summary
In this chapter, we have looked at how we can use impersonation to change the security context that our ASP.NET code runs under. We have seen that we can:
D Configure ASP.NET to impersonate the account that IIS passes to it Q Configure ASP.NET to impersonate a specific account
Q Obtain account tokens by calling the LogonUser function in the Win32 API Q Programmatically impersonate another account within our own code
G Use the Win32 API to do impersonation directly for network access, when the Windowsldentity. Impersonate method will not work.
381
Configuring IIS for Security
Internet Information Server (or IIS) has received lot of attention recently because of security vulnerabilities (and the high profile of worms such as Code Red and Nimda). Magazines, pundits, and competitors of Microsoft have expended much energy in scrutinizing and criticizing its security features. Research firm Gartner has gone even further, suggesting that companies that have been hit by both Code Red and Nimda abandon the use of IIS altogether (see the article Nimda Worm Shows You Can't Always Patch Fast Enough: http://www3.gartner.com/DisplayDocument?doc_cd=101034).
Security vulnerabilities and viruses are not just of concern for users of IIS. In fact, the first recorded Internet worm was released in 1987 for Unix systems, and exploited a buffer overflow problem. According to the 2001 annual report of the CERT Coordination Center, among the most serious security vulnerabilities were multiple vulnerabilities in BIND (Berkeley Internet Name Domain) - the full CERT 2001 annual report may be found at http://www.cert.org/advisories/CA-2001-02.html. This affects most operating systems including OpenLinux 2.3, RedHat Linux, Tru64 UNIX, AIX 5L, HP-UX Hi, SGI, Sun Solaris, FreeBSD, NetBSD, and OpenBSD. However, IIS was not affected, since Microsoft's implementation of DNS is not based on BIND. The second most serious vulnerability, according to the CERT Coordination Center, was the sadmind/IIS Worm. This affects both IIS and Sun Solaris systems.
Security vulnerabilities are also a concern for Open Source products. There have been a number of cases of hackers managing to compromise the security of even high profile open source systems, including a public server of the Apache Software Foundation (http://www.apache.org/info/20010519-hack.html).
IIS gets a great deal of attention because it is one of the most widely used web servers after the Apache web server (based on the Netcraft's survey at http://www.netcraft.com/survey/), and, of course, Microsoft products in general come in for a lot of scrutiny.
Most of the IIS security vulnerabilities can be fixed by applying appropriate service packs and patches. In this chapter, we'll investigate how to minimize the risk of IIS being vulnerable to attack, and how to use the ASP.NET configuration options to maximize security.
Security and the Server Infrastructure
There is no such thing as a 100% secure system or application. Security is a journey, rather than a destination, demanding an ongoing effort.
Consider typical Windows web application architecture: there are many components in the architecture, including a router, firewall, load balancing and clustering technologies, the operating system, and IIS.
Internet
T1,T3, DSL,etcv ;
[^ Router ;
Load Balancing/
Clustering
IIS/FTP/SMTP |
|
* |
IIS/FTP/SMTP |
||
|
|
|
Windows Server |
] |
Windows |
Server 1 |
Server |
! Server n |
|
|
|
Each of these components plays its part in securing the server infrastructure. For example, the firewall filters network traffic such as ports, packets, and protocols before it reaches the web server. Let's assume our web site is going to use HTTP and HTTPS, and that we've blocked all the ports other than 80 and 443. Although we might have closed all the ports other than 80 and 443 in our firewall, and so increased security massively, our site isn't necessarily secure (a welcome addition to Windows has been the addition of the Internet Connection Firewall Feature with Windows XP).
It is very important to configure each of these components to maximize their security.
Security Planning
Like application development, security has many phases, such as planning, implementation, testing, and monitoring:
384