These are practical, straightforward steps that developers can take, with code examples, links to Secure Software. Consider this article a sneak peek at the latest OWASP Top 10 list for developers. If you have questions or suggestions, we’d be happy to hear from you, Just send an email to firstname.lastname@example.org.
1. Protect Your Database From SQL Injection
One of the most dangerous (and most common) attacks on web applications is SQL Injection: attackers inserting malicious SQL into a dynamic SQL statement. SQL injection vulnerabilities are easy for an attacker to find and exploit using free tools like SQL Map or SQL Ninja, or even manually: try inserting a value like 1′ or ‘1’ = ‘1 into the user name, password, or any other text fields and see what happens. Once SQL injection vulnerabilities are found, they’re easy to exploit.
Luckily, SQL injection is also easy to prevent. You simply need to parameterize your SQL statements, making it clear to the SQL interpreter which parts of a SQL statement make up the command and which parts are data. OWASP has a Cheat Sheet that explains how to parameterize queries in Java (using prepared statements or Hibernate) and in other languages.
2. Encode Data Before Using It
The solution to injection attacks is simple in concept: if you can’t clearly separate code from data (which is what you do to prevent SQL injection using a parameterized API), you have to make the data safe before handing it off to an external interpreter, such as an XML parser, an OS command shell, or a browser.
To do this you need to output encode/escape data before handing it to the interpreter so that the interpreter will not recognize executable statements in the data.
3. Validate Input Data Before You Use It or Store It
All data from outside your program or service, especially data from remote clients, is evil and needs to be validated: files, parameters, HTTP headers, cookies, … It doesn’t matter if the client or the other system validated the data. You need to validate it again.
The basic rules of data validation are as follows:
- Don’t rely on client-side checking. Always check on the server.
- Use positive, whitelist validation rules wherever possible. Negative, blacklist checks that reject data if they contain dangerous or illegal values can be subverted through (double) encoding and other evasion tricks. Where you can, use strong whitelist rules that specify the size and range of acceptable values using regular expressions. Look to libraries like the Apache Commons Validator for help in how to properly check for data types like dates, currencies, IP addresses, URLs, and credit card numbers.
4. Access Control—Deny by Default
Deciding who needs what access to which data and to which features—and how these rules will be enforced—should be carefully thought through upfront in design. It’s a pain to retrofit access control later without making mistakes.
- Implement access control rules in a central, server-side management library, instead of sprinkling these rules throughout the business logic. This makes it much easier to audit and update the rules. Use the access control functions of your application framework, or use a security library like Apache Shiro to do this.
- Only use server-side trusted data (data that has been properly validated) to make access control decisions.
- Deny by default—all functions should check to make sure that the user is authorized before proceeding.
5. Establish Identity Upfront
Building your bulletproof authentication and session management scheme isn’t easy. There are lots of places to make mistakes, which is why “Broken Authentication and Session Management” is #2 on the OWASP Top 10 list of serious application security problems. If your application framework doesn’t take care of this properly for you, then look at a library like Apache Shiro to provide functions for authentication and secure session management.
Try to enforce multi-factor authentication if you can. If you have to rely on just User IDs and passwords, make sure to follow rules for password length and complexity. If you are using an email address as the User ID, be careful to keep it safe: bad guys will try to harvest email addresses for other purposes.
When storing passwords, you can’t get away with just salting and hashing the value anymore. OWASP’s Password Storage Cheat Sheet explains what you need to do and what algorithms to use.
6. Protect Data and Privacy
Protecting data and privacy is about access control (which we’ve already talked about), auditing (which we’ll cover under logging), and encryption: encrypting data in transit, at rest, and during processing.
For web apps and mobile apps, encrypting data in transit means using SSL/TLS. Using SSL isn’t hard. Making sure that it is set up and used correctly takes more work. OWASP’s Transport Layer Protection Cheat Sheet explains how SSL and TLS work and rules that you should follow. The Cheat Sheet on Certificate Pinning explains how you can prevent man-in-the-middle attacks when using SSL/TLS.
The most common mistakes in encrypting data at rest are:
- Forgetting to encrypt data in the first place
- Trying to roll your encryption algorithm
- Mishandling keys or other setup steps for standard encryption libraries
OWASP has another Cheat Sheet on Cryptographic Storage which covers the different crypto algorithms that you should use and when. Libraries like Google KeyCzar or Jasypt will take care of the implementation details for you.
7. Logging and Intrusion Detection
Logging is important for more than troubleshooting and debugging. It is also critical for activity auditing, intrusion detection (telling Ops when the system is being hacked), and forensics (figuring out what happened after the system was hacked). You should take all of this into account in your logging strategy.
Review code for correct logging practices and test the logging code to make sure that it works. OWASP’s Logging Cheat Sheet provides more guidelines on how to do logging right, and what to watch out for.
8. Don’t Roll Your Own Security Code
Know your tools and use them. There are lots of built-in security features in frameworks like Spring Security, Ruby on Rails, .NET, AngularJS, and Play, along with iOS and Android mobile platforms that will take care of common security problems for you, if you use them right. Take the time to understand and use them properly.
9. Handle Errors and Exceptions Correctly
Error Handling isn’t sexy, but it has to be done right. Mistakes in error handling and exception handling lead to different kinds of common and serious security vulnerabilities:
- Leaking information that attackers can use to penetrate your system. Stack traces and detailed error messages can disclose too much technical information about your run-time environment or architecture. For example, “invalid user” or “invalid password” instead of “invalid logon” helps bad guys as much as it helps users.
- Missing or inconsistent error handling can lead to errors going unnoticed, unpredictable behavior, or crashes. A University of Toronto study found that small mistakes in error handling can lead to catastrophic system failures in large systems.
10. Build Security Testing Into Development
Make sure that you have good automated unit and integration test coverage for security features and controls (like authentication, access control, and auditing) and critical business features: code that handles money, private data, trade secrets, and admin functions. This has to include both positive and negative tests.
Other system-level security tests and checks can be automated in CI/CD using tools like Gauntlet, BDD-Security, and Zapper (a Jenkins wrapper over the OWASP Zed Attack Proxy). These tools make it easy to run security tests and provide clear pass/fail feedback.
It’s your code. It’s your job to make sure that it is safe and secure.