The IEEE Computer Society Center for Secure Design has released a list of design flaws with an intention to shift some of the focus in security from finding bugs to identifying common design flaws that often lead to security loopholes; with a hope that software architects can learn from others’ mistakes.
Never assume trust:
Software systems work on the right functioning of its existing parts, which if tampered by an attacker renders the whole system insecure. Exposure to untrustworthy environment leads to compromises and so any data sent from untrusted clients should be considered compromised. It is safer to consider that any systems whose components can’t be trusted are not suitable for performing security sensitive tasks. Security aware development strategies cannot eliminate app problems but can minimize potential risks. Subject matter experts should be consulted if system needs a client component with uncompromised level of protection. Also it needs to be made sure that in case of a threat, the system works in a limited fashion.
Using the right authentication mechanisms:
Every user who tries to access a system should be authenticated. Authentication is the act of validating an entity’s identity. Secure designs prevent unauthenticated entities from gaining access to systems. Multifactor authentications that rely upon something you know (e.g., a password), something you are (e.g., biometrics such as fingerprints), or something you have (e.g., a smartphone) should be used to authorize sensitive functions. The system should also consider the strength of authentication before taking action. Authenticated users are provided with tickets/tokens to prevent re-authentication. Designers can use time tested mechanisms like Kerebos and use the right cryptography for authentication of credentials. An authenticated interaction should be time limited so that access does not remain open when the user is idle. In all, a single authentication mechanism should leverage one or more factors as per an application’s requirements.
Authorize after authentication:
Not every authenticated user is given access to every aspect of the system. This is when authorization is necessary. Authorization should be conducted as an explicit check, and as necessary even after an initial authentication has been completed. Authorization depends not only on the privileges associated with an authenticated user, but also on the context of the request. The time of the request and the location of the requesting user may both need to be taken into account.
Separate data and control instructions, and never process control instructions received from untrusted sources:
Co-mingling data and control instructions in a single entity, especially a string, can lead to injection vulnerabilities. At lower layers, lack of strict segregation between data and control instructions can result in the introduction of memory-corruption vulnerabilities, which permits attacker-controlled modifications of control flow or direct execution of attacker-controlled data as machine or byte-code instructions.
When designing applications that rely on existing APIs, avoid APIs that mingle data and control information in their parameters, especially when those parameters are strings. If there is no choice in underlying APIs (for example, if the use of a relational database requires interfacing through a SQL query API), it is often desirable to encapsulate the injection-prone interface and expose its functionality to application code through a higher-level API that enforces strict segregation between control statements and potentially untrusted data.
All data to be explicitly validated:
Software systems and components commonly make assumptions about data they operate on. Vulnerabilities frequently arise from implicit assumptions about data, which can be exploited if an attacker can subvert and invalidate these assumptions. It needs to be ensured that all data are explicitly validated. It is furthermore desirable to design software to make it feasible for a security reviewer to effectively and efficiently reason about and verify the correctness and comprehensiveness of data validation. Designing for verifiability should take into account that code typically evolves over time, resulting in the risk that gaps in data validation are introduced in later stages of the software life-cycle.
Using the right cryptography:
Cryptography is one of the most important tools for building secure systems and hence it should be used correctly. It ensures the confidentiality of data, protects data from unauthorized modification, and authenticates the source of data. Cryptography is so hard to get right that it always makes sense to work with an expert if you can.
There are many common errors related to cryptography. Designing cryptographic algorithm requires significant skills and even experts produce algorithms with subtle errors. This can leak info to attackers and hence standard algorithms and libraries are preferable. Incorrect assumptions about using library routines or choosing the wrong algorithm leads to faulted cryptography.
Identify critical data and how to handle them:
Data are critical to organizations and to users and hence it is necessary to identify sensitive data and how they should be handled. One of the first tasks that systems designers must do is identify sensitive data and determine how to protect it appropriately. Data sensitivity is context-sensitive. It depends on many factors, including regulation, company policy, contractual obligations, and user expectation. Creating a policy that explicitly identifies different levels of classification is the first step in handling data appropriately. It is important to factor all relevant considerations into the design of a data sensitivity policy. Data sets do not exist only at rest, but in transit between components within a single system and between organizations. As data sets transit between systems, they may cross multiple trust boundaries. Identifying these boundaries and rectifying them with data protection policies is an essential design activity.
Always consider the user:
Understanding users are of utmost importance while designing software. Almost every software system in existence today interacts in one way or another with human beings, from fielding to end-user. The security stance of a software system is inextricably linked to what its users do with it. It is therefore very important that all security-related mechanisms are designed in a manner that makes it easy to deploy, configure, use, and update the system securely. Usability and user experience considerations are often the most important factors ensuring that software systems operate in a secure manner.
Integrating the right external components:
It is important to understand how integrating external components changes your attack surface. You need to consider how the external components change the threat model of the entire system and if it adds to the attack surface. You also need to consider if it modifies entry points in the system that had already been considered in its own threat model. Unused features and interfaces needs to be checked for engagement and should be disabled if not being used. These also need to be a check whether the external components used have other external components with their own security weaknesses. It always better to get external components from a trusted source. The external components should also provide security documentation that helps better understand its threat model and security implications of its configuration.
Considering future flexibility:
Change is an integral part of software security development. Software, the environments running software, and threats and attacks against software all change over time and hence designers need to consider the security implications of future changes to objects and actors. Security designs should keep flexibility in mind. This includes the provision to have updates that change minor parts of the software to keep it up to date with the changing technological environment, to shut down a specific aspect of the software in case of a threat or external attack, to change algorithms and layers as needed into the system and to revoke access to areas when a user no longer has a need to access them.
– Read the details here.