Using SOAP Headers The following is a simple ASP.NET Web Service written in VB .NET that implements a SOAP header: Imports System.Web.Services Imports System.Web.Services.Protocols
Trang 1When we run this new code, we get the following result:
Another design decision we have to make is whether to use SOAP headers to send additional data with our Web Service
In the previous chapter we examined how we can support SOAP headers on the server Now, we'll look at how we can use SOAP headers with our proxy
Using SOAP Headers
The following is a simple ASP.NET Web Service (written in VB NET) that implements a SOAP header:
<%@ WebService Class="UsingSoapHeaders" %>
Imports System.Web.Services
Imports System.Web.Services.Protocols
Public Class MySoapHeader : Inherits SoapHeader
Public Value As String
End Class
Public Class UsingSoapHeaders
Public sHeader As MySoapHeader
<WebMethod(), SoapHeader("sHeader")> _
Trang 2Public Function GetValueOfSoapHeader() As String
Trang 3This WSDL uses an XML Schema to define a SOAP Header, MySoapHeader Our proxy generator (either VS NET or wsdl.exe) will then create a class MySoapHeader with a single member variable Value
SOAP Header Proxy
The following is the VB NET code generated by Visual Studio NET for the WSDL of the above Web Service:
Public MySoapHeaderValue As MySoapHeader
Public Sub New()
Trang 4Public Function GetValueOfSoapHeader() As String
Dim results() As Object = Me.Invoke("GetValueOfSoapHeader", _
The proxy class contains a sub-class, MySoapHeader, that has a member variable, Value The proxy class also contains
a member variable, MySoapHeaderValue, that we use to set the SOAP header
Using the SOAP Header
Let's look at how we use this proxy, along with its SOAP header to send additional data with the Web Service request The following is a simple ASP.NET page (written in VB.NET) that uses the proxy:
<%@ Import Namespace="Simple" %>
<script runat="server">
Trang 5Public Sub Page_Load(sender As Object, e As EventArgs)
' Create a new instance of the UsingSoapHeaders
' proxy class used to call the remote asmx file
Dim soapHeaderExample As New UsingSoapHeaders()
' Create a new instance of the mySoapHeader class
Dim myHeader As New MySoapHeader()
' Set the value of myHeader
myHeader.Value = "Sample Header Text"
' Set the MySoapHeader public member of the
' UsingSoapHeaders class to myHeader
soapHeaderExample.MySoapHeaderValue = myHeader ' Get the result of the call
Dim result As String
result = soapHeaderExample.GetValueOfSoapHeader()
span1.InnerHtml = result
End Sub
</script>
<font size=6>The value of the SOAP header is: <font color="red">
<span id="span1" runat="server"/></font></font>
Trang 6In the Page_Load event handler we create a new instance of the Web Service proxy, soapHeaderExample We also create an instance of MySoapHeader that represents the SOAP Header (myHeader) We set the Value field of myHeader to "SampleHeaderText"
Next, we set myHeader to the soapHeaderExample member variable MySoapHeaderValue Finally, we call the GetValueOfSoapHeader Web Service The MySoapHeader class instance myHeader is serialized as a SOAP header for the message and is sent along with the request When the call completes we display the value that was sent as a SOAP header
Using SOAP Headers is easy The WSDL of the service describes what the SOAP Header is, and the proxy generation tool turns the XML description found in the WSDL into a NET class that we program with SOAP Headers are very powerful since they allow us to pass out-of-band data (data that is part of the request, but doesn't belong as part of the method marked as a Web Service) For example, if we want to send a user ID with each request we obviously don't want to design each of the methods in our Web Service to accept that user ID as a parameter It would be much easier to simply make use of a SOAP header
This example is relatively simple, but it's enough to allow us to demonstrate how we could build some meaningful applications that use SOAP headers Later, we'll look at a real-world example of how we could use SOAP headers Specifically, we'll look at how we can use SOAP headers to send authentication details with each request Using SOAP headers for authentication is obviously a 'custom' implementation of authentication and authorization, so should start by reviewing the security features that are already available
Web Services Security
Security, as it relates to Web Services, almost seems to be a taboo topic Individuals and companies are still trying to understand just what exactly they can build with Web Services, but still aren't quite sure how they should protect those investments
Fortunately, because ASP.NET Web Services are simply part of an ASP.NET application, we can take advantage of all the security features provided by ASP.NET including solutions for granting or denying access to resources based on the identity of the user (their username, password, and the groups/roles that the user may belong to) as well as protecting the data through the use of encryption, such as using Secure Sockets Layer (SSL) with HTTP (HTTPS) We can also step outside the bounds of what ASP.NET provides and implement some custom solutions, which we'll discuss at the end of the chapter
Let's start with the authentication and authorization security features provided by ASP.NET
ASP.NET Authentication and Authorization
Trang 7As we learned in Chapter 14 ASP.NET has several offerings for authenticating and authorizing access to ASP.NET resources:
Windows Authentication - Uses existing Windows users, groups and security concepts, such as Access Control Lists (ACLs), to protect resources To enable Windows authentication/authorization, IIS must also enforce Windows authentication, either through NTLM or Clear-Text/Basic Authentication
Forms Authentication - Uses user-defined users and roles and ASP.NET's URL authorization features Using the ASP.NET configuration system, the developer defines what a given user or role is allowed to access ASP.NET then enforces these permissions and redirects the user to a login page if they do not present a valid Forms Authentication cookie
Passport Authentication - Uses a distributed authentication store to validate usernames and passwords
Windows Authentication
To enable Windows Authentication for our Web Service we need to instruct IIS to use Windows authentication This is done through the IIS Administration tool (Start | Programs | Administrative Tools | Internet Services Manager) Then, we find the resource we want to enforce Windows security upon, such as /WebServices/BasicAuthentication/Server Right-click on the resource, select Properties from the menus and then select the Directory Security tab:
From here we'll select the Edit button for Anonymous access and authentication control This brings up the Authentication Methods dialog:
Trang 8We can see the default settings Anonymous access is checked and Authenticated Access is set to use Integrated Windows authentication With Anonymous access checked, IIS will not, by default, require the client to present identity
to the server Instead the server will treat all requests, including Web Services, as anonymous - unless the resource, such
as a file, requested does not allow anonymous access in which case the client will be challenged for credentials
Uncheck Anonymous access, and leave Integrated Windows authentication checked This will instruct IIS to enforce NTLM authentication challenges for resources requested in the directory
/Wrox/WebServices/BasicAuthentication/Server We can then write a simple ASP.NET Web Service (shown here in VB NET) to validate the credentials used:
Trang 9End Function
End Class
This Web Service simply uses the ASP.NET User object's Identity.Name property to return the name of the
authenticated user back to the caller We can now build a proxy for this Web Service and test to see if the credentials are enforced The following is a simple ASP.NET page (written in VB NET) that uses the proxy to call the SecureSample Web Service:
<%@ Import Namespace="System.Net" %>
<Script runat="server">
Public Sub Page_Load(sender As Object, e As EventArgs)
' Create an instance of the SecureSample proxy
Dim secureSample As New SecureSample()
' Create a new NetworkCredential class
Dim credentials As New NetworkCredential()
' Set username and password values
credentials.UserName = "demo"
credentials.Password = "00password"
credentials.Domain = "rhoward"
' Set the credentials
secureSample Credentials = credentials
Trang 10' Call its WhoAreYou() function
lblSecureSample.Text = secureSample.WhoAreYou()
End Sub
</Script>
<asp:label id="lblSecureSample" runat="server"/>
This code creates an instance of the SecureSample proxy and then creates an instance of NetworkCredentials The NetworkCredentials class is used to specify a UserName, Password, and Domain These credentials are then used along with the network request For our proxy class we assign the NetworkCredentials instance to an inherited property of the proxy, Credentials Then we can make our request to the Windows authenticated Web Service If the credentials are valid the user is authenticated, otherwise they are denied access
This example uses NTLM authentication When we make a request to the web server, the web server determines that Windows authentication is required and issues a challenge response back to the caller The caller then has the opportunity
to send a hashed value of the username, password, and domain to the server The server will then attempt to create the same hash If the values match then the user is considered to be 'authenticated' Sometimes, however, we can't use NTLM authentication For example, our client could be a non-Windows application- in which case we might decide to use Clear-Text/Basic Authentication
Clear-Text/Basic Authentication
Whereas Windows NTLM authentication sends a hash of the credentials, this hash can only be computed by an application that is able to create the hash Some Web Service clients may not wish to use NTLM authentication, but are likely still to
be able to use Clear-Text/Basic Authentication
Clear-Text/Basic Authentication works in a similar manner to NTLM, in that the server issues a challenge, but rather than sending a hashed value of the credentials the client Base64-encodes the values The format is BASIC:
Domain\Username:Password Basic authentication hides the data, but the credentials can be easily extracted The value is sent using the AUTHORIZATION HTTP header and the value of this header is the Base64 encoded value Clear-Text/Basic Authentication, by itself, is not considered a secure form of authentication However, when used with HTTPS, which encrypts all communication between the client and server, it is quite useful
We can enable this authentication option in IIS, by revisiting our Authentication methods dialog and selecting Basic authentication only
Trang 11When we enable Basic authentication, IIS will display a warning telling us that this authentication method will pass credentials in clear text
We can use the proxy class to make calls to the Web Service However, instead of using a secure authentication mechanism, our credentials are passed in Base64-encoded clear text
Another option is to use ASP.NET Forms Authentication to validate the user However, we have to make some special arrangements to use this type of authentication with Web Services This is because Forms Authentication uses cookies, and we have already seen how a cookie is kept in memory and is lost when the proxy is destroyed Additionally, Forms Authentication makes use of HTTP redirects to an HTML form for unauthenticated users
Forms Authentication
ASP.NET Forms Authentication allows us to use an HTML form to request the credentials for a user The submitted credentials can then be validated against any data store, and if the credentials are considered valid, an HTTP cookie is sent
to the client This cookie is used on subsequent requests to authenticate the user
While Forms Authentication is a very flexible system, it is somewhat cumbersome to use with Web Services The main issue is that when configured for Forms Authentication, ASP.NET will redirect requests to an HTML forms login page This will cause problems for a Web Service proxy that is expecting a SOAP response To use Forms Authentication with ASP.NET Web Services, we have to bend the rules a little To look at how this is done we'll use the same SecureSampleWeb Service we used earlier with one minor modification:
<%@ WebService Class="SecureSample" %>
Trang 12Public Function Authenticate(username As String, password As String)
If (username = "John") AND (password = "password") Then
Trang 13static method SetAuthCookie to write the Forms Authentication cookie to the client We also need a web.config file that configures this Web Service to use Forms Authentication:
<configuration>
<system.web>
<authentication mode="Forms">
<forms name=".ASPXAUTH" loginUrl="login.aspx"
protection="All" timeout="30" path="/" />
Yet another solution to the authentication problem is to use client certificates Client certificates usually go hand-in-hand with SSL, so we'll discuss them together
HTTPS Encryption and Client Certificates
The recommended strategy for hiding data in transmission is to use the HTTPS protocol HTTPS uses public/private asymmetric cryptography to provide a secure communication between the caller and the server
You should refer to the IIS documentation for directions on how to install and use a server certificate for secure communication
Trang 14Once a server certificate is installed, we can enforce the use of the certificate on our Web Application by simply opening the properties dialog for our Web Application, selecting the Directory Security tab, and clicking the Edit button under
Secure Communications, and then checking the Require secure channel (SSL) checkbox:
Requests to this web application will now require HTTPS to request data from the server, thus encrypting content We can simply add an ASP.NET Web Service to this application and the use of HTTPS will be transparent
An ASP.NET Web Service served from a web application with HTTPS enabled will generate WSDL that correctly identifies the Web Service as using HTTPS (described via the location attribute of the port setting in our WSDL) When we build
a proxy using the WSDL, the proxy will automatically be configured to use HTTPS for secure communication with the server
Another option, which is only available when the server has an installed certificate, is to enable the server to recognize client certificates
Client Certificates
Server certificates are issued by a third party to ensure that the identity of the server is valid and can be trusted A client certificate is also issued by a trusted third party, but is used to ensure that the identity of the client is valid and can be trusted A client can send a request to the server along with the client certificate The server can then determine if the certificate is trusted and use the certificate to authenticate and authorize the client
The most common use of client certificates is a certificate installed with the browser For example, if we obtain a client
Trang 15certificate we can view the certificate in Internet Explorer For Internet Explorer 6, this is done by selecting Tools | Internet Options This opens the Internet Options dialog, and from this dialog we select the Content tab:
If we press the Certificates button we'll be provided with a list of the client certificates installed
We can require the use of client certificates on the server by configuring our web application through the same Secure Communication dialog box we used to enable HTTPS (note that this requires that we have a server certificate as well) To enable support for client certificates, we simply check the Require or Accept client certificates option
Clients that make requests to this web application are then required to present a client certificate If the client does not present a certificate, access is denied If certificates are accepted, it is then our responsibility to determine whether or not
to authenticate the request We validate the certificate through code, as we'll see in a moment
Before we use the certificate from our proxy, we'll test the certificate using a browser that has a client certificate installed and access an ASP.NET Web Service on a server that requires the use of client certificates - this all happens transparently within our code
If client certificates are installed, we can also write some code on the server that examines the certificate and extracts meaningful information from it Below is a simple ASP.NET Web Service (written in VB NET) that accesses certificates presented by the client and extracts meaningful details:
Trang 16Public Function GetCertDetails() As CertDetails
Dim request As HttpRequest = HttpContext.Current.Request
Dim cert As New CertDetails()
Trang 17End Class
Public Class CertDetails
Public Issuer As String
Public Subject As String
Public Valid As Boolean
Public KeySize As Integer
Public PublicKey As String
Public Cookie As String
End Class
If we test this Web Service through a browser and the client presents a certificate, we should see a result similar to this:
This example demonstrates how a certificate sent by the client is used on the server to recognize the client's identity Now we'll look at the same example, but instead of using a browser to present the certificate we'll use our ASP.NET proxy to present it along with the request
Trang 18Using Client Certificates with Web Services
To use client certificates with our proxy, we need to export the client certificate If we select Tools | Internet Options from Internet Explorer, select the Content tab, and view the installed certificates (as we did earlier), we can select a certificate and export it
If we select the certificate and press the Export button, the Certificate Export Wizard will open We can simply press the
Next button, accepting the defaults, until we arrive at the File to Export dialog Here we need to provide a filename for the exported certificate as well as a location, for example C:\cert.cer We can then click the Finish button and our certificate is exported
We now need to modify our application that's using the Web Service proxy to supply the certificate with the call:
<%@ Import Namespace="System.Security.Cryptography.X509Certificates" %>
<script runat="server">
Public Sub Page_Load(sender As Object, e As EventArgs)
Dim cert As new CertExample()
Dim details As CertDetails
Trang 19Cookie: <asp:label id="lblCookie" runat="server"/>
In this code we use the X509Certificate class to load the client certificate from the C:\cert.cer file We then add the certificate to our proxy and issue the request
In the next section, we're going to delve a bit deeper into the security issues that relate to Web Services
Trang 20Custom Authentication and Authorization
SOAP headers provide a great way to send out-of-band data We use HTTP headers to send details that aren't directly part
of the body with the HTTP message, and we can do the same thing with SOAP headers This allows us to decouple application details, such as session cookies and authentication, from the transport protocol and instead pass them as part
of the SOAP message This way, no matter what transport the SOAP message is sent over these details remain with the message- instead of being lost when we change transport protocols
We'll look at an example that uses SOAP headers to send an 'authentication' header, rather than relying upon a cookie or
an HTTP header This example shows off several great features of ASP.NET:
Custom Authentication - Bypass the authentication features that ASP.NET offers, such as Forms or Windows, and plug our own authentication system into ASP.NET
SOAP headers - Use SOAP headers to transmit credentials and decouple the information from the HTTP headers, making the message transport-independent
HTTP Module - Our example makes use of an HTTP module that looks at each request and determines if it is a SOAP message
Custom Application Events - The HTTP module raises a custom global.asax event, within which we implement our application logic to verify credentials
Let's start by examining the Web Service
ASP.NET Web Service
Below is the code for a Web Service (written in VB NET) that implements an authentication SOAP header:
<%@ WebService Class="SecureWebService" %>
Imports System