Part II: Secure Coding Made Easy: 5 More Tips to Integrate Security Into Development
For part two, we’re diving into five additional tips and best practices, from protecting data to leveraging existing frameworks securely.
Join the DZone community and get the full member experience.Join For Free
You’ve heard it before: it’s time to get serious about security. Cyber threats aren’t slowing down, which means security must become a critical part of your job as a developer. But it’s not always easy to fix your code during or after release to production, especially when you have to stop and search for knowledge resources. That’s where secure coding best practices and fine-tuned training meet to set you up for success.
In part one of this two-part guide, we broke down best practices like parameterizing your queries to avoid SQL injection and encoding your data to address the three main classes of Cross-Site Scripting (XSS). For part two, we’re diving into five additional tips and best practices, from protecting data to leveraging existing frameworks securely.
Tip 6: Implement Access Controls
You can dramatically improve protection and resiliency in your applications by building authorization or access controls into your applications in the initial stages of application development. Note that authorization is not the same as authentication. According to OWASP, authorization is the “process where requests to access a particular feature or resource should be granted or denied.” When appropriate, authorization should include a multi-tenancy and horizontal (data specific) access control.
- Use a security-centric design, where access is verified first. Consider using a filter or other automated mechanism to ensure that all requests go through an access control check.
- Consider denying all access for features that haven’t been configured for access control.
- Code to the principle of least privilege. Allocate the minimum privilege and time span required to perform an action for each user or system component.
- Separate access control policy and application code, whenever possible.
- Consider checking if the user has access to a feature in code, as opposed to checking the user's role.
- Adopt a framework that supports server-side trusted data for driving access control. Key elements of the framework include user identity and log-in state, user entitlements, overall access control policy, the feature and data requested, along with time and geolocation.
Consider checking if the user has access to a feature in code, as opposed to checking what role the user is in code. Below is an example of hard-coding role checks.
Tip 7: Protect Data
Organizations have a duty to protect sensitive data within applications. To that end, you must encrypt critical data while it’s at rest and in transit. This includes financial transactions, web data, browser data, and information residing in mobile apps. Guidelines like the EU General Data Protection Regulation make data protection a serious compliance issue.
- Don’t be tempted to implement your own homegrown libraries. Most modern languages have implemented crypto libraries and modules, but in the event, your language did not, consult your security team to find a security-focused, peer-reviewed, and well-maintained library.
- Don’t neglect the more difficult aspects of applied crypto, such as key management, overall cryptographic architecture design, tiering, and trust issues in complex software. Existing crypto hardware, such as Hardware Security Module (HSM) solutions, can make your job easier.
- Avoid using an inadequate key or storing the key along with the encrypted data.
- Don’t make confidential or sensitive data accessible in memory or allow it to be written into temporary storage locations or log files that an attacker can view.
- Use transport layer security (TLS) to encrypt data in transit.
The security of basic cryptographic elements largely depends on the underlying random number generator (RNG). An RNG that is suitable for cryptographic usage is called a cryptographically secure pseudo-random number generator (CSPRNG). Don’t use Math.random. It generates random values deterministically, and its output is considered vastly insecure.
Tip 8: Implement Logging and Intrusion Detection
Logging should be used for more than just debugging and troubleshooting. Logging and tracking security events and metrics help to enable what’s known as attack-driven defense, which considers the scenarios for real-world attacks against your system. For example, if a server-side validation catches a change to a non-editable field, throw an alert or take some other action to protect your system. Focus on four key areas: application monitoring, business analytics and insight; activity auditing and compliance monitoring; and system intrusion detection and forensics.
- Use an extensible logging framework like SLF4J with Logback, or Apache Log4j2, to ensure that all log entries are consistent.
- Keep various audit and transaction logs separate for both security and auditing purposes.
- Always log the timestamp and identifying information, like source IP and user ID.
- Don’t log opt-out data, session IDs, or a hash value of passwords, or sensitive or private data, including credit card or Social Security numbers.
- Perform encoding on untrusted data before logging it to protect from log injection and log forging.
- Log at an optimal level. Too much or too little logging can heighten risk.
In mobile applications, developers use logging functionality for debugging, which may lead to sensitive information leakage. These console logs are not only accessible using the Xcode IDE (in iOS platform) or Logcat (in Android platform), but by any third-party application installed on the same device. For this reason, disable logging functionality in the production release.
Tip 9: Leverage Security Frameworks and Libraries
You can waste a lot of time — and unintentionally create security flaws — by developing security controls from scratch for every web application you’re working on. To avoid that, take advantage of established security frameworks and, when necessary, respected third-party libraries that provide tested and proven security controls.
- Use existing secure framework features rather than using new tools, such as third-party libraries.
- Because some frameworks have security flaws, build in additional controls or security protections as needed.
- Use web application security frameworks, including Spring Security, Apache Shiro, Django Security, and Flask security.
- Regularly check for security flaws and keep frameworks and libraries up to date.
The crucial thing to keep in mind about vulnerable open source libraries is that it’s not just important to know when a library contains a flaw, but whether that library is used in such a way that the flaw is easily exploitable. We know that, upon initial scan, 70.5 percent of applications have flaws in an open source library. Additionally, data compiled from customer use of Veracode’s Software Composition Analysis solution shows that at least nine times out of 10, developers aren't necessarily using a vulnerable library in a vulnerable way.
By understanding not just the status of the library but whether or not a vulnerable method is being called, organizations can pinpoint their risk and prioritize fixes based on the riskiest uses of libraries.
Tip 10: Monitor Error and Exception Handling
Error and exception handling isn’t exciting, but like input validation, it is a crucial element of defensive coding. Mistakes in error and exception handling can cause leakage of information to attackers, who can use it to better understand your platform or design. Even small mistakes in error handling have been found to cause catastrophic failures.
- Conduct careful code reviews and use negative testing, including exploratory testing and pen testing, fuzzing, and fault injection, to identify problems in error handling.
- Manage exceptions in a centralized manner to avoid duplicated try/catch blocks in the code. In addition, verify that all unexpected behaviors are correctly handled inside the application.
- Confirm that error messages sent to users aren't susceptible to critical data leaks, and that exceptions are logged in a way that delivers enough information for QA, forensics, or incident response teams to understand the problem.
Returning a stack trace or other internal error details can tell an attacker too much about your environment. Returning different errors in different situations (for example, "invalid user" vs. "invalid password" on authentication errors) can also help attackers find their way in.
Give Your Secure Coding Knowledge a Boost
There isn’t a one-size-fits-all solution to writing more secure code. It takes patience, practice, and the know-how that comes from relevant training in the languages you use most and consistent best practices. Veracode Security Labs Community Edition – free to developers who want to enhance their secure coding skills – relies on hands-on training for greater impact. Practice exploiting real applications in contained environments to learn how threat actors operate and then patch them to learn how to keep your code secure.
Stay on top of the latest best practices in application security (AppSec) to improve every day. If you missed the first part of this series with more secure coding tips and tricks, read it here.
Opinions expressed by DZone contributors are their own.