Safeguard your code: 17 security tips for developers
Rigorous input testing, passwords, encryption -- security is a feature no programmer can afford to overlook.
The scary stories from the Web are getting worse. First there were a few stolen credit card numbers. Then there were a few thousand. Now we hear about millions of financial records being exposed by security breaches, and we grow numb to the potential threat. Credit card numbers barely scratch the surface of what the bad guys are after, and there are more dangerous stories that come out of the labs studying cyber war.
Writing secure code begins long before the first loop is formed -- and is no easy task. To even approximate bulletproof code, architects, engineers, auditors, and managers must try to imagine everything that could go wrong with every aspect of the code. Although it's impossible to anticipate every nasty curve the attackers will throw, you have to do all you can to reduce your attack surface, plug holes, and guard against the fallout of a potential breach.
Here are 17 tips for producing more secure code. The list is far from complete, but it should bring you closer to your goal. Feel free to share your insights for developing secure code in the comments below. We all benefit from greater security.
Secure programming tip No. 1: Test inputs rigorously
Attackers need a path into your machines, and the easiest routes are through the doors your code opens. If your software takes input from the Internet, someone will try to sneak something past you.
The classic example is the buffer overflow created by lazy C programmers who accept any string of characters until that string hits the number zero, the official C symbol for the last character. Attackers discovered long ago that they could send arbitrarily long packets of data and write over the programming stack and the memory as long as they never sent that terminating zero. If they were clever with what they wrote, they could assume control and rewrite anything.
Another classic attack on open doors is SQL injection. That Web form may just ask for your ZIP code, then dutifully paste these few characters into an SQL query, but clever hackers started adding extra characters that expanded the scope to be more than a simple search. When the software blithely grabbed all the input, it ended up feeding the SQL directly to the database.
The solution is to test the size and structure of the incoming data and never, ever trust the person on the other end of the Internet.
In general, programmers want to offer more flexibility and less enforcement. Checking every last bit of data is time consuming for the software and exhausting for the programmer. Data transport languages like XML and JSON don't do much to ensure that the data avoids these problems. But checking is what the programmers need to do to secure their code.
Secure programming tip No. 2: Store what you need, and not one bit more
Before you ask for your customer's snail mail address, ask yourself whether you will ever send them a physical letter through the post office. If email is sufficient for correspondence, you might want to rethink storing home or business addresses. That information costs time to process, takes up disk space, and makes an attractive target for information thieves.
Programmers often think like obsessive hoarders, storing away copies of anything that stands the least chance of someday being useful. This instinct may help debug software, but it leaves a trail of data for anyone to find.
Is every column and table in the database absolutely necessary? When in doubt, make the forms shorter and the database tables smaller. Avoid the temptation to be a data pack rat. Simplify everything. The data thieves will hate you, but everyone else will enjoy spending less time filling out forms.
Secure programming tip No. 3: Avoid trusting passwords more than necessary
Everyone knows the problem with passwords, but no one knows a better solution. People forget their passwords, choose ones that are too simple, then reuse them again and again. Yet no other solutions are as flexible or as simple.
Some companies are already using N-factor authentication by tossing several different hurdles in the way. They might send a text message with a random number to your cellphone and ask you to type it in along with your password. It's a nice mechanism unless you forget your cellphone, burn down the batteries, or find yourself inside a building where the text messages can't reach.
It's always possible to add even more security with special hardware that locks up cryptographic keys. They are expensive, though, and even easier to lose than a cellphone.
Other sites keep track of the IP addresses you use to log into their service. If you approach the system from unknown address, they send you a polite email just in case.
None of these choices are perfect, but they are better than just relying on a password. The important step is recognizing the limitations of a string of letters even if some have the right mixture of uppercase and lowercase letters, numbers, and punctuation marks.
Secure programming tip No. 4: Negotiate requirements
Building secure code is not just something that happens in the code editor. When managers draft requirements and discuss them with developers, everyone should seriously consider how each requirement could open the door to problems down the road.
A feature may be cute, but will it force you to keep additional sensitive information and increase the level of security required everywhere? Is a slick feature worth all those extra headaches? The right time to start securing your code against future breaches is when the requirements document is still flexible and the customers aren't salivating over the features you've promised them.
Secure programming tip No. 5: Add delays to your code
Many attacks rely on using a computer to relentlessly try and try again. It may take thousands, millions, trillions of iterations, but the computer doesn't care. Some people screenscrape databases by sending millions of queries pretending to be a user. Others try trillions of potential passwords until just the right one is found.
The trick is to add progressively more delay to confound these bots. In many cases, you don't want your software to be very fast or very efficient. You want it to be fast enough to support the right humans but much too slow for the attacking bots to get much accomplished.
Some log-in programs double the delay with each incorrect password. Some databases limit the number of queries coming from each IP address. Some systems deliberately send an email request to a human to slow you down. It's all in the interest of security because humans won't notice the extra second or two, but a bot will be bored to the point of being ineffective.
Secure programming tip No. 6: Use encryption more often than you think you should
Encryption is often underused because it adds yet another step to the machinery -- and makes debugging that much harder. It can be hard enough to find errors in a system; it's even harder when the data is some inscrutable pile of numbers.
But what's inscrutable to you is also inscrutable to the attackers. Locking up personal data before storing it in the database saves you the trouble of worrying about the database, the underlying operating system, and to some extent the hypervisor that might be running underneath it all.
The right amount of encryption doesn't need to reduce functionality. I examined a number of different examples in my book "Translucent Databases" that can still provide useful services while protecting personal information. Plus, extra protection is itself a feature.
Secure programming tip No. 7: Build walls
The need to add security often can't compete with the demand for ease-of-use. People hate to keep logging into different parts of the system, but it can be dangerous to link all of the systems and powers together into one portal. One weak link compromises everything.
There is no easy way to decide just how easy it should be for a user to navigate the system and accomplish what they want with just one click. The easier you make it for the legitimate user, the easier you make it for an attacker who slips in.
It can make sense to segregate the most sensitive operations into a separate system and require people to log in again when they want to use it. A bank might give a portal the ability to check status and deposit money, but it might require substantially more authentication before money is withdrawn.
Secure programming tip No. 8: Tested libraries -- use them
Encryption is hard to do well, and even the best theory and carefully built code can have loopholes and backdoors. It's usually a mistake to reinvent a well-tested library, but it's even more problematic with encryption. Well-tested libraries are more important in this field than others. Choose better code here and don't invent your own algorithms.
Secure programming tip No. 9: Use internal APIs
Breaking your code into modules and enforcing communication through well-designed APIs is an old lesson everyone learns early in their career. It's even more valuable for security because APIs can make it simpler to audit interactions, find holes, and fix problems. Modules can be scrutinized individually, and the results can be combined.
It often makes sense to create internal submodules as well; the same idea applies inside of modules, too. Parts are easier to analyze than the whole.
Secure programming tip No. 10: Bring in outside auditors to critique your code
Each of us can use an editor. If an enterprise is investing in a well-built installed base, it should also be investing in code audits. These can identify flaws and generate suggestions for improving the code.
In general, more eyeballs looking over the code can spot problems that may occur. Outsiders can also unjam internal political logjams and break ties. They often don't know any more than insiders, but they have the advantage of being unaffiliated with internal factions.
Secure programming tip No. 11: Code analyzers are your friend
Though far from perfect and not as smart as a human, code analyzers can be worthwhile. After all, they're diligent and they don't get tired, thirsty, hungry, or bored.
Code analyzers like the FindBugs tool from the University of Maryland can look for common mistakes we make when we're not thinking. Many of these mistakes have little to do with security, but some can be fatal.
Secure programming tip No. 12: Limit privilege
Developers love to think ahead, and giving someone all the access they might need is a simple way to plan for the future. If they're just starting on the project, why not give them the ability to read all of the databases and commit code? The same goes for systems. If one of your development projects is going to access a database, why not give the code a login that lets them read, write, and update?
While leaving doors open is a simple way to handle future chores and avoid creating annoying road blocks for users and staff, the open doors, as mentioned in tip No. 1, are often the first pathway that's abused. A good principle is to give code and people the smallest amount of privilege needed to get the job done.
If this turns into a management headache generating too many requests for extra privileges, it may make sense to rethink the overall architecture for the data. Are you keeping too much information? If people need more access than you're comfortable giving, you may be storing too much information. Saving less data (tip No. 2) can make it easier to give everyone a simple amount of access.
Secure programming tip No. 13: Model your threat
Do you hold credit card numbers? Then a common thief may be after your information. Do you track people's location with their cellphones? The dangers grow creepier.
Spending time thinking about who wants your data can be a useful precursor. If you can imagine a threat, you can keep the attacker in mind while you design and implement the system. They present an antiuse case to avoid.
It's important to recognize that no list or model will ever be perfect. Just because the threat isn't imagined doesn't mean you don't have to worry about it. It's just a start.
Secure programming tip No. 14: Trust goes both ways
It's easy to be suspicious of those who log into your website, but remember that they should be suspicious of you, too. Are you really the bank that holds their money, or are you a phishing website trying to steal everything they own?
Some sites are investing in proving themselves to the customers. They ask the customer to upload some photo or set of words that the website can use to prove that they're who they say they are. This can make everyone more secure.
Secure programming tip No. 15: Keep apprised of the latest threats
Following the industry press is absolutely essential, and InfoWorld is just one of the publications that covers tragic mistakes. Good articles can show you what others did wrong and give you a chance to think like an unauthorized prowler.
Understanding what happened in the past is a good way to begin planning for the future when a similar attacker may come after you -- a similar attacker who is also reading the same articles and thinking about them in a more malicious way. Once the ideas are out there, you have to take notice or the attackers will get a jump on you.
Secure programming tip No. 16: Deep research can pay off
The daily press is the first draft of how not to step in deep manure. Better lessons come from reading the books and journal articles written after the researchers have had time to think about what went wrong. These often include good rules and methods for avoiding the problem in the future.
Investing some time and money in books is often an incredibly cheap way to get knowledge from some of the most highly paid consultants. A book that costs $200 or $300 may seem outrageously expensive, but not when the consultant also charges $500 an hour and insists on a 20-hour minimum.
Secure programming tip No. 17: Educate yourself
You can enroll in a local university or try one of the new free courses online. These are different ways of learning the information that often hasn't been distilled and put in book form. The professors are usually following the latest publications in academic conferences, and they likely include copious footnotes and pointers. Even if you know much of the information already, auditing a course helps you keep current with the latest discoveries and publications.