Professional ASP.NET Security - Jeff Ferguson
.pdfDESCryptoServiceProvider des = new DESCryptoServiceProvider(); byte[]
inputByteArray = Encoding.UTF8.GetBytes(stringToEncrypt);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor( key, IV ), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length); cs.FlushFinalBlock();
return ms.ToArray();
Let's take a look at what modifications are needeed on our client.
The first thing we need to modify is the namespace for the proxy class that is being imported. We then add a new class named EncryptedSOAPAuthHeader, which is derived from the base class System.Web.Services.Protocols.SoapHttpClientProtocol.
<%@ |
Import Namespace="SOAPEncryptlProxy" %> <%@ |
|
Import |
Namespace="System.Web.Services.Protocols" |
%> <%@ |
Import |
Namespace="System.Web.Security"%> |
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional//EN" > <HTML><HEAD>
<title>SOAPHeaderAuthClient</title>
<Script language="C#" runat="server"> public class EncryptedSOAPAuthHeader : System.Web.Services.Protocols.SoapHttpClientProtocol
{
Next, we set the URL in the constructor:
public EncryptedSOAPAuthHeader()
this.Url = "http://localhost/SoapEncrptl/ppv.asmx";
Now we're ready to include our SoapEncryptExt and set the DecryptMode value to None.
[SoapEncryptExt(Decrypt = DecryptMode.None)] [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/get Credentials")]
Next, we invoke the getCredentials method of our pay-per-view web service, and return our results.
public string getCrentials()
object!] results = this.Invoke("getCredentials", new object[0]); return ((string)(results[0]));
366
Web Service Security
Everything else remains the same. We call the getCredentials method of our ppv object and write out the results:
3 SOAPHeader AuthClient - Microsoft Internet Explorer |
|
|||||
Rte |
Edit |
View |
Favorites |
Tools |
Help |
|
4»8ack » "» |
• |
£ j3 i3 |
'S5ear* |
^Favorites |
'^fMedia |
|
Address |^J http: //localhost/50APEncrypt 1 /50APHeader AuthClient .a |
up |
SOAP Encrypted Authentication
Results:
182148 37
46201 12
127 143
18555 192
98 24 236
3544
Encrypting Other Message Nodes
To encrypt other nodes of our SOAP message such as soap:Body or soap: Envelope, all we need to change is the following line in the EncryptSoap and DecryptSoap methods of our SoapExtension:
XmlNode node = dom.SelectSingleNode("//soap:Header", nsmgr);
If we want to encrypt or decrypt the soap: Body or the entire soap: Envelope, we just replace soap: Header with the node we want to encrypt or decrypt.
Role-Based Security for Authorization
We talked extensively in Chapter 8 about authorization based on roles, so we won't repeat it all here However, we will take a look at what is required to extend our pay-per-view web service to support authorization based on roles.
Role-Based Authorization in a Web Service
Since we'll be authorizing our consumer sites based on credentials stored in a database, we will be using the Genericldentity and GenericPrincipal objects. These objects enable us to implement role-based security that is independent of the Windows security system. The Genericldentity class provides us with the identity of a user, based on a custom authentication method. The GenericPrincipal class represents users and roles in a custom authentication mechanism.
Typically, applications that use GenericPrincipal objects attach the created GenericPrincipal to the current thread by setting the Thread. CurrentPrincipal property. This makes our principal object readily available to the application for subsequent role-based security checks, and provides access to this object for any assemblies that the application might call on the thread. Attaching the Principal object also enables our code to use declarative role-based security checks and security checks using PrincipalPermission objects.
OCT
XML Key Management Specification
XML Key Management Specification, otherwise known as X-KMS, establishes a standard for XML-based applications to use Public Key Infrastructure (PKI) when handling digitally signed or encrypted XML documents. XML Signature addresses message and user integrity, but not issues of trust, which key cryptography ensures.
XKMS comprises two subprotocols:
Q X-KRSS (XML Key Registration Service
Specification) Q X-KISS (XML Key Information
Service Specification)
X-KRSS calls for a registration server that will register, issue, revoke, and recover public and private key pairs that are used to attest to the authenticity of the user, regardless of whether the server uses X.509, PGP (Pretty Good Privacy), or SPKI (Simple PKI) technologies. The registration server intermediates between the client and the PKI service that stores key and user information. The role of X-KRSS is similar to registration and certificate authorities in a PKI.
X-KISS, on the other hand, involves assertion servers that will retrieve and validate keys from a registration service. This is done through an XML Signature's Keylnf o element, which contains important information on keys, users, and other attributes. X-KISS allows an application to off-load the processing of this information to the assertion server.
More information about the XML Key Management Specification may be found at
http://www. w3. org/2001/XKMS/Dra fts/xkms. html.
Security Assertion Markup Language
Security Assertion Markup Language, otherwise known as SAML, aims to standardize the exchange of user identities and authorizations by defining how this information is to be presented in XML documents, regardless of the underlying security systems in place.
SAML is a system of XML-based messages that supply information about whether users are authenticated (uniquely known to a system), what attributes they have (such as their role within an organization), and whether they are allowed to access and manipulate electronic resources based on those attributes and other policy rules. There is also a request/response protocol for determining what is allowed to be asked of a server (request) and what form the server's reply can take (response).
More information about Security Assertion Markup Language may be found at
http://www. oasis-open, org/committees/security/.
Summary
In this chapter we took a look at several security issues that need to be considered when implementin secure web services, and began by investigating the basic framework of web services security. In man respects, this is not too different from ASP.NET security issues in general.
370
Web Service Security
We investigated application-level security, and the technologies that are available for building a secure web service. In this section of the chapter, we took a look at how to implement a custom authentication mechanism for a web service using either forms or SOAP-based authentication. We even took a look at how we can build a pay-per-view web service and secure it using SOAP authentication and encryption.
We also spent some time examining how we can use role-based security for authorization to our web service.
Finally, we closed our chapter by taking a brief look at some nascent technologies that will enable us to develop better and more secure web services in the future.
•371
Impersonation
Everything that ASP.NET does is executed under an identity. By default, this identity has the username ASPNET but ASP.NET can be configured to use a different logon. As each page request is processed, the configured identity determines what ASP.NET can and cannot do. Impersonation provides us with a way to make this system more flexible - we can change the identity that ASP.NET uses for each page request. We can even change the identity within a page request. The process of acting as another user is called impersonation.
Important Note: Impersonation does not magically give us the ability to circumvent Windows security. We must have the credentials for the user we wish to impersonate, or a user must provide them for us at ru-time.
Why Do We Need Impersonation?
One good reason for using impersonation is to avoid granting privileges to the ASP.NET account, which we want to restrict. Giving a privilege to the ASP.NET account means that all page requests will gain that privilege. If we only want particular page requests or parts of page requests to have the privilege, we can use impersonation to have them act with the identity of a different user account.
Another common reason for using impersonation is to impersonate the user who is using the application, acting under their identity rather than those of the configured ASP.NET account. This allows the actions of ASP.Net to be limited by that permissions granted to the current user. This can restrict their actions, or allow them to do things that the configured account cannot, depending on their privileges.
Impersonation
You may need to use the Object Types button to specify that you want to add a user.
Configured Impersonation
The first type of impersonation we will look at is configured impersonation, where we use the ASP.NET configuration files to define what impersonation behavior we want to achieve.
We can configure impersonation with the <identity> element of the web.config. Let's look at some different scenarios and how we would configure the <identity> element:
We do not want to use impersonation
<identity impersonate="false">
This is the default setting for <impersonates Our code will run under the standard ASP.NET account.
We want to impersonate the account that IIS uses to service the request
<identity impersonate="true">
This setting will impersonate the account that has been specified in the IIS configuration. This will most likely be something like IUSR_[servername].
This setting is useful because we can configure different sites on a server to run under different accounts - by impersonating these users, we can allow different permissions to different web applications.
We want to impersonate the user who is making the request
•cidenity impersonate="true">
In this scenario, we must also force users to log into IIS by activating one of the IIS authentication methods and disabling anonymous authentication. We looked at how to do this in the previous chapter.
We want to impersonate a user of our choice
<identity impersonate="true" username="chosenUsername" password="matchingPassword">
This allows us to impersonate any user account for which we have a username and password.
Note - This method involves including a username and password for a Windows user account as plain text in the web.config. This can be dangerous, especially if the account in question is highly privileged.
375
Impersonating a User Temporarily
Configured impersonation allows us to impersonate a user for the entire duration of each page request. If we want more control, impersonating a user for only part of the page request, we will have to do the impersonation ourselves in our code. The key to impersonating a user within our own code is the Windowsldentity. Impersonate method. This method sets up impersonation for an account that we provide an account token for. Account tokens are what Windows uses to identify users once their credentials are approved. If we have a token for a user, we can impersonate that user.
The general process we will use is as follows:
1 . Obtain an account token for the account we want to impersonate.
2. Use Windowsldentity. Impersonate to start impersonation.
3. Call the Undo method of the WindowsImpersonationContext generated by Windowsldentuty. Impersonate to revert to our original identity.
Getting a Token
As we have just mentioned, in order to impersonate a user, we must have a token for that user. Windows returns a token when a user enters their credentials to log in.
There are two main ways that we can get an account token:
Q From the user of our application
Q By sending credentials to the Windows API.
Getting a Token from the Current User
If we have activated Windows authentication in the web.config, we can access the account token of the current user through the Windowsldentity.Token property. This returns an IntPtr object. Tokens are represented in .NET as IntPtr objects, which are representations of pointers to unmanaged memory locations. This does not matter to us - we will simply be passing the IntPtr to Windowsldenity.Impersonate.
Here is an example of extracting the token from the current user:
IntPtr token = ((Windowsldentity)User.Identity).Token;
Getting a Token by Logging a User In
.NET does not currently have built-in functionality for logging a user in with Windows. We therefore have to access the Win32 security API ourselves through a platform invoke.
A platform invoke is one way of accessing functionality that is outside of the managed .Net environrnen
Before we do a platform invoke, we should add the following attribute to our assembly:
376