Undoubtedly there are all sorts of languages in the world, yet none of them is without meaning.
|1 Corinthians 14:10 (NIV)|
The issues discussed in the rest of this book generally apply to all languages (though some are more common, or not present, in particular languages). However, there are also many language-specific security issues. Many of them can be summarized as follows:
Turn on all relevant warnings and protection mechanisms available to you where practical. For compiled languages, this includes both compile-time mechanisms and run-time mechanisms. In general, security-relevant programs should compile cleanly with all warnings turned on.
If you can use a “safe mode” (e.g., a mode that limits the activities of the executable), do so. Many interpreted languages include such a mode. In general, don’t depend on the safe mode to provide absolute protection; most language’s safe modes have not been sufficiently analyzed for their security, and when they are, people usually discover many ways to exploit it. However, by writing your code so that it’s secure out of safe mode, and then adding the safe mode, you end up with defense-in-depth (since in many cases, an attacker has to break both your application code and the safe mode).
Avoid dangerous and deprecated operations in the language. By “dangerous”, I mean operations which are difficult to use correctly. For example, many languages include some mechanisms or functions that are “magical”, that is, they try to infer the “right” thing to do using a heuristic - generally you should avoid them, because an attacker may be able to exploit the heuristic and do something dangerous instead of what was intended. A common error is an “off-by-one” error, in which the bound is off by one, and sometimes these result in exploitable errors. In general, write code in a way that minimizes the likelihood of off-by-one errors. If there are standard conventions in the language (e.g., for writing loops), use them.
Ensure that the languages’ infrastructure (e.g., run-time library) is available and secured.
Languages that automatically garbage-collect strings should be especially careful to immediately erase secret data (in particular secret keys and passwords).
Know precisely the semantics of the operations that you are using. Look up each operation’s semantics in its documentation. Do not ignore return values unless you’re sure they cannot be relevant. Don’t ignore the difference between “signed” and “unsigned” values. This is particularly difficult in languages which don’t support exceptions, like C, but that’s the way it goes.
Here are some of the key issues for specific languages. However, do not forget the issues discussed elsewhere. For example, most languages have a formatting library, so be careful to ensure that an attacker cannot control the format commands (see Section 9.4 for more information).