Secure design
- Design criteria
- Coding practices
- Practice in Java
- Analysis tools
- Top 10 controls
- Top 10 attacks on the web
Security should be a concern, not functionality.
Design criteria
Here is a list of possible criteria to consider for designing secure code:
- Least Privilege: An entity should only have the necessary set of permissions to perform the actions for which they are authorized, and no more.
- Default Fail-Safe: A user's default level of access to any system resource should be "denied" unless explicitly granted "permission".
- Mechanism Economy: The design should be as simple as possible. This makes them easier to inspect and trust.
- Complete Mediation: A system must validate access rights to each and every one of its resources.
- Open Design: Systems must be built openly, without secrets or confidential algorithms.
- Separation of privileges: Granting permissions to an entity must be based on multiple conditions, not just one.
- Less common mechanism: Anything that is shared between different components can be a communication path and a potential security hole, so the minimum possible data should be shared.
- Psychological acceptability: Security mechanisms should not make access to the resource more difficult than if they were not there.
- Responsibility: The system must record who is responsible for using a privilege. If there is abuse, we will be able to identify the person responsible.
Coding practices
Technical aspects that enhance the security of the code:
- Immutability: We can save ourselves problems associated with data integrity and availability.
- Fail-fast design by contracts: Clearly establishing what the preconditions and postconditions are for something to work correctly.
- Validation: We validate the origin, size, context, syntax and semantics of the data interacting with the system. There are libraries that help with this task, such as the Commons Validator.
Practice in Java
- Use access modifiers correctly and minimize coupling (minimum API).
- Avoid serialization, reflection and introspection.
- Do not expose credentials or personal information, or store it in source code or resource files: use the environment.
- Uses known and tested libraries, monitors third-party dependency vulnerabilities and updates to the latest version.
- Always use prepared statements (avoid SQL injection).
- Don't show implementation information in error messages.
- Checks that input to the system does not cause disproportionate use of CPU, memory and disk space.
- Always free resources: files, memory, etc.
- Check that primitive types such as integer do not overflow (you can use
addExact
,multiplyExact
,decrementExact
).
Analysis tools
We have dynamic tools (which run the code) and static tools (which analyze the code without running it).
Dynamic:
- Integration and unit testing: You need to design integration and unit tests into the code. In Java, we have JUnit.
- Code coverage: tools used to detect which code has been executed or not, and decide if we need to add test cases. We have a coverage tool in Eclipse (Java), for example.
Static:
- Static code analysis: allows us to analyze the quality of the code we have written, with the detection of the most common semantic errors in the language. In Java, we can use SpotBugs and its two extensions, fb-contrib and find-sec-bugs.
Top 10 controls
According to OWASP 2018, this is the TOP 10 controls a developer should do:
- Define application security requirements. You need to identify, research, document, implement and test the functionalities in the application against the requirements. A good starting point can be the catalog at the ASVS.
- Take advantage of existing security libraries. Make a catalog of the bookstores, and make sure they are maintained and updated. Try to keep them always encapsulated, in case you need to replace them.
- Secure access to the database. This applies to queries (avoid injection), configuration (avoid default), authentication (user management), authorization (only necessary), and communication (encryption).
- Encode and escape the data. This allows any interpreter to not read special characters and allows unintended actions to be performed on the application. You can avoid injection and cross site scripting.
- Validate all entries. Always on the server, both syntactically (format: whitelisting / blacklisting) and semantically (value).
- Implement digital identity. We need to verify the user's identity (authentication), and perhaps maintain their state (session management). We can do this with passwords, with multi-factor and with cryptography.
- Force access control. Access must be allowed or denied granularly and for all requests. Criteria: deny by default, least privilege, avoiding hardcoded roles, and logging events.
- Protect data anywhere. Both in transit (end-to-end encryption) and at rest. At rest, if possible, do not store anything. On mobile, be more cautious and always use secure storage. In servers, never in code, better in environment variables.
- Implement security registration and monitoring. It allows us to use intrusion detection systems, perform forensic analysis and implement regulatory regulations.
- Handle all errors and exceptions. Make your code more secure and reliable. In addition, bugs can allow detection of attack attempts. Make a record, and if you show something to the user, that it doesn't contain critical data.
Top 10 attacks on the web
According to OWASP 2017:
- Injection: Injection faults, such as SQL, NoSQL, US or LDAP occur when untrusted data is sent to an interpreter, as part of a command or query. The attacker's malicious data can trick the interpreter into executing unintended commands or accessing data without proper authorization.
- Authentication Break: Application functions related to authentication and session management are implemented incorrectly, allowing attackers to compromise users and passwords, session tokens, or exploit other implementation flaws to assume identity of other users (temporarily or permanently).
- Exposure of sensitive data: Many web applications and APIs do not adequately protect sensitive data such as financial, health or Personally Identifiable Information (PII). Attackers can steal or improperly modify this protected data to commit credit card fraud, identity theft, or other crimes. Sensitive data requires additional protection methods, such as encryption in storage and in transit.
- External XML Entities (XXE): Many old or poorly configured XML processors evaluate references to external entities in XML documents. External entities can be used to reveal internal files via URI or internal files on out-of-date servers, scan LAN ports, execute code remotely, and perform denial-of-service (DoS) attacks.
- Loss of Access Control: Restrictions on what authenticated users can do are not properly enforced. Attackers can exploit these flaws to gain unauthorized access to functionality and/or data, other users' accounts, view sensitive files, modify data, change access rights and permissions, etc.
- Incorrect Security Configuration: Incorrect security configuration is a very common problem and is partly due to setting the configuration manually, ad hoc, or by default (or outright lack of configuration). Examples are: open S3 buckets, misconfigured HTTP headers, error messages with sensitive content, lack of patches and updates, outdated frameworks, dependencies and components, etc.
- Cross-Site Scripting (XSS): XSS occurs when an application takes untrusted data and sends it to the web browser without proper validation and encoding; or update an existing web page with user-supplied data using an API that executes JavaScript in the browser. They allow commands to be executed in the victim's browser and the attacker can hijack a session, modify websites, or redirect the user to a malicious site.
- Insecure Deserialization: These flaws occur when an application receives malicious serialized objects and these objects can be manipulated or deleted by the attacker to perform replay attacks, injections, or elevation of execution privileges. In the worst case, insecure deserialization can lead to remote code execution on the server.
- Using Vulnerable Components: Components such as libraries, frameworks, and other modules run with the same privileges as the application. If a vulnerable component is exploited, the attack can cause data loss or take control of the server. Applications and APIs that use components with known vulnerabilities can weaken application defenses and allow various attacks and impacts.
- Insufficient logging and monitoring: Insufficient logging and monitoring, along with a lack of response to incidents, allow attackers to sustain the attack over time, pivot to other systems, and manipulate, extract, or destroy data. Studies show that the detection time of a security breach is greater than 200 days, being typically detected by third parties rather than by internal processes.