Secure Coding is the art of writing codes with a security-first mindset.
SecDevOps is such a buzzword these days that I am sure you have encountered it a couple of times. While this article is not about SecDevOps, a three-way handshake between security, development, and operations, it narrows down to how to put on your security helmet while writing codes: secure coding.
As cyber threats and attacks continue to increase, it is extremely important to prioritize security when building applications. Secure coding helps prevent potential attacks and vulnerabilities.
This article comprises of two parts:
- Common Software Vulnerabilities and mitigations
- Secure Coding Best Practices
If you are familiar with software vulnerabilities, skip to the Secure Coding Best Practices part.
Common Software Vulnerabilities
Vulnerabilities are loopholes in software programs. The goal of a threat actor is to find and exploit these vulnerabilities, thereby launching successful attacks on software applications. Understanding these vulnerabilities is the first step to secure coding.
1. SQL Injection
Structured Query Language (SQL) is a standard programming language used to manage and manipulate data stored in relational databases. SQL queries can perform basic create, read, update, and delete (CRUD) functions. SQL Injection occurs when an attacker injects a malicious SQL query into an application through input data.
The developer has created an input field to receive data that is run backend to either carry out a post to or pull from the database. Instead of performing the intended operation, the input data adds, modifies, reads, or deletes data from the database or, worse still, shuts down the database itself.
Example of a vulnerable SQL code:
SELECT * FROM users WHERE email = 'input_value';
Below is an SQL Injection to exploit the above vulnerability
' OR '1'='1
With the above as an input, the query becomes
SELECT * FROM users WHERE email = '' OR '1'='1';
Now, because '1'='1'
will always be true, the attacker can read all the data in the users
table. This can result in data exfiltration.
SQL Injection Mitigations
- Proper input validations: This ensures the input conforms to the required format.
- Escaping special characters: The special characters useful in SQL Injections include but are not limited to,
'
,"
,--
,=
,;
,\0
,#
,|
. Escaping these characters will make user inputs less harmful. - Using prepared statements and parameterized queries: This separates input data from code
2. NoSQL Injection (Non-relational Databases)
One would think that SQL injections are the battle only for those who use SQL databases, but they are not. NoSQL databases like MongoDB have their share of Injection vulnerabilities.
Example of a vulnerable NoSQL code:
db.users.find({ "email": input_data });
Example of an injection input data:
input_data = { "$ne": null }
Resulting Query:
db.users.find({ "email": { "$ne": null } }
Result:
The query above will return all users where the email is not null.
NoSQL Injection Mitigations
- Input validations and sanitizations
- Use of secure Object-Document Mappers which offer built-in protections against injections
3. Object-Relational Mapper (ORM) Injection
This injection relies on vulnerabilities in the ORM's generated codes. The ORM acts as a wrapper, exposing functions that make it easier to interact with the DB.
For example, in 2019, it was found that the popular Javascript ORM Sequelize was vulnerable to SQL injection attacks.
ORM Injection Mitigations
- Use updated versions
- Use ORMs with reputable security implementations
4. Null Byte Injection (%00
):
The value %00
is called a Null Byte, meaning its presence is negligible. In this type of Injection, the attacker manipulates the data to bypass weak or improper validation/filtering mechanisms.
Example:
Suppose an attacker wants to upload a malicious script onto a victim’s server, knowing that the server only validates file extensions and allows .jpeg, .jpg, .png, etc. In that case, that attacker can bypass the extension validation using a Null Byte by naming the file thus:
malicious-script.php%00.png
The file above will be successfully uploaded to the server, and the server will ignore the %00.png
and execute the malicious script.
Null Byte Injection Mitigations
- Strict file type validation: Check the MIME (Multipurpose Internet Mail Extension) type of the file instead of relying on the extension.
- Proper input validation: Sanitize filenames and reject null bytes in filenames.
5. Information Leakage:
This type of vulnerability occurs when an application inadvertently reveals sensitive information that can aid attackers in understanding its underlying technology and devising means of exploiting it.
Example:
Suppose an application returns X-Powered-By: Express
or X-Powered-By: PHP/7.0.3
in its response header. This information will help an attacker understand that the underlying technology is Express.js or that the application is running PHP version 7.0.3. Armed with this information, the attacker can tailor its exploits accordingly.
Mitigations
- Avoid verbose response headers.
- Implement security headers such as
Content-Security-Policy
,Strict-Transport-Security
, andReferrer-Policy
to enhance the application's security.
6. Missing Security Headers or Security Misconfiguration:
When security headers like Content-Security-Policy (CSP)
, Strict-Transport-Security (HSTS)
, and X-Content-Type-Options
are missing or not correctly configured, it leaves the application vulnerable to potential attacks such as man-in-the-middle attacks, cross-site scripting (XSS), MIME-type sniffing, etc.
Examples of Security Headers
6.1. Content-Security-Policy (CSP): This header whitelists the sources from which content can be loaded. By doing so, it prevents XSS attacks.
Example:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; img-src 'self' data:;upgrade-insecure-requests
- default-src 'self': By default, only resources from the exact origin of the page (i.e., the same domain) are allowed.
- script-src 'self' https://trusted-cdn.com: JavaScript can only be loaded from the exact origin or https://trusted-cdn.com.
- img-src 'self' data:: Images can be loaded from the exact origin or as inline base64-encoded data.
- upgrade-insecure-requests: Automatically upgrades insecure requests (HTTP) to HTTPS.
6.2. Strict Transport Security (HSTS): This header ensures that the browser only communicates with the application over HTTPS, preventing man-in-the-middle and protocol downgrade attacks.
Example:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
- max-age=31536000: Instructs the browser to remember to use HTTPS for this site for the next 31,536,000 seconds (1 year).
- includeSubDomains: This rule also applies to all site subdomains.
- preload: Requests that the domain be included in the HSTS preload list, which means that browsers will enforce HSTS even on the first visit.
6.3. X-Content-Type-Options: This header restricts the browser's interpretation of the content type to the value set in the Content-Type
header. Otherwise, the browser will try to guess and interpret the content type—a process called MIME-Type Sniffing.
Example:
Content-Type: text/plain
X-Content-Type-Options: nosniff
Let's assume an attacker uploads a malicious executable script but specifies the Content-Type as text/plain
; if the browser were to sniff the content, it would interpret the content as an executable file that it is and, hence, execute it. But with the header above, it will rely on the Content-Type: text/plain
header, which will not execute the harmful file.
7. Cross-Site Scripting (XSS):
Let's imagine a website is a street shop. XSS is like a sneaky dude who slips a note into the shop's display shelf. The note lures other customers to send their personal information to the sly dude.
In the context of an application, the note above is malicious Javascript that redirects users' sensitive information, such as Cookies, to the attacker.
The application is mainly vulnerable to XSS if it processes the user's input data without proper validation and sanitization, and it can allow scripts from untrusted sources to be executed on the browser.
There are two main types of XSS:
- Reflected XSS: The malicious script is reflected off the web server and executed in the user's browser. An attacker can craft a URL containing a malicious payload and execute it when a victim clicks it.
Let's assume a website has a search feature that immediately renders that result in the user's browser; an attacker can craft a search URL as below:
http://example.com/search?q=<script>alert('XSS')</script>
When the link is clicked, the browser renders it as shown below:
<html>
<body>
<h1>Search Results for: <script>alert('XSS')</script></h1>
</body>
</html>
The script then gets executed in the user's browser and viola! A successful XSS attack happens. In reality, the simple script above can be a more dangerous script that could steal a user's sensitive information and send it to the attacker.
- Stored XSS: The script persists in the server and is executed each time the browser renders the content from the server.
Let's say a website has a forum where users can post comments; an attacker can inject a malicious script into the comment input field, which is sent to the server and stored. Anyone who visits the website and interacts with the malicious comment gets hooked.
XSS Mitigations
- Ensure that any user input, reflected or stored, is adequately sanitized and HTML-encoded to prevent script execution
- Validate users' input to conform to the required data format.
- Use security headers like
Content-Security-Policy
to restrict sources from where scripts are loaded. - For more details, read OWASP XSS Prevention Cheat Sheet
8. Directory Traversal
This vulnerability exists when web servers allow operators that can be used to navigate through the directories and filesystem of the application and access unintended files. Consider a web server with the directory structure shown below:
The directory structure above indicates that the web pages live in the /var/www/html
directory. Assuming the website were www.example.com
, the URL www.example.com/profile.php
would point to /var/www/html/profile.php
on the server.
An attack can use the knowledge of Linux system directory navigation to try to access known files such as the /etc/shadow
which stores the encrypted password information of system user accounts by accessing the URL:
www.example.com/../../../etc/shadow
If successful, the attacker will feast on the system user account credentials.
Directory Traversal Mitigations
- Sanitize and normalize user-supplies input especially when it relates to file paths, ensure operators such as
..
,/
, etc are not allowed in the inputs - Whitelist all known good inputs that relates to file access
- Use secure file access APIs that has built-in protection against directory traversal
- Apply appropriate access control measures to sensitive files
9. Session Hijacking
Session hijacking occurs when an attacker gains unauthorized access to an authenticated user's session.
Imagine registering at a conference, you receive a badge signifying that you are a verified attendee. If someone unregistered steals your badge, they can pose as a verified attendee.
In a technical scenario, consider an application that issues a cookie after successful authentication, the cookie is to be used in subsequent authenticated communication with the server. The server does not care about how the cookie was generated as long as it's valid. If an attacker lays hold of this cookie, they can impersonate owner.
Session Hijacking Techniques
- Cross-Site Scripting (XSS): If an application is vulnerable to XSS, an attacker can inject malicious scripts that can execute on the user's browser and send the user's session ID to the attacker.
- Network Eavesdropping: If session IDs are transmitted over unsecured connections (HTTP), an attacker can intercept the session IDs using network traffic analyzers such as Wireshark
- Brute Force: If the session ID is predictable, an attacker can guess the session ID by trying different values until they find a valid session ID. An example of a predictable session ID would be generating cookie tokens by encoding numerically incremented user IDs in a JWT.
Session Hijacking Mitigations
- Use Secure Connections (HTTPS): Secured connections over HTTPS ensures that data is encrypted in-transit between the client and the server.
-
Use Secure Session Management: Ensure that cookies that store session IDs are marked with
Secure
andHttpOnly
flags. Keep the session expiry limit low to reduce the hijacking window (a combination of access token cookie with short TTL and a refresh token with a longer TTL would be a good approach). - Use Session Timeouts: Log out users automatically after a period of inactivity
- Use Unpredictable Session IDs: Ensure that session IDs are randomly generated, unique and unpredictable to minimize brute-force probability.
10. Cross-site Request Forgery (CSRF)
As the name implies, a request is forged on behalf of a user. The attacker exploits the user's existing authenticated session to trick the application into performing actions on their behalf without their knowledge.
There are 2 key factors that makes an application vulnerable to CSRF:
- The request parameters are predictable
- The application relies only on the session cookies to identify the requester, and the
SameSite
attribute of the cookie is misconfigured. The value ofSameSite
that provides the best protection against CSRF isStrict
, followed byLax
. See
Example:
Consider a hypothetical banking application vulnerable to CSRF attack. To transfer fund from account A to account B, the user needs to make the request below:
POST /transfer-funds HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 48
Cookie: session=WaT9lKmXYH8wJq4U7xP3rFv2BnNvG6eX
accountA=1234567890&accountB=0987654321&amount=100
An attacker can craft a webpage as shown below:
<html lang="en">
<body>
<form id="csrfForm" action="https://example.com/transfer-funds" method="POST">
<input type="hidden" name="accountA" value="1234567890">
<input type="hidden" name="accountB" value="0987654321">
<input type="hidden" name="amount" value="100">
</form>
<script>
// Automatically submit the form when the page loads
document.getElementById('csrfForm').submit();
</script>
</body>
</html>
Notice that the inputs are hidden and the form submits automatically on page load.
The attacker can use similar methods as Reflected XSS to deliver the attack to the victim by crafting a link which when the victim clicks it executes the attack. If the victim is authenticated in the banking app on the same browser at the time of clicking the link, the session cookie is sent alongside the form data, and the attacker succeeds transferring fund from the victim's account to their designated account.
CSRF Mitigations
- Use CSRF Tokens: CSRF tokens are unique secrets that the server issues the client which the client must include with sensitive requests to the server. Instead of relying only on the session cookie, the CSRF tokens offers another layer of protection against CSRF attacks.
-
Set SameSite Cookie Attribute: SameSite attribute defines the sites the browser can include a given cookie when requests are sent to them. While
Strict
offers the best protection against CSRF,Lax
permits cross-site for less sensitive GET requests.
Secure Coding Best Practices
From the foregoing, it is apparent that a majority of the software vulnerabilities and the associated threats can be prevented at the point of writing the code. For each of the vulnerabilities considered, there were a few suggested mitigation strategies, and by applying those strategies, you are practising Secure Coding.
Below are the summarized OWASP Secure Coding best practices:
Input Validation: Validate all user inputs, including HTTP headers and database queries, to prevent SQL injection and XSS attacks. Implement input validation centrally and sanitize potentially harmful characters.
Authentication Security: Centralize authentication, enforce HTTPS, use security-verified libraries, and validate authentication data securely. Avoid revealing specific authentication errors and enforce multi-factor authentication for sensitive accounts.
Server Authentication: Ensure SSL/TLS usage with proper validation, avoid weak client authentication, and ensure secure handling of authentication credentials, including storing them in a secure location like Secrets Manager.
Password Management: Enforce password complexity and length, hash and salt stored passwords, limit password reset attempts, and prevent password re-use. Disable
remember me
functionality for password fields.Authorization Controls: Implement rule-based access control, enforce least privilege, re-authenticate users for critical operations, and prevent unauthorized access to sensitive URLs. Avoid including authorization information in query strings.
Session Management: Use secure session management controls, ensure session identifiers are not exposed, and enforce session timeouts. Implement per-session strong tokens to prevent CSRF attacks and ensure secure logout procedures.
JWT Security: Use secure algorithms and key lengths, validate JWT tokens and fields, ensure secure communication via HTTPS, and frequently rotate keys. Avoid using the "none" algorithm and ensure the integrity of JWTs.
Output Encoding: Encode all user-supplied data before outputting it to prevent XSS attacks. Use server-side encoding and sanitize all data, especially in unsafe contexts like JavaScript functions or URL handling.
Canonicalization: Convert input data to a standard format, such as UTF-8, to prevent spoofing attacks. Ensure proper validation of URLs and parameters to avoid insecure direct object references.
JSON Security: Use application/json Content-Type, avoid eval() for parsing JSON, and ensure proper error handling for duplicate keys and numbers. Sanitize all data before serializing it to JSON to prevent JSON injection attacks.
Data Protection: Encrypt data both in transit and at rest.
Handle all Errors and Exceptions: Ensure that the application handles all error gracefully and that errors do not reveal too much information.
Implement Security Logging and Monitoring: This ensures that problems are identified early enough, and also helps in troubleshooting the problems.
More Resources
https://portswigger.net/web-security/nosql-injection
https://owasp.org/www-community/attacks/xss/
OWASP XSS Prevention Cheat Sheet
Cross-site request forgery (CSRF)