The value that applications and databases present to attackers has seen a move from network based attacks towards web application and database attacks. We are starting to see more and more high profile web based attacks. The Ashley Maddison hack is one that is fresh in my mind. At the end of the day, our adversaries are after the data that our organisations hold for financial gain. As with anything else, our adversaries are using the easiest path to get to this data and in most cases this tends to be web applications and heir back-end databases. Why – because web applications are always available to anyone, anywhere and we have seen way too many examples of insecure code development and basic coding errors that lead to compromised web applications and subsequently databases.
Once applications are hacked, the consequences can be enormous. For the end client this could mean loss of reputation, loss of customer confidence, loss of market share, financial penalties to name a few. For the developer, this could mean ‘game over’. Once your application is outed as insecure, it will be very difficult to convince your customers to deal with you.
Having said the above, what simple steps can be taken to increase the security of developed applications? I will now highlight twenty that can be useful.
Test inputs rigorously: Attackers will try and gain access to your underlying servers and you do not want your code to be the door that allows that. Ensure that you test the size and structure of the incoming data into your applications and never trust the person on the other side of the internet. Sanitise any input into the application. Limit the character sets and other attributes of the data that can be entered into data fields to what makes sense for the dataset in question. Where possible, present a preselected list or stored procedures that users can chose or run.
Store only what you need: Only request and keep what you need. Having lots of unnecessary data is like offering a pot of honey to a bear! 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 store data that you might need one day and keep this to a bare minimum.
Don’t trust password more than you need to: Passwords inherently are weak. Unfortunately people keep using them as the alternatives seem hard to implement. Some possible options include using N-factor authentication, keeping track of the IP addresses users log in from, etc. None of these choices are perfect, but they are better than just relying on a password. The important step is recognising the limitations of a string of characters regardless of the length and mixture. And do not hardcode passwords anywhere within your application!
Understand and negotiate requirements: Take care not to implement requirements that may lead to security weakness further down the track. Ask yourself – is that feature going to weaken the security of my application? Do I really need to store that piece of data? The right time to start securing your code against future breaches is when the requirements document is still flexible and changes to scope can still be made.
Add delays to your code: This is effective against brute force attacks where bots try trillions of iterations until the attack is successful. A good example of this is an automated password guessing attempt by a bot. The trick is to add progressively more delay to confound these bots. You want your software to be fast enough to support the right humans but too slow for attacking bots to accomplish much. Some login programs double the delay with each incorrect password. Some databases limit the number of queries from each IP address. This delay wouldn’t necessarily be noticed by a human, but would foil most automated bots.
Encrypt your data: Encrypt any data you store and if feasible, that you transfer. Attackers struggle to use scrambled data. Locking up personal data before storing it in the database saves you from worrying about the database, the underlying OS, and to some extent the hypervisor that might be running underneath. This is particularly important for passwords. At a minimum ensure strong encryption and salting. You don’t want your passwords hacked and cracked easily!
Segregate sensitive parts of the system: Ease of use may dictate single sign on onto the whole system. The issue with this is that if you can sign onto the whole system, so can the attacker. Consideration should be given to segregating sensitive parts of the system from non-sensitive parts with the former requiring re-authentication. This is not too dissimilar to online banking transactions whereby transactions deemed sensitive require an extra factor of authentication.
Use tested libraries: Use libraries that have been developed already and well tested. There is no benefit in reinventing the wheel and in doing so you run this risk of introducing loopholes and backdoors into the code.
Use internal APIs: Decouple the system and break it into modules. Enforce communication between these modules through well designed APIs. The advantage from a security perspective here is that APIs can make it simpler to audit interactions, find holes, and fix problems. Modules can be scrutinised individually, and the results can be combined. It often makes sense to create internal sub-modules as well; the same idea applies inside modules, too. Parts are easier to analyse than the whole.
Use an outside auditor to critique your code: Being a developer usually means that you cannot see ‘the wood from the trees’. Get external help to look at your code from an independent perspective so that issues can be identified and suggestions to improve the code can be made.
Use code analysers: Automated code analysers can be helpful as well. Code analysers like the FindBugs tool from the University of Maryland can look for common mistakes we make. Many of these mistakes have little to do with security, but some can be fatal!
Need to know privileges only: Ensure that users are only given the privileges they require to perform their job functions and no more. If users can get excessive privileges, so can attackers. If this turns into a management headache generating too many requests for extra privileges, it may make sense to rethink the architecture for the data. Excessive privileges may also mean that you are storing too much data. If people need more access than you're comfortable giving, you may be storing too much data too.
Understand your threat landscape: Know the data you are keeping and its value to attackers. The more valuable the data, the more security you need to build in to protect it.
Prove your trust to your users too: With the increase in phishing attacks, it is becoming more and more important to prove your identity as well. 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.
Keep up-to-date with the latest threats: Understand what the latest threats are, how they are working and secure your application against them. The threat landscape is forever changing and we must keep up with this in order to keep our applications secure. News articles are a good start, but investing in reading security research material from books and journal articles written after the researchers have had time to think about what went wrong can be worth their weight in gold.
Use a Web Application Firewall (WAF) and a Database Access Management (DAM) solution: These devices are specifically designed to protect against web based (application / Layer 7) attacks. They look at more than just the connection state, and inspect the payload and what it is designed to do before deciding whether it is safe or not. Another great benefit of these devices is that they can be used for virtual patching. A lot of web applications cannot be patched since this can cause functionality issues. However, it a terrible idea to leave web applications unpatched. WAFs are an effective way to virtually patch web applications. WAFs can carry signatures based on released patches and use this to stop attack traffic related to these patches.
DAMs operate in a similar fashion. They are designed to protect against database specific attacks (think SQL Injections) and can effectively do virtual patching as outlined above.
Use standards to guide you: There are multiple standards available to guide you in terms of secure code development. Requirement Six within the PCI DSS standards for example is a good place to start that provides very simple, yet effective guidance towards secure application development.
Perform regular vulnerability scans and web application penetration testing: Use automated tools to scan for web application and database vulnerabilities and patch these as practical. Run regular web penetration testing to test the resilience of your web application against simulated attacks.
Use unique accounts and remove test accounts and data: Do not use generic accounts within web applications to ensure user accountability. Remove all test accounts and data as these tend to have weak controls and can allow easy unauthorised access. Ensure all vendor default accounts are disabled or at a minimum have their passwords changed. All vendor maintenance accounts should be locked until required and locked again after use.
Education: And finally, if you have not had formal secure code development training, think about investing to one. There are multiple courses and certifications (e.g. CSSLP from ISC2) that are available that can help.
Within this short article I have tried to highlight some simple pathways to secure code development and protecting your web applications and databases. When followed, these steps can allow you to develop your code securely and avoid some of the common pitfalls and issues associated with insecure coding and web development.