Defending Your XML Web Service Against Hackers, Part II
September 19, 2001
In the last article, we talked about different kinds of attacks, and what you can do from a configuration standpoint to protect yourself from these attacks. In this article, we will focus on design and development issues in regard to attack prevention.
Before we get started, I want to point out a couple of great new tools that Microsoft® has made available for making sure your Web servers are as secure as possible. The IIS Lockdown Tool will make sure your Microsoft® Internet Information Server (IIS) is as inaccessible to would-be attackers as possible. The lockdown tool also gives you an "advanced" option that allows you to pick and choose what settings you want to make. There is also a "rollback changes" option in case the changes it made were not what you intended. Check it out.
Another important tool is the Hotfix Checking Tool for IIS 5.0. This tool queries a constantly updated XML document published by Microsoft for all security patches that are available, then compares this with what is installed on your local machine(s), and reports any discrepancies. Using this tool will help make the job of managing security patches for a single Web server, or a large Web farm, much easier.
When designing your Web service, you must carefully consider security issues and how you will be able to mitigate attack risks. Many of the factors that may come into play when trying to prevent attacks can be addressed at design time. This is when you will want to consider issues such as how you want to perform authentication, or what sorts of faults you want to return.
Determine Your Security Needs
Early on in the design of your XML Web Service, you need to determine the level of security required. Some XML Web Services require no authentication at all, while others have very strict requirements for determining who is using the service. What level of privacy is required for the data that is to be received and sent by your XML Web Service? What kind of costs might be incurred, in man-hours, processing capability, or legal fees if one of the users of your XML Web Service claim that they did not make the requests to your service that your records indicate?
First, we will look at authentication. Certain types of authentication are more vulnerable to attack than others. At the low end, if you use "HTTP basic authentication," you allow anyone who can see the packets on the network to see your username and password. If you are sending requests across the Internet, you have no control over who can or cannot see your packets. On the high end of the authentication scale, you could consider performing authentication using SSL client certificates, which offer an encoded channel along with a non-existent vulnerability to malicious packet sniffers. For a full discussion of authentication options, see Mary Kirtland's "At Your Service" column on Authentication and Authorization.
We already alluded to privacy issues during authentication that you should be concerned about in regard to spoofing. You also need to be aware of privacy issues in regard to any data that you may be sending to and from your XML Web Services—not just usernames and passwords. For instance, you may have a session key that you generate for an authenticated user that the user sends with each request to identify themselves. If this key is sent unencrypted, then a malicious packet sniffer might be able to see that key, use it to send their own requests to your Web service, and therefore be identified by your Web service as the initial, legitimate user.
Another privacy issue is the simple data that is being sent and received by your Web Service. Is the data sensitive enough that it requires encryption? The penalty for SSL encryption is the performance cost of encrypting the entire channel being sent to and from your Web Service. You potentially could encrypt just sensitive items in a request, but then you would probably require custom-written software on your clients to enable the encryption/decryption. One advantage of using SSL to encrypt the entire channel is that most client platforms today have support for basic SSL communications, without requiring application specific code to be written.
As far as basic security design goes, you must also consider the concept of repudiation—that is, that a person may refuse to acknowledge actions that they performed through your XML Web Service. For instance, if you had a stock trading service and someone claimed that they did not ask to sell the stocks that your system sold for them, they are repudiating the sell order. Obviously some XML Web Services may be more concerned with this kind of a problem than others, but you should determine the risk involved for your service and determine what measures make sense in your scenario.
Using a secure system for authentication is certainly an initial step in avoiding these sorts of risks. For instance, you may use HTTP Basic Authentication, which is not secure, but do it over an encrypted channel using SSL, which is secure. Yet even if you have a secure authentication system, it will be of little value if your users have blank or easily guessed passwords. Enforcing the use of strong passwords is an important step in preventing these types of claims. Ultimately, there will be a shared responsibility between the user and the service implementer to protect passwords from being compromised.
Finally, secure authentication and strong passwords mean almost nothing in regard to repudiation if you do not audit the events that occur with your service. Every transaction in which the threat of repudiation is an issue, should be logged along with the user, time, date, and enough information to identify the details of the transaction. Otherwise when disagreements occur, you will lack the evidence required to back up the validity of your arguments.
Auditing, Reporting, and Monitoring
Auditing plays an important role in mitigating repudiation risks; it also plays a critical role in identifying other sorts of attacks. For instance, you may not even realize that your service is under attack, unless you have the statistical data from your audit records to determine unusual use of your service. Would you, for example, notice if someone was performing a dictionary attack on your logon method? So we will look at what you need to consider in regards to auditing, reporting, and monitoring in order to protect your XML Web Service from attack.
The idea behind auditing is to record every piece of information for every event that takes place. This may be impractical, however, if large amounts of data is passed to or from your XML Web Service. Audit records should include, at a minimum, the time, date, and IP address of all requests. If you have an authenticated XML Web Service, you will need to include the username in each audit record. If your service supports multiple methods or message formats, you need to identify which one is being called. Finally, you need to include enough information to fit your needs to identify the details of the call. For instance, if your XML Web Service exposes a method, you may want to log all the parameters passed to that method.
You should keep in mind other needs as well, such as the need to potentially roll back transactions in the event your site is hacked. Also, your audit records tend to be a good source of information for certain reports. And because audit logs may get quite large, you need to coordinate your auditing design with your backup strategy.
While auditing deals with the act of recording all the events that occur with your service, reporting deals with the idea of presenting information on your system's use to users, operators, and administrators. Reporting is an important part of protecting your XML Web Service from attack, because it gives you the means to observe how your service is used. One essential type of reporting, is to report errors that occur. Of prime importance is the ability to report processing errors experienced by your XML Web Service. Similarly, you will want to report errors that may indicate malicious client intent. For instance, if you receive an exceptionally long string as one of the parameters in the request, then you will want to report that error in a way that it will be easily noticed. For errors of these types, you should be creating events in the application event log, so that they can be monitored appropriately. For more information on writing events to the event log see Event Logging in the Platform SDK.
The other type of reporting crucial to your service is generating reports that summarize the use of the service. This should take two forms: First, create global reports for your own analysis that you can use to detect the level of usage or any unusual patterns. It is important to gain a good feel for the appearance of a normal report, so you can recognize unusual use when it occurs. Secondly, you need to provide reports for your users. Your users should also be able to monitor their use of the service. It is possible for attack behavior to be lost in a global report, while an individual user might be able to notice a problem immediately in their individual report.
We cannot talk about reporting without mentioning a very useful type of reporting that you basically get for free if your XML Web Service is running on Internet Information Server (IIS). That is, the IIS logging performed for all incoming HTTP requests, including requests for your service. You can use the information provided in the IIS logs to enhance your own reporting. '
Finally, if you go through the effort of performing audits and of following proper reporting measures, you need to make sure there is some sort of mechanism for discovering the problems reported. This is where monitoring comes in.
Monitoring may take place at different levels. Certainly, viewing periodic reports by hand is one way of monitoring an XML Web Service's usage, but you should also check event logs for errors that have been reported, use performance monitor logs, and take advantage of any one of a number of tools that monitor when a Web server has gone down. Performance Monitor can be critical in detecting attacks. Fortunately, a large number of performance counters associated with IIS can provide a number of important statistics for detecting problems.
You may also want to create your own performance counters for your XML Web Service. For information on creating your own performance counters see Performance Monitoring. It is crucial, in cases warranting unusual consideration, that you be notified in some fashion about what is happening. Take advantage of Performance Monitor alerts to send a pop-up message, or run a program when an unusual event occurs. Figure 1 shows a Performance Monitor alert that will monitor the number of outstanding IIS ISAPI requests, as well as the number of ASP requests that are currently queued.
Figure 1. Creating a Performance Monitor alert
Ultimately it won't do you any good to audit, report, and monitor for abusive actions unless you can do something about a problem should it occur. A denial-of-service attack may be defined to a particular IP address, which means you may need to filter requests from that address at your router. However, a denial-of-service attack or a spoofing attack may be associated with a particular user of your XML Web Service. You must be able to disable accounts when such a problem occurs. This may take the form of simply disabling a Windows user account in the Microsoft® Active Directory™, or if you are doing your own authentication, it may mean that you must add a status field to your user records that can indicate a disabled account. You should also make sure that users of your XML Web Service agree to a "Terms of Service" document that indicates under what situations you can remove or disable their accounts.
Define Your Interface
One of the key advantages to having an XML Web Server application, compared to other Web applications, is that the entire XML schema passed to your application is well defined. What this means to application designers and developers is that you already know that the data your XML Web Service must process is in a valid format. If the data received is not in the proper format, then the tools, such as Microsoft® SOAP Toolkit 2.0 or the .NET Framework, will filter out the request so that you do not have to worry about it.
For instance, you do not need to parse date input for syntactical validity. It must already be in a valid XSD format for dates, or the request would be thrown out. You will want to take advantage of the structure verification, so do not hide structures inside string variables. Take advantage of the power and flexibility of XML to fully describe the data you send and receive.
The Invisible Server
When hackers want to attack your system, the first thing they look for is information. Is this Web site hosted on Windows? Is it hosted on something else? Is it running Active Server Pages? Is Index Server installed? Is a known vulnerable component installed? Is a known vulnerable CGI application installed? Is the host machine running Microsoft® SQL Server? Can I make Distributed COM (DCOM) calls to this server?
For a site concerned about defending itself against attacks, none of these questions should be answerable, by even a clever user on the Internet. The less a hacker knows about your system, the less they can assume about the platform, and the harder it is for them to find problems on your server.
Consider, for instance, what hackers can learn by simply knowing that the URL for your XML Web Service is "http://www.coldrooster.com/ssf/account.asp". Because this URL has an extension of .ASP, they can make a good assumption that this is a Windows machine running Active Server Pages. Hackers now have enough information, based on their knowledge of the default configuration of Internet Information Server, to try numerous vulnerabilities that may not have been configured properly. They could poke and prod at the machine with a lot of most likely valid assumptions about how it is configured.
Now, what if the URL looked like this instead: "http://www.coldrooster.com/ssf/account/"? In this case, the hacker is given no information as to the operating system being used on the server, and they can make no assumptions whatsoever as to its configuration. Mapping a request at the virtual directory level to a specific ASP page is a trivial configuration option and can offer your server a great deal of protection.
For those of you familiar with the basic HTTP protocol, you probably realize that there is an HTTP header that specifically indicates what sort of Web server you are using. Yes, this is another, more difficult way to determine the operating system on a machine, but it is also possible to write an ISAPI filter that removes or replaces this header as well. See Developing ISAPI Filters, and the SF_NOTIFY_SEND_RESPONSE notification in the IIS Programmer's Guide, for information on how to do this. The harder it is to recognize the underlying system that your server runs on, the more likely that a script written by hackers to walk the Internet to find certain types of machines will fail to recognize your machine.
But operating systems alone are not the only vulnerabilities. What happens if the ASP page that you created performs a SQL query against the backend, and throws an exception? Do you return the exception information to the user's browser? This not only indicates the Web server platform, but also the database platform. On top of that, it may educate the user on the specific SQL query you are making, and give them information on how they can change the inputs they are putting into the form to get back information they should not have access to.
How about other COM exceptions? If you let the exception information propagate back to the user, then hackers will know what COM components you have installed on your machine. If the COM object is a third party DLL, then a hacker can probably get a copy of it and make an exhaustive search for any vulnerabilities that it might have. This gives the hacker an extremely low-level look into problems that could exist on your server.
Similarly, the hacker may use the fact that a COM exception was triggered to try to clog up your server. Hackers realize that most exception code paths are not well tested and are often the source of resource leaks or worse. To avoid any exposures in this regard, your code on the server should catch all exceptions, and should return only generic errors. In the case of an XML Web Service, you should return SOAP faults that have very little platform information in them. You may want to somehow return data to the user with an ID that will allow you to match the error with a record in your audit log, but put the details of the failure in the audit log, and not in the returned SOAP fault. I suggest that application designers create their own application's SOAP fault schemas, with a very short list of possible options, and only return a fault on that list. A client calling your XML Web Service does not need to know the details of a SQL query exception; they just need to know that there was a SOAP server fault.
If you are using Microsoft® SOAP Toolkit 2.0, you can implement the ISoapError interface on your COM object to return the exact SOAP fault you want to return, instead of the generic toolkit fault, which provides a boatload of information about what was happening on your server when the fault occurred. The toolkit faults are great for debugging purposes during the development cycle, but they have no place on a production server. They can provide hackers with abundant and potentially destructive information.
A user of your XML Web Service needs to know the format of the SOAP messages being sent to and from your service, and the endpoint location for your service—and that is it. Any other information is simply providing potential hackers ammunition to abuse your system. To protect yourself, limit the platform-specific information being returned, and eliminate the extra pieces on your machine. This should include removing any default virtual directories or script mappings that would help to identify your system to others.
The vulnerability of a server to hackers is inversely proportional to the quality of the code that is running on the server. This includes the quality of the underlying system (the operating system, the Web server, and the SOAP tools being used), as well as the code written for the specific application. It potentially also includes any other code that is running on the server, even if it is not a part of your application. But from a developer's standpoint, we want to look at issues that we can control, and what we can specifically do to keep the quality of our code high, and avoid increasing the vulnerability of our XML Web Service and any other applications that may be running on our server.
The scariest types of attacks on a server are ones where a remote user can execute hostile code leading to a full compromise of your system. Most of these sorts of attacks occur due to buffer overflow bugs. A typical example of such a bug is where a local variable has been allocated in some C code with a fixed length, and then the code copies information from the HTTP request into that variable. If you assume that the data in the request will not be larger than an arbitrary value, then a malicious request to your server can exceed that value and cause the data that it sent to be written passed the end of the allocated buffer. In the case of a local variable, the buffer is stored on the stack, which happens to also hold the address of the code to return to when the current function completes. By writing past the end of the local variable's buffer, hackers can overwrite the return address and cause the function to return to any address the hacker wants, including the address of the Windows CreateProcess function. The parameters that would be passed to the CreateProcess function would also live on the stack, so if hackers overwrite where these parameters are stored, they can functionally launch any application on the server that they want to.
You avoid these types of attacks by making no assumptions on the data being read from the request; then make sure that there are no bugs in your buffer manipulation code. For C/C++ programmers, you should be extra careful of all the following functions: strcpy, strcat, memcpy, gets, sprintf, scanf, and all the various variants of these functions (such as lstrcpy, wcscpy, CopyMemory, etc.). Realize that the strncpy function and the other 'n' functions are only slightly better, since the length variables are quite often determined programmatically in these scenarios and can still overwrite a fixed length buffer. The length parameters in the 'n' functions should be based on the size of the output buffer, and not on the size of the incoming string. If you are going to use these functions, use the 'n' versions and allocate your buffers dynamically from the heap so that there are no stack-overrun possibilities. Also, the Microsoft® Visual Studio® .NET C compiler has support for a new /GS switch that protects your code from many common buffer overrun problems.
Take advantage of the .NET Framework and managed code, which eliminates the potential for most buffer overflow problems. When Microsoft designed the .NET Framework, they realized the benefits of garbage collection, and how it eliminates the problems that can arise with traditional buffer manipulation, so they provided that kind of capability for managed code. You must still be careful about the interaction of managed code with unmanaged code. But at least you have the option of securing your application appropriately if you desire.
Check for Errors
Check the return codes of all functions that you call. If you are calling a Win32 API, make sure that you check that the call completed successfully. If you are allocating memory, make sure that a NULL value was not returned. If you are making COM calls, make sure that an exception was not raised, particularly if you are calling it using Microsoft® Visual Basic®, and you specified "On Error Resume Next." Again, do not make assumptions on the outcome of such calls.
If you are calling Win32 security functions, you should be extra careful. For instance, if you are calling ImpersonateLoggedOnUser, you should check the return code for an error; otherwise, you risk providing the user with an elevated security context. This is a particular concern when your Web applications are configured for running "In Process" on IIS, since code could potentially run as the Local System account, which has almost no restrictions on the local machine. You should also realize that certain cross-apartment COM calls can run in a thread that may also have elevated privileges.
Validate Inputs Early and Quickly
When your XML Web Service receives a request, the first thing you should do is to validate the data that is submitted. The Web Services Description Language (WSDL) validation against the schema will catch many errors, but you should immediately perform any application-level validation that needs to take place. This includes checking for special characters, checking that numeric values are within particular ranges, checking string lengths, and so forth. You should assume incoming data is invalid until proven otherwise, even if you think all requests must come from a particular application. The fact is that requests to your XML Web Service can come from anywhere. If you make any assumptions about your data, it should be that you assume the data is potentially coming from a malicious user.
It's also important that parameter validation code is quick. The sooner you can recognize an invalid request, the better. Otherwise your XML Web Service could be vulnerable to a denial of service attack. It is a lot easier to prevent your servers from servicing legitimate requests if it takes a long time for your servers to process illegitimate requests. Lengthy operations and resource-intensive operations should always be considered as securely as private data. If you must perform a lengthy SQL query, or you have an operation that requires a lot of processing power, make sure that the request is legitimate first. Is the user a valid user? Authenticating the request not only keeps invalid users from using your resources on your server, but provides the ability to track misuse in your audit log in a fashion that will allow you to trace illegitimate use to a particular user. If you are validating inputs, you should always validate the user credentials first. If you use normal HTTP authentication mechanisms, then user authentication is done for you, before your code is even called.
Sometimes it is appropriate during the development phase of a project to provide a means for getting around some of the processing on your server. For instance, it is not unusual for an XML Web Service to generate a key to identify a user across multiple calls, or to maintain some sort of state between those calls. For debugging purposes, it may have made sense to add some extra code to accept a key that was made of all ones, instead of going through the process of generating a real key. However, if you do provide mechanisms for getting around certain checks for legitimate debugging purposes, make sure you remove these backdoors before your XML Web Service goes live.
Similarly, avoid trying to provide clever mechanisms to get around security, even if you think it might help support the service in the long run. Consider the situation where an XML Web Service is performing application-level authentication. It may be tempting to have a secret administrator level username hard coded into your code to allow for an entrance into the system for those who forget their administrator account's password. However, as soon as the first user uses this backdoor, the secret is out, and all other users of this code are vulnerable.
In fact, it may not even take the first legitimate use of your backdoor for the secret to get out. If the backdoor account and password are stored as strings in your code, it is easy for someone looking at your shipping binaries to see any strings that have been defined in your code. If a hacker sees a string in a DLL like "SecretAdministrativeUser", it will raise suspicion that this might be some sort of backdoor into the code, and they will attempt to exploit it.
Finally, in regard to backdoors, you should not attempt to provide a general means for gathering information on a server. Again, this is often done to help the supportability of a product, but it has backfired on numerous occasions. Do not create code that allows you to view or download configuration files or the source code on the system. Although this may be very convenient for analyzing what is going wrong on a server, hackers can use it to get at the same information. Quite often, usernames and passwords are stored in configuration files, and many companies consider the intellectual property of their source code as their most precious asset. The risk involved is even greater when you consider that capabilities like this often allow the viewing of files for other applications on the server as well. So, even if your XML Web Service code seems invulnerable to these capabilities, there may be other applications on the server that are more vulnerable.
Attacks on Web systems do occur as Code Red Worm and its variants have painfully exemplified. Fortunately, you can take steps to decrease the level of risk for your XML Web Service. Hopefully, we have opened your eyes to some of the vulnerabilities that can occur—and how to avoid them—so that you can create a secure XML Web Service.