CGI programs and C++-based plug-ins operate with relatively unfettered access to the server machine on which they execute (limited on Unix machines by the user account permissions of the web server process). This isn't so bad for an isolated programmer developing for a single web server, but it's a security nightmare for internet service providers (ISPs), corporations, schools, and everyone else running shared web servers.
For these sites, the problem isn't just protecting the server from malicious CGI programmers. The more troublesome problem is protecting from careless CGI programmers. There are dozens of well-known CGI programming mistakes that could let a malicious client gain unauthorized access to the server machine. One innocuous-looking but poorly written Perl eval function is all it takes. For an extensive list of CGI security gotchas, see Chapter 6 of The WWW Security FAQ at http://www.w3.org/Security/Faq/www-security-faq.html.
To better understand the situation, imagine you're an ISP and want to give your customers the ability to generate dynamic content using CGI programs. What can you do to protect yourself? Historically, ISPs have chosen one of three options:
He's a good guy and a smart programmer, and besides, we have his credit card number.
If he reads the WWW Security FAQ and passes a written test, we'll let him write CGI programs for our server.
Before we install any CGI program on the server, we'll have our expert review it and scan for security problems.
None of these approaches work very well. Having blind faith is just asking for trouble. Programmer education helps, but programmers are human and bound to make mistakes. As for code review, there's still no guarantees, plus it takes time and costs money to do the extra work.
Fortunately, with servlets there's another, better solution. Because servlets are written in Java, they can be forced to follow the rules of a security manager (or access controller with JDK 1.2) to greatly limit the server's exposure to risk, all with a minimal amount of human effort.
Servlets built using JDK 1.1 generally operate with a security model called the "servlet sandbox." Under this model, servlets are either trusted and given open access to the server machine, or they're untrusted and have their access limited by a restrictive security manager. The model is very similar to the "applet sandbox," where untrusted applet code has limited access to the client machine.
What's a security manager? It's a class subclassed from java.lang.SecurityManager that is loaded by the Java environment to monitor all security-related operations: opening network connections, reading and writing files, exiting the program, and so on. Whenever an application, applet, or servlet performs an action that could cause a potential security breach, the environment queries the security manager to check its permissions. For a normal Java application, there is no security manager. When a web browser loads an untrusted applet over the network, however, it loads a very restrictive security manager before allowing the applet to execute.
Servlets can use the same technology, if the web server implements it. Local servlets can be trusted to run without a security manager, or with a fairly lenient one. For the Java Web Server 1.1, this is what happens when servlets are placed in the default servlet directory or another local source. Servlets loaded from a remote source, on the other hand, are by nature suspect and untrusted, so the Java Web Server forces them to run in a very restrictive environment where they can't access the local file system, establish network connections, and so on.[3] All this logic is contained within the server and is invisible to the servlet, except that the servlet may see a SecurityException thrown when it tries to access a restricted resource. The servlet sandbox is a simple model, but it is already more potent than any other server extension technology to date.
[3]If you want a local servlet run in the restrictive environment, a workaround is to place them in your server's document root (such as server_root/public_html) and configure the server load them remotely from the same server.
Using digital signatures, it is possible for remotely loaded servlets to be trusted just like local servlets. Third-party servlets are often packaged using the Java Archive (JAR) file format. A JAR file collects a group of class files and other resources into a single archive for easy maintenance and fast download. Another nice feature of JAR files that is useful to servlets is that they can be digitally signed. This means that anyone with the public key for "Crazy Al's Servlet Shack" can verify that her copy of Al's Guestbook Servlet actually came from Al. On some servers, including the Java Web Server, these authenticated servlets can then be trusted and given extended access to the system.[4]
[4]You can create your owned signed servlets using a certificate generated by the JDK's key management tools (javakey in JDK 1.1 or keytool and jarsigner in JDK 1.2). Alternately, you can obtain signed certificates from VeriSign or another certificate authority.
This all-or-nothing approach to servlet permissions is useful, but it can be overly limiting. Consequently, some servlet engines have begun to explore a more fine-grained protection of server resources--for example, allowing a specific servlet to establish a network connection but not write to the server's file system. This fine-grained control is fairly awkward using the JDK 1.1 notion of a SecurityManager class and, therefore, isn't widely implemented, although it can be done, as the Java Web Server 1.1 proves.
The Java Web Server 1.1 includes eight permissions that can be granted to servlets:
Let the servlet load a named servlet.
Let the servlet write any file on the local file system.
Allow the servlet to accept incoming socket (network) connections.
Allow the loading of native libraries, such as the JDBC-ODBC bridge.
Let the servlet read any file on the local file system.
Allow the servlet to connect to an external host.
Permit the servlet to execute external programs on the server. This is useful for servlets that absolutely require access to some system utilities, but it is very dangerous: rm and del qualify as an external programs!
Grant access to java.lang.System properties.
A screen shot of the Administration Tool configuration page that assigns these permissions is shown in Figure 8-3.
Theoretically, any criterion can be used to determine what a servlet can or cannot do. It's possible for the security manager to base its permission-granting decision on any factor, including these:
For example, this servlet can read files and load native libraries but cannot write files.
For instance, any servlet responding to a request from this client user can write files.
For example, any servlet responding to a request from this machine can establish network connections.
For instance, any servlet in a JAR file signed by this entity has full reign on the server system.
JDK 1.2 introduces a new extension to the security manager system: the access controller. The new architecture is quite similar to the "give particular servlets particular privileges" approach implemented by the Java Web Server 1.1, except that it applies to all JDK 1.2 programs and therefore makes fine-grained permission implementations much easier.
An access controller allows what might be called super-fine-grained permission control. Instead of granting a servlet the general ability to write files, with an access controller a servlet can be given the right to write to a single file--perfect for a counter servlet, for example. Or it can be given the right to read and write files only in the client user's home directory on the server--appropriate for a client/server application. With access controllers, servlets can be given the rights to do exactly what they need to do and nothing more.
Access controllers work by placing individual pieces of code, often identified by digital signatures, into particular virtual domains. Classes in these domains can be granted fine-grained permissions, such as the ability to read from the server's document root, write to a temporary directory, and accept socket connections. All permission policy decisions are managed by a single instance of the java.security.AccessController class. This class bases its policy decisions on a simple configuration file, easily managed using a graphical user interface.
Now, instead of relying on complicated custom security managers as the Java Web Server team had to do, a servlet engine need only add a few lines of code to use an access controller. So, while the Java Web Server is the only servlet implementation supporting fine-grained security as of early 1998, once JDK 1.2 becomes popular, it should be easy for other servlet engine implementers to add the same level of fine-grained access control. These implementations may already be available by the time you read this.
Copyright © 2001 O'Reilly & Associates. All rights reserved.