Secure Engineering Guidelines
Some best practices for building and trusting software.
Do Not Trust Input
Never trust anything that comes from the user or client. Always validate or sanitize all input before using it in the application. Common abuses of unchecked user input include:
- SQL Injection, where untrusted user input is used in raw SQL queries (use parameterized queries).
- Cross-Site Scripting, where untrusted user input is used in raw HTML, CSS, or JavaScript sent back to the user’s web browser (use a
template engine or sanitize input). - JSON Injection, where untrusted user input is used in raw JSON (use a JSON library to build JSON blobs).
- Shell Injection, where untrusted user input is used in shell commands.
- Code Injection via deserialization, where untrusted user input is used in serialized objects.
Minimize Attack Surface
Every interface you accept user input from is attack surface. If you don’t need to take data from the user, don’t!
Architect and Design for Security
Build security into your system from design to deployment, if possible.
Favor Simplicity
The simplest solution is often the correct solution. Simple solutions are easy to understand and explain to other engineers, easy to test, and easy to verify they are implemented correctly.
Principle of Least Privilege
Always grant permissions as required and never over-permission. Try to assume whatever you’re assigning permissions to could be taken control of by an attacker and attempt to give an attacker the least amount of access possible.
If an attacker found an SQL injection vulnerability, it would be best if that application only had read access to the tables it needed to, rather than read and write access to the whole database.
Separation of Duties
Separation of duties is used to limit the risk of unauthorized or unintentional modification of information and systems. Try to assume that an attacker can gain access to at least one component of the system and attempt to make it impossible for an attacker to steal data or compromise the integrity of the system without access to all components.
Defense in Depth
“In a composite system, there is no critical gate: everything is a gate.” — Felix ‘FX’ Lindner
Every access control is an opportunity for an attacker to exploit a system. Equally, every access control is an opportunity for engineers to make a system more robust and secure against attackers. Whenever possible, build defenses in.
Secure Defaults
Default settings, constants, and permissions should always be set to secure values before they are overridden through a secure process.
Secrets Management
Secrets should be stored securely. Passwords, API tokens, and encryption keys should never be committed to source control or stored on an engineer’s machine. Use a secure repository that meets your requirements.
Fail Securely
Always consider the side effects of a system if it were to crash. If a failure were to occur, what state would the system be left in. Always try to catch exceptions and restore the system to a safe state. Consider the example of the Apple goto fail bug.
A failure should never let a user gain unauthorized access. Never assume a user is logged in before they are verified. Never assume a user has permission before it is verified. Never fail open unless you absolutely need to.
Fix Warnings and Errors
As part of general code hygiene, all compiler and runtime errors should be tracked and resolved.
Compiler warnings are typically warnings for a reason. If they aren’t immediately a vulnerability, they may become one. Application runtime errors can leak attackers extra information about the underlying code through stack traces or behavioral leaks. All application exceptions should be handled properly and exception details should never be shown to the user.
General Do Nots
- Do not trust vendors and third-party services.
- Do not output user input into a client-side JavaScript context.
- Do not use user input in shell commands.
- Do not use user input in Windows paths.
Third-Party Libraries
Use these guidelines when determining if a third-party library is secure:
- Do you trust the library, the author, and their infrastructure? An adtech company might approach the author and ask them to insert a snippet that collects information about your code or your users. A developer could use their library for criminal activity, such as credit card theft or ransomware.
- How easy would it be to own the author or their infrastructure? A popular library could be a target for attackers.
- Does it do anything that you consider dangerous? Examples include remote access or arbitrary code execution.
- Does the plugin send information to a remote server? Is the data it sends sensitive?
These guidelines are drawn from the following resources:
- Sun Certified Security Administrator for Solaris 9 & 10 Study Guide Chapter 1
- OWASP Security by Design Principles
- OWASP Principles
Thanks to the security team at Flatiron Health who helped solidify these guidelines, including Ben Khakshoor, Chris Sandulow, Nicholas Arvanitis, Darren Gruber, and Justin Berman.