… added functionality that changes sensitive properties or objects in the system |
- Protect with authentication - You must make sure that all new functionality is protected with authentication. Validate that an individual, entity or server is who it claims to be by using strong authentication mechanisms like SAML, OAuth, etc.
- Protect with authorization - Authorization enforces what permissions/authority a person has on an entity or operation.
- You must make sure that you exercise least privilege access control policies on all new functionality. You may design for coarse-grained authorization, but keep the design flexible for fine-grained authorization.
- Make sure secrets are not in cleartext - A secret is only as good as how it is protected. When using passwords or cryptographic keys, it is important to keep them protected at all times. Try to minimize the amount of time they are available in memory by scrubbing variables right after use. Do not use hardcoded secrets under any circumstance.
- Exercise least privilege - When deciding the level of privilege needed by a process or service, keep in mind that it should be only as much as that process or service needs. For example, if you are only querying a database, the credentials you are using should not be owned by a user that can write to the database. A process that does not need elevated (root or Administrator) privileges should not be running as root or Administrator.
- Account for all vectors for client bypass - Any logic in the client side of the application is an easy target for attack. Ensure that client-side controls cannot be bypassed by skipping steps of the application, submitting incorrect values, etc.
|
… created a new process or actor |
- Exercise least privilege - When deciding the level of privilege needed by a process or service, keep in mind that it should be only as much as that process or service needs. For example, if you are only querying a database, the credentials you are using should not be owned by a user that can write to the database. A process that does not need elevated (root or Administrator) privileges should not be running as root or Administrator.
- Make sure credentials are securely stored Store user credentials as a salted and hashed value in a database. Ensure that a strong hash algorithm and sufficiently random salt is used.
- Exercise appropriate hardening - Harden your system or component (commercial, open source, or inherited from another team), by regularly patching, installing updates, minimizing attack surface, and practicing the principle of least privilege.Minimize the attack surface by reducing the number of entry points into the system. Turn off features, services, and access that is not strictly necessary. Practice the principle of least privilege by providing the lowest amount of access and permissions necessary for a role's function. Audit each of these controls to ensure compliance.
|
… used cryptography |
- Make sure you used an approved toolkit - When including outside content (libraries, toolkits, widgets, etc.) it is important to verify that these have been vetted for security issues.
- Make sure you don't write your own crypto - Writing your own crypto can introduce new flaws and a custom algorithm may not have the necessary strength to protect against attack. Ensure that you are using an industry standard cryptographic algorithm in the correct way, for the correct purpose. See OWASP Cryptographic Storage checklist for further details.
- Make sure you have the right algorithm and key size - Ensure that you are using the right algorithm and key size. Use an up-to-date industry standard cryptographic algorithm and key size in the correct way, for the correct purpose. See OWASP Cryptographic Storage checklist for further details.
- Make sure any secrets are correctly stored - A secret is only as good as how it is protected. When using passwords or cryptographic keys it is important to keep them protected at all times. Try to minimize the amount of time they are available in memory by scrubbing variables right after use. Do not use hardcoded secrets under any circumstance. Follow industry best practices for key and secret management.
- Verify there are no hard-wired keys or secrets that cannot be user-defined - Do not hard-code any keys. Keep keys out of code, repos, team and personal notes, and other plaintext storage. Ensure that keys are properly stored in a password manager or as a salted and hashed value in a database.
|
… added an embedded component |
- Exercise appropriate hardening - Every embedded component must be hardened. As part of your hardening effort, you must:
- Minimize the attack surface: Reduce the number of entry points into the system. Turn off features, services and access that is not specifically necessary.
- When choosing a third-party component (commercial, open source, or inherited from another team) become aware of its security requirements, configuration and implications. Contact your security team if you need help hardening a component.
- Consider component threat model - When you use a third-party component, you also inherit the risks/vulnerabilities associated with it to your product, making it necessary to perform a threat model on third-party components you may be using. Identify all the data flows to and from the third-party component in your application and use The Autodesk Threat Modeling Handbook to generate threats.
- Some examples of things to look for when threat modeling a third-party component:
- make sure that the third-party component is not given more privileges than needed in the application.
- make sure that you don't have unnecessary features (like debugging services) enabled in the third-party component
- make sure you followed any existing security and hardening guidance for the component
- make sure you chose restrictive defaults for the component's configuration
- document the role of the component in the security of your complete system
Once the threats are identified for the third-party component, make sure that they are addressed accordingly based on the risk/severity of those threats. Don't ship your product if it has unaddressed critical or high vulnerabilities in your third-party components. - Add to component inventory - Add the new embedded component to the inventory to monitor it for updates and patches. This inventory must be kept up-to-date as a living document that may be accessed quickly and easily during a security event.
|
… received uncontrolled input from an untrusted source |
- Verify and limit size of input - Verify the size of input (bounds checking), as failure to do so may cause memory issues such as buffer overflows, injection attacks, etc. Failure to verify and limit input size results in data being written past allocated space and overwriting contents of the stack/heap. Implement input validation close to use (not just on the GUI!) to prevent malformed/unexpected input.
- Assume all input is malicious and protect accordingly Treat all input as malicious. At a minimum, validate input and sanitize output before performing actions with it. This improves the overall security posture of your application. Use a Known-Good Approach as opposed to a Known-Bad approach when validating input. Alwaysperform input validation server-side, even if input is validated client-side, because client-side input validation can be easily bypassed.
- Consider encoding the input before it's output - In cases where user input is appended to the response and is displayed on the web page, context-sensitive encoding of the output assists in the prevention of cross-site scripting (XSS). The type and the context in which the encoding is done is just as important as having encoding, as it is possible for XSS to manifest despite encoding, if it is done incorrectly. Read more about context-sensitive encoding in this brilliant OWASP Article.
- Consider saving input in encoded form – for example, url-encoded non-alphanumeric characters In cases where user input is appended to the response and is displayed on the web page, context-sensitive encoding of the output assists in the prevention of cross-site scripting (XSS). The type and the context in which the encoding is done is just as important as having encoding, as it is possible for XSS to manifest despite encoding, if it is done incorrectly. Read more about context-sensitive encoding in this brilliant OWASP Article.
- Consider where and how input will be used down the processing chain - If potentially malicious input originating or passing through your application is being sent to downstream applications, and if the downstream applications implicitly trust data received from your application, this could lead to their compromise. To prevent this, make sure that you treat all input as malicious. Validate the input accordingly and encode it before data is output to downstream applications.
- Ensure that input is not used "as-is" when it comes from an untrusted source - Validate input before performing actions with it. This improves the overall security posture of your application. Use a Known-Good Approach as opposed to a Known-Bad approach when validating input. Always perform input validation on the server side even if you are doing it on the client side because client side input validation can be easily bypassed.
- Verify any interpreters using the data know they’ll be using tainted data Some languages, like Perl and Ruby, are able to do something called "taint checking". If the contents of a variable can be modified by an external actor, it is marked as "tainted" and will not participate in security-sensitive operations without an error. This functionality is also present in some SQL interpreters, and if you happen to be developing your own parser/interpreter, it is advised that you also implement this functionality.
- Notify QA with your parsing specification to create fuzz tests - Fuzz tests throw random data at various sizes - over, under and just right - in order to test the ways that parsers and other functions accepting user input behave under edge conditions. If you created a function that accepts and "understands" user input, make sure to communicate with your QA team so they can develop the corresponding tests necessary to validate your parsing.
|
… added Web (or Web-like, REST) functionality |
- Protect with authorization - Authorization enforces what permissions/authority a person has on an entity or operation.
- You must make sure that you exercise least privilege access control policies on every new functionality. You may design for coarse-grained authorization but keep the design flexible for fine-grained authorization.
- Protect with authentication - You must make sure that every new functionality is protected with authentication. Validate that an individual, entity or server is who it claims to be by using strong authentication mechanisms like SAML, OAuth, etc.
- Validate use of tokens, headers and cookies, as uncontrolled input from an untrusted source - Never trust input that comes from the request headers as this data can be manipulated by an attacker on the client-side. Treat this data as you would treat any other potentially malicious data and apply the steps as described under "received uncontrolled input from an untrusted source" section.
- Make correct use of TLS, checking certificates appropriately - Don't use outdated versions of TLS. Don't use broken or obsolete ciphers for your TLS connection. Make sure you are using encryption keys of sufficient size. Make sure that the certificate itself is valid and that the common name on the certificate matches the domain that is presenting the certificate. Make sure the certificate presented is not part of the certificate revocation list (CRL). This is not an exhaustive list of things to look for in TLS and certificates. Read this brief article to get more information on how to get TLS right.
- Make use of POST instead of GET to protect arguments to calls from exposure - Using POST to send sensitive data in the body of the request is safer than sending the data as arguments in the URL of a GET request. Even if you are using TLS, the URL itself will not be encrypted and might get stored in logs, browser etc. thus revealing the sensitive info.
- Ensure the session can’t be fixated - A "fixated" session is one that is manipulated in a way that manipulates an identifier in order to escape the valid scope of a user and enter another. For example, if a given URL accepts any session ids, taken from query strings with no security validation, then an attacker can send an email to a user with that URL and append their own crafted session_id: http://badurl/?session_id=foo. If the target is fooled into clicking into the URL and entering their (valid and pre-existing) credentials, the attacker will be able to use the preset session id "{}foo" to hijack the user's session. For that reason, provide defense-in-depth: use TLS to protect the whole session from interception, change the session id after initial login, provide different IDs for each request, invalidate past sessions after logout, avoid exposing the session ID on the URL and only accept session IDs generated by the server.
- Ensure secure storage and accessibility of secrets - A secret is only as good as how it is protected. When using passwords or cryptographic keys it is important to keep them protected at all times. Try to minimize the amount of time they are available in memory by scrubbing variables right after use. Do not use hardcoded secrets under any circumstance. Follow industry best practices for key and secret management.
- Ensure high quality randomness of identifiers - Use a sufficiently random value for all identifiers to ensure that they are not easily predicted by an attacker. Use a cryptographically secure pseudo-random number generator to produce a value with at least 256 bits of entropy for the identifier.
|
… transmitted data over the network |
- Ensure data cannot be sniffed in transit - To protect data in transit, you must encrypt sensitive data prior to moving and/or use encrypted connections like HTTPS/SSL/TLS to protect the data from being sniffed in transit.
- Ensure data cannot be tampered in transit - Depending on your use case you may use hashing, MACs/HMACs or digital signatures to make sure that integrity of data is maintained. Read this article for more info.
- Ensure data cannot be replayed - To make sure that data cannot be replayed, you may use a timestamp or a nonce to compute a MAC/HMAC of the data before transmitting it.
- Ensure the session cannot be hijacked - Make sure that the session id has sufficient length and is cryptographically random. Make sure that the session id itself is transmitted over TLS. Wherever possible, set the "Secure" and "HTTPOnly" flags on the session cookie. Also make sure that you are not vulnerable to session fixation. Read this OWASP article for more information.
- Make sure you are not depending on the client to protect, authenticate or authorize - A client runs in an environment that is fully under control of the user, and so also in control of the attacker. If your security controls rely on the client, they can be bypassed and expose sensitive data and functionality. For example, it is not enough to verify a credential or security property on the browser using Javascript, given that an attacker would be able to modify it by a number of different mechanisms, for example by using a proxy. The client should not be responsible for security decisions, but pass the relevant data to the server and use that as their security decision. A proper solution offers client-side validation for feedback purposes but server-side application of security controls.
|
… created a computationally- or storage-bound process |
- Make sure you will not provoke a denial of service to other users/actors if the process goes haywire for any reason Implement the following best practices to avoid a Denial of Service situation:
- A fault-tolerant design that enables a system/application to continue its intended operation in case of failure
- Prevent single point of failure
- Avoid/limit CPU consuming operations
- Keep queues short
- Correctly manage memory, buffers and input
- Implement threading, concurrency and asynchronicity so as to avoid operations that block while waiting for completion of large tasks to proceed
- Implement rate-limiting (controlling traffic to and from a server or component)
|
… created an install or patching capability |
- Make sure your installer is signed - An installer by definition contains binaries to be installed in the target host and scripts that are responsible for that installation: creating directories and files with their permissions, making changes to the registry, etc. Many times these installers run with elevated privileges. Therefore, extra care should be taken when validating to the user that the installer they are about to execute is indeed the one that contains only trusted software.
- Make sure your keys can be rotated - Encryption keys must be rotated periodically, so that in case a key is compromised only a small amount of data is leaked. Support the ability to perform key rotation:
- Periodically, due to compliance requirements like SOC2 or PCI DSS, keys must be rotated once per year.
- Based on an event, when access provided by a key needs to be revoked.
|
… created a command interpreter (CLI) or execute a system command as part of a process |
- Assume all input is malicious - Treat all input as malicious. At a minimum, validate input and sanitize output before performing actions with it. This improves the overall security posture of your application. Use a Known-Good Approach as opposed to a Known-Bad approach when validating input. Always perform input validation on the server side even if you are doing it on the client side because client side input can be easily bypassed.
- Make sure you cannot inject extraneous commands as arguments - When building queries and commands that will be eval()'uated or exec()'uted by any kind of interpreter, parser, etc. you MUST make sure that you are applying the correct validation, escaping and quoting to the input in order to avoid injection issues. On the interpreter side, make sure you are using the safest version of the available calls, and that (if such exists) you are letting the interpreter know that the incoming data is tainted.
- Make sure you are not providing an elevation of privilege vector to an attacker (least privilege) - When deciding the amount of privilege needed by a process or service, keep in mind that it should be only as much as that process or service needs. For example, if you are only querying a database, the credentials you are using should not be owned by a user that can write to the database. A process that does not need elevated (root or Administrator) privileges should not be running as root or Administrator.
- Make sure you are limiting the reach of the command to those operations and areas of the filesystem you intend to (input validation & least privilege) - If for example you are accepting an input to a file-related operation, make sure you are verifying, close to execution (not on the GUI!) that the full path you are trying to access is, indeed, in the area where you intend the user to be. Make sure strings that modify the scope of the path, like ".." and leading "/" are accounted for. Consider links when accessing a file or directory. Always use the canonical format of a path (and not relative paths) to perform a command.
- Make sure the language mechanism you are using to execute commands does not have unsafe side-effects - A popular example here is the yaml.load() function in the PyYAML library. It allows an attacker to supply Python code inside an YAML file, which then gets executed. Even though it is the right function for the needed use, one should instead use yaml.safe_load(). This difference is noted in the documentation, but many do not pay attention to it. This is why you need to be aware of side-effects of any function that reads, parses and executes code inside your code. Examples are exec(), eval(), any kind of load(), pickle(), serialization and deserialization functions, etc. For an in-depth analysis of this popular issue, but in a Ruby environment this time, see the article here.
- Prefer using a well-established command execution library instead of creating a new one - Chances are that if you try to roll your own command execution library, you may end up forgetting some specific and obscure way of character quoting, black- and white- listing, or another way to manipulate input to bypass filters. Give preference to an established, tried and tested library that takes that responsibility off your hands. At the same time, of course, make sure to choose a good library and keep an eye open for any caveats, updates and bug fixes for it.
|
… added a capability that can destroy, alter or invalidate customer data and/or system resources |
- Consider adding 2 factor authentication as a barrier before executing the procedure - Two-factor authentication is an out-of-band method of providing an additional layer of protection against an attacker performing unauthorized actions. Two-factor authentication must be out-of-band and a different method of authentication than the primary authentication method (something you know, are, or have). For example, if you log in with a password (something you know) using a browser, a method of two-factor authentication could be using a hard token (something you have that is out-of-band, as in, not online or on your computer) to get a random value. LastPass is an example of a popular application that requires two-factor authentication.
- Make sure you cannot inject extraneous commands as arguments When building queries and commands that will be eval()'uated or exec()'uted by any kind of interpreter, parser, etc. you MUST make sure that you are applying the correct validation, escaping and quoting to the input in order to avoid injection issues. On the interpreter side, make sure you are using the safest version of the available calls, and that (if such exists) you are letting the interpreter know that the incoming data is tainted.
- Verify the operation is being logged with a timestamp and the identity of the requester In order to track malicious actions of an attacker, it is important to log both the identity of the person making changes and the time of the change. In this way, if an attacker takes over an account, malicious actions can be pinpointed by verifying activity with the owner of the account.
|
… added a log entry |
- Make sure you are not logging sensitive information (passwords, IPs, cookies, etc.) - It is tempting to log as much information as possible in the case of something going wrong. But, in many cases this approach may fall short of compliance targets like GDPR, and in some cases, this may expose sensitive information like cleartext forms of passwords, sensitive cookie contents, etc. Make sure that you are not collecting more data from your users than what is strictly necessary. Ensure that your logging is not saving more than what is needed or for longer than necessary, especially when dealing with personal and/or sensitive data.
- Strive to provide non-repudiation capabilities to the logged messages - A security event is not a matter of if, but rather when. In order to be ready for that event, we want to be able to provide timely and detailed information to anyone investigating issues. In order to be able to do that, we need to make them sure that whatever message they see in the logs is not only correct but only appears in the log as a result of that operation which it is reporting on. In order to do that, make sure that the logs cannot be modified by an unauthorized user (configuration), that they are received in order and that their source is clearly established. If possible, implement signed log entries.
|