With global cyberattacks making headline after headline, businesses are realizing that security is critical for any software project. Clients often come to us thinking that security can be added with a product or service, or can be worked into an existing product/codebase. But there's no way to take an existing product and simply "make it secure": security must be built into a product from the very beginning.
Security vulnerabilities are ultimately just bugs. No matter how many checklists you follow or how many firewalls you install, vulnerabilities will still arise from an application developer making a mistake – it could be as small as messing up the condition in an if statement. Security vulnerabilities are a deviation from expected behaviour, because an engineer failed to consider either the result of every possible input or all the consequences of the application's desired behaviour. (For a list of badly thought out desired behaviour, see https://www.troyhunt.com/reckon-youve-seen-some-stupid-security-things-here-hold-my-beer).
Here at Fusionbox, security is something we think about in every step of designing and implementing software. Our engineers hold themselves to very strict security protocols, beyond programming, to prevent social engineering attacks.
That said, several steps that can be taken to minimize security vulnerabilities in a web application. Data security comprises three facets: availability, integrity, and confidentiality. Here’s how we address these three facets:
Availability, as the name suggests, means that the application and data be available to the user when needed.
- Use a reputable VPS provider that takes care of the hardware and network aspects of availability for us
- Conduct regular scheduled backups to ensure the data is not lost in case of a disaster
- Mitigate denial of service attacks by immediately upgrading any packages where vulnerabilities that could lead to DOS attacks are discovered
- Have automated deployment scripts, so mistakes that lead to downtime are minimized, and if any problems do occur, fixes can be deployed quickly
Integrity means that the data is accurate, complete and trustworthy, and cannot be modified in an unauthorized or undetected way.
- Access control on the server. I will go into more detail about this further in the post.
- Validate all inputs on the server, because even if there is client side validation, the client cannot be trusted
- Use HTTPS to prevent unauthorized modification of information during transport with man-in-the-middle attacks
Confidentiality means that information is only disclosed to authorized individuals.
- Use HTTPS to encrypt traffic between the client and server, keeping information private during transport
- Access control on the server. Again, this will be elaborated more later.
- Ensure that strong passwords are used everywhere, and where it’s important, use two factor authentication
- To the server:
- Disable password login to servers, and only allow login with private keys, to prevent brute forcing
- To the app:
- Strong passwords for admins, 2 factor authentication
- Testing of authorization code
- Write code that is secure by default. For example, using whitelists instead of blacklists and denying permissions by default.
Diving deeper, there are several best practices we follow for protection from common vulnerabilities:
- Use Django, which has good defaults for protection against common vulnerabilities like CSRF, XSS and SQL injection
- Use Content Security Policy and HTTP Strict Transport Security HTTP headers on sites
- Keep server software and dependencies up to date and install patches as soon as vulnerabilities are announced
- Only run services that are absolutely necessary, only have packages installed that are needed, and change all default passwords for services
- Run each service with its own user account with the minimum necessary permissions, so that the impact of a vulnerability in one is contained
- Configure firewall to prevent applications from listening on unwanted ports
- Use automation tools to configure servers and deploy code, to ensure no mistakes are made in configuring the servers that leave it unsecured. This step removes the human error that could negate the previous security practices.
Our security policy:
All these practices protect the applications, but that does not matter if an attacker can gain access via social engineering. To prevent this, we adhere to a strict security policy that includes:
- For all passwords, at least 50 bits of entropy, and a different password for every service
- Two factor authentication on all services that support it, in this preferred order: 1. U2F keys 2. TOPT 3. SMS, only as a last resort (see https://krebsonsecurity.com/2016/09/the-limits-of-sms-for-2-factor-authentication/). SMS must be disabled if either of the previous options are supported.
- Full disk encryption on all workstations
- Locking all devices when leaving them unattended and auto locking on short timeouts
- Escorting any guests in the office and not allowing them to wander alone
- Requiring in-person verification of any requests for access to anything, and not simply granting access when someone requests it via email
Looking to build secure, well-architected software or do a Python security code audit? With over 15 years of experience, Fusionbox can help. Learn more.