Building Secure Software: Ten Tips for Software Developers
Bahaa Noah • May 22, 2023
Secure coding guidelines
Security is crucial in software development, and you don't have to be a security engineer to take responsibility for your software's security. Here are some tips to keep in mind to make your software more secure.
- Don't trust the user: Implement proper validation and user input sanitization to prevent unwanted or harmful outcomes.
- Validate and sanitize: Perform input validation and sanitization on both the front-end and back-end to ensure data integrity and prevent security vulnerabilities.
- Limit privileges: Grant users the minimum level of access necessary to perform their tasks to minimize the risk of accidental or malicious actions.
- Encrypt everything: Utilize SSL/TLS protocols and encryption mechanisms to protect sensitive information from unauthorized access or interception.
- Don't trust yourself: Rely on rigorous testing, including unit tests and external security audits, to identify and address security vulnerabilities in your code.
- Public vs. private data: Classify and protect data based on its sensitivity, applying appropriate security measures to safeguard private information.
- Field verification: Validate and verify user-provided data to ensure accuracy, integrity, and protection against data manipulation or injection attacks.
- Authenticate every interaction: Implement robust authentication mechanisms to verify the identity of users and secure interactions with your software.
- Beware of leaks in the console: Utilize code linters and build tools to minimize the risk of sensitive information leaking through console logs.
- Assume your walls will be breached: Adopt a proactive approach to security by performing premortem exercises, identifying potential breaches, and implementing preventive measures.
I am going to break it down to two main sections:
1. Don't Trust the User
When putting this phrase in the right context, it doesn't mean the user is dumb or doesn't know how to use the software, what it means is that the person using your app shouldn't be able to do anything that leads to unwanted or harmful outcomes.
Let's say you're developing a web application that allows users to upload files. One of the features of your application is a file-sharing functionality where users can share files with each other. However, you want to ensure that users cannot upload or share malicious files that may harm other users or compromise the security of the system.
In this case, you shouldn't trust the user's uploaded file blindly. Even if a user appears to be trustworthy, there's always a risk that they might unknowingly or intentionally upload a malicious file. To mitigate this risk, you should implement proper validation and sanitization mechanisms on the server-side, and that takes us to the next point which is validation and sanitization.
2. Validate and Sanitize
Validating and sanitizing all inputs is crucial for security. Validation should be performed on both the front-end and back-end, while sanitization involves stripping out any errant code and cleaning input from anything that shouldn't be there. Similar to validation, sanitization should be performed on both ends. Additionally, all outputs from the server should be sanitized before they are displayed on the front-end.
Let's imagine you're building an Authentication service that accepts email and password from the user as inputs.
- The email address should be in a valid format (e.g., "email@example.com").
- The password should meet complexity requirements (e.g., a minimum length, including a mix of uppercase and lowercase..etc).
However, relying solely on front-end validation is not sufficient. It's important to perform server-side validation as well to prevent any malicious attempts to bypass the front-end checks. For instance:
- The email address should be checked for uniqueness to avoid duplicate accounts.
- Server-side validation can also include additional checks, such as verifying that the email address belongs to a valid domain or checking against a list of commonly used weak passwords.
Input sanitization is the process of removing or neutralizing potentially harmful characters or code from user inputs. This is crucial to prevent attacks like Cross-site scripting (XSS) or SQL injection. For example:
- inputs should be properly escaped or encoded before storing it in the database or using it in dynamic queries.
- Special characters and tags should be sanitized from text inputs to prevent malicious scripts from being injected and executed.
Output Sanitization: It's not just important to validate and sanitize inputs; you should also ensure that any data output from the server to the front-end is properly sanitized to prevent cross-site scripting attacks. For example:
- User-generated content, such as comments or forum posts, should be sanitized to prevent the execution of embedded scripts when displayed on the website.
- Any dynamic content rendered on the front-end, such as user profiles, should go through output encoding or HTML sanitization to prevent injection of malicious code.
3. Limit privileges
Give people as limited access as possible, this is also one of the security pillars in AWS that they highly recommend when you set permissions with IAM policies for granting users access to AWS resources.
Limiting access is important for mainly two reasons:
- To prevent trusted users from accidentally doing things they shouldn't.
- To prevent malicious users from doing things when they gain access.
4. Encrypt everything
The internet is an information distribution network, always assume someone is listening and encrypt everything.
Implement an end-to-end encryption by using SSL/TLS protocols. SSL certificates are provided for free by services like let's encrypt to protect the confidentiality of user messages and prevent unauthorized access to sensitive information. Even if the communication is intercepted, the intercepted data will be in an encrypted form and cannot be deciphered without the recipient's private key.
And it goes without saying that encrypting sensitive information like passwords with strong one-way hashes (using cryptographic hashing algorithms like bcrypt) ensures that even if a data breach occurs, the actual passwords remain secure and cannot be easily reversed or exploited.
Furthermore, it's essential to enforce encryption and block unencrypted communication whenever possible.
5. Don't trust yourself
"Did I close the door? Is the light still on?" These are questions most of us ask ourselves when leaving the house. It's fascinating how, once doing something becomes routine, our memory of having done it becomes fleeting to the point where we often can't remember at all. Then, on the rare occasion when you actually forget to do something, the only way you'll find out is by going back and checking.
When it comes to development, we can't trust ourselves to make sure all security measures are addressed and that everything is working properly. You can't trust yourself to test your own code! Trust your test process.
To ensure security measures are addressed and that everything is working properly, follow these steps:
- Implement unit tests for security, including sanitization and validation.
- Have both novice and security professionals pen-test your application.
- Have some security scanning tools to scan your code, like SonarQube, Snyk or Guardrails.
6. Public vs. private data
A good rule of thumb is to assume that all data available over the internet will eventually be compromised and accessed by someone who shouldn't have access, either by accident or intentionally.
With this in mind, for every piece of data, consider whether it should be public or private, what access level it requires, and the best way to protect it from eventual compromise.
Always ask the question: should the data be public or private? Based on your answer, apply the appropriate measures to protect it.
If you're working with a lot of private data, you might need to consider encrypting it or eliminating it altogether, and only collecting it temporarily for each interaction, such as credit card information.
7. Field verification
Validate and verify user-provided data to ensure its accuracy, integrity, and adherence to expected formats.
Use input validation techniques on the client side and server side to prevent data manipulation and injection attacks.
You usually have control over client-side verification. When relying on third parties, you also rely on their server-side validation and must build around their error reporting and messaging.
8. Authenticate every interaction
Authenticating every interaction is a best practice that dramatically increases security.
There are different levels of authentication, from base level with SSL certificates to full user accounts with authorization levels. The level of authentication necessary depends on the project, but it's important to collect as little personal information as possible while still verifying the user's identity. Two-factor authentication is a good best practice to introduce.
9. Beware of leaks in the console
Logging in the console is useful for debugging and seeing what's going on with your application. However, the console can also become a major vector for leaking information if you're not careful.
To Avoid data leaks in Console, Build tools are your friend here:
- Use a code linter that flags console logs as a warning.
- Use build tools to strip out console logs before deployment.
- Do production tests to make sure no data is leaking into the console.
10. Assume your walls will be breached
Performing a premortem exercise during software development can help identify possible security breaches and their prevention. As per Wikipedia a premortem "is a managerial strategy in which a project team imagines that a project or organization has failed, and then works backward to determine what potentially could lead to the failure of the project or organization.".
To do this, sit down with your team and say, "Okay, it's four months from now. We've launched our project and a major newspaper's on the phone, asking for comments about the huge security breach we just experienced. What went wrong?"
- Imagine a security breach has already happened and brainstorm all possible scenarios.
- Then address each one by identifying how it could happen and how to prevent it.
- Document how each imagined breach can be prevented and create a backlog from this list.
This exercise can be done at various stages of development to keep everyone focused on security and proactively deal with issues before launch.
Software security is a crucial aspect of development that should be prioritized by all developers, regardless of their role or expertise. By following these ten security tips, you can significantly enhance the security of your software.
Additionally, it is important to familiarize yourself with the OWASP Top 10.
Lastly, I highly recommend checking out the web security learning path provided by PortSwigger, they are a market leader in security and the course curriculum is well-made. I have completed the course and found it a valuable learning experience. It's completely free and comes with laps to test on as well.
Thanks for reading, I hope this was helpful. Please feel free to reach out if you need more help or have any suggestions.