Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional ASP.NET Security - Jeff Ferguson

.pdf
Скачиваний:
28
Добавлен:
24.05.2014
Размер:
13.26 Mб
Скачать

//check membername/password combo if (Rdr.ReadO)

{

if (Rdr [ "MemberName" ] .ToString ( ) == sMemberName &&

Rdr [ "MemberPassword" ] . ToStringl ) == sMemberPassword) { // sets authentication cookie

FormsAuthentication. SetAuthCookie (sMemberName, true) ; return true; }

else // Failure to signin returns false returnfalse;}

else

//Closethedata reader Rdr. Closet) ; return false;

Building and Testing our Forms-Authenticated Web Service

To build and test our web service, right-click on ppv.asmx in the solution explorer then select Build and Browse from the context menu. It'll take a few seconds to build our service but eventually we should see the following screen that will enable us to see the web service in action:

lUthentication fails,

This web service is using http://tempuri.org/ as its default namespace.

Recommendation: Change the default namespace before the XML Web service is made public.

public

clasa SyVebSecvlce

i //

Implementation

:iass HytfebService

To test the Signin WebMethod of our Web Service, we click on the Signin link and the following test page will be displayed. Enter the Member Name "GUane" and Member Password of "GUoe" and click Invoke:

356

Web Service Security

If you used the dotNetSecurity. sql file to create and populate your database, this action will result in a new browser window that displays a positive (true) response from our WebMethod.

Our example transmits the password across the wire in clear text. This not a good idea for a production application, as it is fairly easy for a knowledgeable person to intercept user passwords as they're transmitted. To secure passwords in transmission, use SSL for applications that implement forms authentication. In the next section of the chapter, we'll take a look at how we can use SOAP to protect passwords as they're transmitted to our web service.

3http://localhost/dotNetSecurity/ppv.asmH/Signln?sMemberfianie=g]

File

£<ft

View

Favortes

Tools

Help

