Innocent Code: Summary of Rules

[Note: This is a pre-language-laundry version. Note also that this chapter does not stand on its own. One should read the book to understand the consequences of breaking the rules.]

Rule 1: Do not underestimate the power of the dark side

Compared to the web application programmer, an attacker is generally more creative when it comes to destructive thinking. It is dangerous to think that something is unbreakable just because one can't think of a way to break it oneself. Don't be sloppy. Never set the rules aside because "this will never happen".
(Section I.1, page xv.)

Rule 2: Use POST requests when actions have side effects

With GET requests, browsers are free to redo the request whenever they feel like it, for instance when the user pushes the "back button" in his browser. That behavior is not acceptable when the action taken makes something change, such as money transfers in a bank. Browsers are not allowed to redo POST requests without first asking the user for permission.
(Section 1.1.1, page 4.)

Rule 3: In a server-side context, there's no such thing as client-side security

Anything coming from the client side may have unexpected values. Even HTTP headers, cookies, hidden fields and option values may be controlled by the attacker. Client-side scripts can be bypassed or modified. Java Applets can be decompiled and replaced by other programs. Never trust data from the client.
(Section 1.1.1, page 6.)

Rule 4: Never use the Referer header for authentication or authorization

The Referer header originates on the client side, and is thus under the control of the user. Attackers may easily change headers to circumvent security mechanisms. Also, many users instruct their browsers or proxies not to send Referer headers, as those headers may be seen as a threat to people's privacy.
(Section 1.1.2, page 7.)

Rule 5: Always generate a new session ID once the user logs in

Often, a session is generated as soon as a someone visits a web site. And often, the session is given more privileges later on, for instance when the user actually logs in. It may be possible for an attacker to get hold of the session ID as the user starts browsing the site, for instance through Session Fixation. The same session ID should not be used once the session is given more privileges.
(Section 1.2.1, page 14.)

Rule 6: Never pass detailed error messages to the client

First, error details may be very valuable as a source for knowledge of the inner workings of an application. Second, system error messages, detailed or not, are indications of weakness. An attacker that is able to provoke an error message, knows that the system leaves handling of malformed input to other layers. He may get the impression that the system has a low guard, and that his search for exploitable holes will go undetected.
(Section 2.1.2, page 32.)

Rule 7: Identify every possible metacharacter to a subsystem

Before passing data to a subsystem, make sure you know about all metacharacters. Note that similar subsystems from different vendors may do things slightly differently. Example: Some database servers give backslash characters inside string constants a special meaning, while others do not. Keep that in mind if a subsystem is replaced with another.
(Section 2.1.3, page 36.)

Rule 8: Always handle metacharacters when passing data to subsystems

Metacharacters cause problems no matter if they come from the remote user or from some internal part of the application. We should always handle metacharacters before passing data to a subsystem, no matter where the data come from. If we fail to do so, we may not only experience strange error conditions, but also be vulnerable to second order injection attacks.
(Section 2.5, page 51.)

Rule 9: When possible, pass data separate from control information

If the subsystem we talk to supports data-passing mechanisms that only allow data to be passed, we should use that mechanism in order to avoid metacharacter problems. Such mechanisms include prepared statements for SQL, DOM for XML and operating system pipes rather than command line arguments for external programs.
(Section 2.5, page 52.)

Rule 10: Watch out for Multi-level Interpretation

In some cases, what we see as the subsystem is just a path to another subsystem. Such as when we call a command shell in order to run an external program, or when we create an SQL string that will be used in a LIKE clause. The command shell and the SQL string need metacharacter handling. But the target program and the LIKE pattern matcher may need additional metacharacter handling.
(Section 2.5.1, page 53.)

Rule 11: Strive for "Defense in Depth"

As neither humans nor technology work perfectly all the time, things tend to go wrong every now and then. When dealing with security, we should always look for an opportunity to add redundancy: A backup that may stop an attack if a security mechanism fails. Example: We do everything to avoid SQL Injection, but we should nevertheless configure the database server not to allow write access where it is not strictly needed. Just in case.
(Section 2.5.3, page 55.)

Rule 12: Don't blindly trust the API documentation

Unfortunately, some API documents may incorrectly give the impression that security is taken care of. If you see vague statements on e.g. the possible return values from a function, do not trust those statements. Do your own checks for validity.
(Section 3.1, page 61.)

Rule 13: Identify all sources of input to the application

Input may be far more than what the user types on the keyboard. Values from hidden fields, check boxes, radio buttons, select lists, submit buttons, cookies and HTTP headers may be manipulated by malicious users, and used to make web applications misbehave if we forget to validate. Pay special attention to peculiar mechanisms in the programming platform, for instance mechanisms that let unexpected input override the expected input.
(Section 3.1, page 62.)

Rule 14: Pay attention to the invisible security barrier: Validate all input, always

Make sure you have a thorough understanding on when data are on the server, and when they are on the client. Data being sent from the server to the client and then back again have passed a barrier. On the server everything is presumably safe. On the client it is not. The data may have been modified. Never trust data that have passed the invisible security barrier.
(Section 3.1.1, page 64.)

Rule 15: When filtering, use whitelisting rather than blacklisting

With whitelisting we have a list of intended good values, and filter everything that is not in that list. With blacklisting, we have a list of known bad values and filter those bad values, while keeping the rest. The problem with blacklisting is that we do not filter the set of unknown values. If our list is incomplete, for instance because a standard has evolved since we wrote our application, or because we did not think about all possible bad values, the application may be vulnerable to attacks.
(Section 3.2.1, page 73.)

Rule 16: Do not massage invalid input to make it valid

It is often hard to get things right. An attacker may be able to find a way around the massage, by crafting his attack in accordance with the massage algorithm. When invalid input is detected, there is no need to continue the operation. For invalid user-generated input, give an error message and ask the user to try again. For invalid server-generated input, give a simple error message, and log the incident. Modified server-generated input is generally a sign of an attack.
(Section 3.3, page 76.)

Rule 17: Create application-level logs

The web server normally creates access logs that contain HTTP-level information on requests. These logs do not necessarily reveal application-level attacks. The dynamic web application knows much more about the possible outcome of requests, about the validity of input parameters, about logged-in users and what they are allowed to do and so on, so the application should create its own log.
(Section 3.3.1, page 77.)

Rule 18: Never use client-side scripts for security

Scripts running in the users' browsers may easily be modified or disabled. Never use client-side scripts for validation, authorization, authentication or other security mechanism. Never hide passwords or other secrets in scripts that get passed to the client.
(Section 3.4, page 82.)

Rule 19: When possible, use data indirection for server-generated input

For server-generated input, such as data coming back from hidden fields, option tags and other elements not directly modifiable by the user, it is often a good idea to not reference resources directly, but rather name them using an index or a label. The indirection makes it harder to bypass authorization tests.
(Section 3.5.1, page 86.)

Rule 20: Pass as little internal state information as possible to the client

Data passed to the client side may be modified by an attacker before being returned. It is often hard to remember to revalidate the data, so it is better not to send the data at all if possible. Use server-side storage, such as session objects.
(Section 3.5.2, page 88.)

Rule 21: Do not assume that requests will come in a certain order

An attacker may skip a request and jump directly to the one that normally follows. He may jump back and redo a previous request. Applications that assume a certain order of requests may, in some cases, be vulnerable to both input manipulation attacks, authentication attacks and authorization bypassing.
(Section 3.5.3, page 90.)

Rule 22: Filter all data before including them in a web page, no matter the origin

In order to prevent all kinds of Cross-site Scripting attacks, every piece of data that is not supposed to contain markup should be passed through some HTML Encoding filter before being included in the final web page. If possible, this filtering should be done automatically.
(Section 4.1.3, page 107.)

Rule 23: Stick to existing cryptographic algorithms, do not create your own

Existing cryptographic algorithms contain some heavy math, and they have withstood several years of scrutiny from the top experts in the world. If you need to encrypt anything, use one of the established algorithms. Unless you are a cryptologist, chances are that your home-grown encryption scheme will be broken in minutes by anyone familiar with the toolbox of the cryptanalysts.
(Section 6.1, page 136.)

Rule 24: Never store clear-text passwords

People tend to use the same password for many things. If an attacker gets access to the user passwords from your site, he may try the same user name/password combinations at other, more serious sites. Avoid leaking passwords, even if the worst thing happens. Store hashed passwords only.
(Section 6.2.1, page 143.)

Rule 25: Never use GET for secret data, including session IDs

Parameters to GET requests may end up in Referer headers sent by the browsers. It is often hard to control where those Referer headers are going, so you risk leaking secret stuff to untrusted servers. GET parameters will also be seen in web server and proxy logs, and they will be available in the browser's history.
(Section 6.4.1, page 155.)

Rule 26: Assume that server-side code is available to attackers

There are many ways for an attacker to gain access to server-side code, some of which are outside the control of the programmer. We should try not to base the security of the application on the secrecy of our code. Note that compiling or otherwise obfuscating the code will not stop the determined attacker: Any code that is to be interpreted by machines may also be interpreted by humans.
(Section 6.5.2, page 159.)

Rule 27: Security is not a product; it's a process

We cannot tack security on in the end. And we cannot leave it to the administrators. Our code may be abused in many ways, so for every line of code we write, we need to ask: "How can this line be abused?" and "How should I solve this problem in order not to please the attackers?" We must deal with security throughout the entire development process. Fortunately, as soon as we get used to it, it won't take much extra time.
(Section 7.1, page 164.)