Address |^)ht[p://localhost/dotNetSecurity/ppv.asmx/5ignIn?sMemberName=gijane&sMernberPassword-gi)oe

<?xrnl version="1.0" encoding="utf-8" ?>

<boolean xm!ns="http://tempuri.org/">true</boolean>

OK, now that we've built our web service, let's get back to security and take a look at how we can use SOAP to securely pass credentials and other information into our web service.

Custom SOAP Authentication

SOAP headers are a convenient way to pass user credentials into a web service from a consumer application. A consumer application can add user credentials to the SOAP header and our web service can then retrieve them to authenticate the user before allowing them access. Using SOAP headers provides us with the advantage of not having to pass credentials as part of the parameter for every one of our WebMethods.

357

SOAP Headers in a Web Service

To implement SOAP header authentication for our web service, we are going to need to make some changes. The first thing we need to do is disable the Forms Authentication that we previously set up.

To do this, we open up the web.config file, change the authentication elements mode attribute to None, and remove the forms element. This modification to our web service means that we have now implemented a custom authentication scheme. All of the code that we'll discuss in this section is contained in the DotNetSecuritySOAP folder of the code download for this chapter.

<conf iguration> <system.wek»

outhentication mode="None" />

</system.web> </conf iguration>

Now we can open the ppv . asmx, which contains the web methods for our web service, and add the System. Web . Services . Protocols namespace to the top of the file, in order to get access to the SOAPHeader class.

using System. Web. Services . Protocols ,-

Next we'll define a new class in our web service called SOAPAuthHeader, which is derived from SOAPHeader. Our new SOAPAuthHeader class contains two public string members that inherit from

SOAPHeader:

// SOAPAuthHeader class derived from SOAPHeader public class SOAPAuthHeader : SOAPHeader

{

public string MemberName; public string MemberPassword;

Now we need to add a SOAPAuthHeader field type to our web service class and apply the SOAPHeader attribute to our web service method. The attribute constructor is then defined using the name given to the field type: sHeader:

public class ppv : System. Web. Services .WebService { public ppv()

{

//CODEGEN: This call is required by the ASP. NET Web Services Designer InitializeComponent ( ) ;

}

Component Designer generated code

public SOAPAuthHeader sHeader;

358

Web Service Security

//Signln WebMethod - verifies our users credentials.

[WebMethod(Descriptions"Verifies the users credentials.")] [SoapHeader("sHeader",Direction=SoapHeaderDirection.InOut,Reguired=true)]

Before we go any further, we are going to add a new method to our web service. The getCredentials method will enable us to retrieve the string values passed in by the consumer application and perform a series of validation routines on the strings to ensure that a malicious user is not attempting to pass malformed text (malicious text) into our Signln method. We'll then use the getCredentials method to call the Signln method and convert our Signln method from public to private to make it a little more obscure and more difficult for a hacker to run malicious programs against.

Security by obscurity is not the best methodology to use for defending a web services against malicious attacks, but as long as it doesn't give you a false sense of security, it doesn't hurt to hide critical methods.

public string getCredentials()

{

if (sHeader.MemberName.Length > 0 && sHeader.MemberPassword.Length > 0)

{

if (Signln(sHeader.MemberName, sHeader.MemberPassword) == true) return "Hello " + sHeader.MemberName;

else

return "SoapAuthentication Failed"; }

else

return "Zero Length"; }

private bool Signln(string sMemberName, string sMemberPassword) {

These are all the changes that we need to make to our pay-per-view web service. It's set up to expect a SOAP Header containing user credentials for the purpose of authenticating a consumer application. If we attempt to test the web service using the test page that is built by default, we should notice that we can no longer pass in the credentials via an HTTP Get. That's because our web service will no longer accept either HTTP Get or Post for this web method.

In order to test the getCredential web method on our web service, we must pass a SOAP call into it. With .NET, we can build a proxy that can be used in our client application. The proxy makes our jobs as developers much easier, since we don't have to do the serialization in our code. Instead, we just pass the parameters for the SOAP Header into our web service call through the proxy, and it does the serialization for us.

Let's take a look at how we build a consumer application that uses a SOAP header now.

Consumers and SOAP Headers

In this section, we are going to take a look at how a consumer application populates a SOAP Header.

Since we are discussing .Net Security, we'll use the .NET methodology for accessing the Web Service, and build a proxy to be used by our consumer application. There are multiple ways to create a proxy object: we can use Visual Studio.NET, or a command line. For convenience, there are two batch files that contain the necessary commands in the dotNetSecuritySOAP folder of the code download for this chapter. Run the build.bat file from the command line to build our C# assembly dotNetSecurityProxy.es, and then run the make.bat file, which will compile our dotNetSecurityProxy.dll.

oca

The output from this operation should appear as follows:

etSecuritySOflP>USDI,

/1:CS

/n:dotSec

 

 

 

itySOflPProxy

 

ttp://localhostxdotHetSecuritvSOftP/ ierosofi;

<R>

Ueb

 

 

 

Services

Description

Language

Utility [Microsoft

 

 

 

 

CB>

.NET

 

Framework,

Uersion

1.0.3705.0] "Copyright

 

 

 

<C> Microsoft Corporation 1998-2001. flll rights

 

 

 

iUriting

file

'dotKecuritySOftPProxy.es'.

 

 

 

 

 

 

 

i>h\Wijwroot\dotNetSecuritijSOflP>Paii! y

 

 

 

 

 

 

 

 

key to continue

.

.

.

 

 

 

 

 

 

 

F:\Inetpnb\Ut

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

:urUy50HP>natse.bat

 

 

 

 

 

 

 

 

 

;F: Si netpufoNUvmroot \dotNetSecur it ySOftP>CSC

/r:systero.dll /r:Syste

:ten.Xp!l.dll

 

/r:Systeri.Web-Services -dll /t: library /out :dotSecuritySOAPPVoxy.dll

Microsoft

<R>

Uisual Ctt

-NET

Compiler uersion

7.00.9466

 

for Microsoft

 

<H>

.NET

Framework version

1.0.3705

 

 

 

^Copyright

 

CO Microsoft

Corporation 2081.

fill rights reserved.

jF:Mnetpub\W«wrt

 

 

 

 

 

 

 

 

 

 

 

 

 

.uritySO(IP>Pause

 

 

 

 

 

Press

any

key

 

 

 

 

 

 

 

 

 

 

 

 

tc

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

.F:\InetpubxUuuraotxdotNet!

The SOAP header elements that are required by our pay-per-view web service are detailed in the WSDL contract. We can see the SoapAuthHeader elements in the SOAP header of our WSDL contract in the next figure. Consumer applications running on the Windows platform can use a proxy class to populate the elements of a SOAP Header.

Click h(.-re for a complete list of operations.

getCredentials

Verifies the users credentials,

Test

No test form is available as this service or method does not support the HTTP GET protocol,

SOAP

The following is a sample SOAP request and response, The placeholders shown need to be replaced with actual values.

POST /dotMetSecur itySOAP/ppv.asmx HTTP/1.1

Host: localhost

Content-Type: text/xatl; charset^utf-S

Concent-Length: length

SOAPActxon: "http : // teiaputri.org/ get Credentials"

<?x»l version="l.0" encoding="utf-8"?>

<soap : Envelope xadns:x3im"http: //wwtr, a3 .org/2001/XHLSchertia-instance" xinins:xsd*"http:// <soap: Headeo OOAPAuthHeader x»lns»"http://tempuri.org/"> <HemberNaine>string</HentoerKaKie> <HeiribecPag3word>«trlng</HeR4)erPassBor(a> </SOAPAuchHeader>

<soap:Body>

<getCredentials Xttlns«"http^/tewpuci-org/" /> </soeip:Body> </soap:Envelope>

To build the consumer that will populate our SOAP Headers for us, we'll need to add a new web form to our dotNetSecuritySOAP project. Right-click on the project name in the solution explorer and select Add I Add New Item. In the Add New Item dialog box that appears, select a web form template, give it a name (SOAPHeaderAuthClient. aspx), and click Open. The file may also be found in the dotNetSecuritySOAP folder of the code download for the chapter.

360

Web Service Security

We then open the new web form we have just created. The first thing we need to do then is get access to the namespaces that we'll need, including the namespace for the proxy class we just created:

<%@

Import

Namespace="System.Web.Security"%>

<%@

Import

Namespace= "System. Web. Services . Protocols" %>

<%@

Import

Namespace="dotNetSecuritySOAPProxy" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4 . 0 Transitional / /EN" >

<html>

<head>

<title>SOAPHeaderAuthClient</title>

</head>

We'll use the Page_Load event to execute our script and pass our hard-coded parameters into the SOAP Header. The first thing we'll need to do is create an instance of our SOAP header object, then we'll create an instance of our pay-per-view object to represent our web service, and populate our SOAP Header. Next, we can associate our SOAP header with the proxy for our web service. Finally, we can close our try... catch block.

The last thing we need to add to our form is a web control, which will display the results of the call to our web service.

<Script language="C#" runat=" server ">

public void Page_Load( Object sender, EventArgs E) { try

{

SOAPAuthHeader oProxy = new SOAPAuthHeader (); ppv oWebService = new ppv();

oProxy.MemberName = "gijane"; o Proxy. MemberPas sword = "gijoe"; oWebService. SOAPAuthHeaderValue = oProxy;

IblValidate.Text = oWebService. getCredentials ();

}

catch (Exception Ex)

{

IblValidate.Text = Ex.ToString( ) ;

</Script>

<body MS_POSITIONING= " Gr idLayout " >

<form id="SOAPHeaderAuthClient" method="post" runat=" server "> SOAP Authentication Results :<asp: Label ID="lblValidate" Runat=" server ">< /asp: Label >

</form> < / body > </html>

One more thing: we mustn't forget to move our proxy into the bin folder of our project: the code won't run without it.

•JC-I

3 SOAPHeaderAuthClient - Microsoft I

Fite

Edit

View

Favorites

Tools

Address ||J!] http://localhost/dotNetSecuritySOAP/SOAPHeaderAi

SOAP Authentication Results;Hello gijane

Web Service Encryption Methods

All of the examples that we've looked so far in this chapter have a common security flaw to them: they pass the users' credentials across the wire in clear text. Anyone could easily put a sniffer on the network and read the user IDs and passwords that are being passed.

In this section, we are going to take a look at two of the options that we have for protecting our data as it is transmitted between a consumer application and the web service.

SSL

The first of these options is the Secure Sockets Layer 3.0 or Transport Layer Security 1.0 protocols, commonly know as SSL. The functionality and use of SSL has been so thoroughly documented elsewhere that we won't spend much time on it here.

Detailed information about SSL may be found at http://developer.netscape.com/docs/manuals/security/sslin/contents.htm, and in SSL and TLS: Designing and Building Secure Systems, by Eric Rescorla, Addison Wesley, ISBN 0-201-61598-3 or SSL & TLS Essentials: Securing the Web, by Stephen A. Thomas, John Wiley &Sons, ISBN 0-477-38354-6.

SSL is now widely used on the Internet to provide a secure channel for transmitting confidential data such as credit card numbers. It is a commonly accepted method for securing applications. However, SSL is not without its problems. The primary issue with SSL is performance. SSL uses a public-key encryption scheme in which the server must perform CPU-intensive mathematical operations to begi each session. This involves generating and exchanging a key, and encrypting the channel. Once the session has been established, the effect of SSL on performance is decreased, although encrypting and decrypting information demands a lot of processing resources. SSL accelerator cards or machines i be used to alleviate this problem.

Once the key pair is generated and exchanged, the client and server use it to communicate secui enabling the browser to request a file or web object before closing the connection. To retrieve additional objects, however, the browser must usually reconnect to the server before it can rende page, requiring the server to generate yet more keys. This session startup process can cripple tl performance of an application.

Another issue with SSL for web services is that it is transport dependent. That is, it relies on H This makes it very difficult to route messages using SSL, since all servers along the route would support HTTPS.

362

Web Service Security

SSL is a viable solution for securing our web services, but what if our application only needs to encrypt user credentials or a credit card number. With SOAP, we can selectively encrypt specific information. Let's now take a look at how we can do this.

SOAP

Another option we have for securing data in transit between our web service and the consumer is a SOAP extension. We can use a SOAP extension to encrypt and decrypt SOAP messages exchanged between the web service and the consumer application. A SOAP extension will allow us to simply add another attribute to the method (along with the WebMethod attribute), and have the message encrypted or decrypted. SOAP allows selective encrypting, where SSL does not, thus relieving the major drawback of SSL: performance.

SOAP offers the encryption options:

Q Encrypting only select messages

Q Encrypting only the header of the message

Q Encrypting only the body of the message

Q Encrypting the whole message

Let's take a look at each of these in more detail now.

Encrypting Only the Header of the SOAP Message

Since consumers of our web service pass their credentials in the soap header, we are going to want to encrypt them to prevent them from being intercepted and read. Let's see how this is done.

We open a new ASP.NET Web Service project and add the files from the SOAPEncryptl folder from the code download for Chapter 14. We can safely delete the servicel. asmx file, as we won't need it.

Now we open our pay-per-view web Sservice (ppv.asmx), and add the new custom attribute for our SOAP extension to the web service, as shown here:

//Signln WebMethod - verifies our users credentials.

[WebMethod(Description="Verifies the users credentials.")] [SoapHeader("sHeader",Direction=SoapHeaderDirection.InOut,Required=true) ' [SoapEncryptExt(Encrypt=EncryptMode.Response)] public string getCredentials()

Next we open the SoapEncryptExt. cs file from the code download for this chapter, and add the System.Web. Service. Protocols namespace, so that we can access the SOAPExtension class. Now, take a look at the ProcessMessage method.

The ProcessMessage method allows us get access to the serialization of our SOAP message. Because we want to encrypt our SOAP message after it is serialized and before it is sent, we'll call our Encrypt method. In order to decrypt a message, we'll use the Decrypt method before it is deserialized.

363

public override void ProcessMessage (SoapMessage message) { switch (message. Stage) {

case SoapMessageStage . Bef oreSerialize : break;

case SoapMessageStage . Af terSerialize : Encrypt { ) ; break;

case SoapMessageStage. Bef oreDeserialize: Decrypt ( ) ; break;

case SoapMessageStage . Af terDeserialize : break;

default:

throw new Exception ( "invalid stage");

Now that we've seen when messages are encrypted and decrypted, let's take a closer at how this occurs.

The Encrypt and EncryptSoap methods will jointly provide the encryption services. Encrypt checks the EncryptMode set by the client to see if its set to either EncryptMode . Request or EncryptMode .Response. If so, we call the EncryptSoap method and pass it the encrypted stream:

private void Encrypt ( )

{ newStream. Position = 0;

if ( (encryptMode == EncryptMode. Request) | (encryptMode == EncryptMode. Response) )

newStream = EncryptSoap (newStream) ;

Copy (newStream, oldStream) ; }

The EncryptSoap method accepts the stream passed in by the Encrypt method and returns a new stream with the soap : Header encrypted. We load the stream that was passed into our method into a DOM object. Next we use the AddNamespace method of the XmlNamespaceManager class to load the schema for SOAP to the DOM. Finally, we use the selectSingleNode method of our DOM object to locate the node of the SOAP message that we want to encrypt. In our web service, this is the soap:

Header node. Next we'll call another Encrypt and pass it the innertext of our DOM object.

public MemoryStream EncryptSoap (Stream streamToEncrypt)

{ streamToEncrypt . Position

= 0 ;

XmlTextReader reader =

new XmlTextReader (streamToEncrypt) ;

XmlDocument dom = new XmlDocument ( ) ; dom. Load (reader) ;

XmlNamespaceManager nsmgr = new XmlNamespaceManager (dom. NameTable) ; nsmgr. AddNamespace ( "soap" , "http: //schemas .xmlsoap.org/soap/envelope/" ) ;

364

Web Service Security

XmlNode node = dom. SelectSingleNode (" //soap: Header" , nsmgr) ; node = node.FirstChild.FirstChild;

byte[] outData = Encrypt (node. InnerText) ;

StringBuilder s = new StringBuilder ();

forfint i=0; i<outData. Length; i++) { i f ( i== ( outData . Length- 1 ) )

s . Append ( outData [ i ] ) ; else

s .Append (outData [i] + " " ) ; }

node . InnerText = s .ToString( ) ;

MemoryStream ms = new MemoryStream ( ) ; dom.

Save (ms) ; ms. Position = 0;

return ms ;

DESCryptoServ/ceProv/der

Our SOAP Extension uses the DESCryptoServiceProvider class to access the cryptographic service provider (CSP) version of the Data Encryption Standard algorithm, better known as (DES). The DESCryptoServiceProvider uses a custom key and initialization vector to encrypt and decrypt the selected node of our SOAP message.

The Data Encryption Standard (DES) is a symmetric algorithm, which means it uses the same algorithm and key for both encryption and decryption processes. It is also considered a block cipher, which means it encrypts data in 64-bit blocks. A 64-bit block of plain text input into the algorithm results in a 64-bit block of cipher text output. It uses a 56-bit key. We can seee this key specified in our code, below.

This private Encrypt method, which is called from EncryptSoap, uses the DESCryptoServiceProvider. Our first step is to create two byte arrays to represent our key and our initialization vector. Next, we create a new DESCryptoServiceProvider object. Once we create the DESCryptoServiceProvider object, we then convert the string to a byte array.

We then create new MemoryStream and CryptoStream objects. In order to create the CryptoStream object, we pass in the MemoryStream and call the CreateEncryptor method of the the DESCryptoServiceProvider object, passing in the key and IV (initialization vector).

Finally, we write the encrypted stream into the CryptoStream object and convert the MemoryStream object into an array before we return it.

private

Byte[]

key =

{0x01,

0x23,

0x45,

0x67,

0x89,

Oxab,

Oxcd,

Oxef}; private

Byte [ ]

IV =

{0x12,

0x34,

 

0x56,

0x78, 0x90,

Oxab,

Oxcd,

Oxef};

 

 

 

 

 

 

 

private byte [ ]

Encrypt (string stringToEncrypt)

{

 

 

365

Соседние файлы в предмете Программирование