In 2023, the average cost of a data breach reached $4.45 million, the highest in seven years. With cyber threats becoming more sophisticated and the cost of data breaches soaring, prioritizing application security is no longer an afterthought but a necessity — and it offers a competitive advantage to businesses that get it right. Many software companies already understand the importance of application security. According to our 2024 State of Software Quality report, 84% of development teams conduct regular security audits, and 88.4% have a dedicated security team or person.
This article will teach you about application security, common security threats, and best practices to secure and mitigate these threats. Understanding application security will help you develop and deploy reliable, scalable, and secure software applications.
What is Application Security?
Application security, or AppSec, is the practice of protecting software applications from malicious attacks, unauthorized access, data breaches, and other potential threats. These activities include:
- Integrating security into every stage of the application's development lifecycle, from planning and design to coding, deployment, and maintenance
- Implementing security best practices such as input validation, encryption, and authentication
- Conducting regular security audits and assessments to measure the security level of the application
- Monitoring and responding to security incidents and events that happen on the application in real-time
Five Common Application Security Threats and Mitigation Strategies
The Open Web Application Security Project (OWASP) is a non-profit organization that aims to improve the security of web applications. One of its most popular and influential projects is the OWASP Top 10, a standard awareness document for developers on web application security. It represents a broad array of the most critical security risks to web applications based on data analysis from web sources and community feedback.
These five vulnerabilities were highlighted on the OWASP list as key security concerns. You need to know how each can hurt your business and how to effectively mitigate them in order to fully protect your business.
1. Broken Access Control
Broken access control is the failure to enforce proper restrictions on the actions or resources that users can perform or access on a web application. Broken access control allows attackers to bypass authentication, retrieve unauthorized data or functionality, escalate privileges, or execute commands on behalf of other users.
Some scenarios are:
- An attacker changes the URL parameters and accesses another user's account information.
- An attacker changes a different URL parameter https://sample.com/download?file and accesses a restricted file.
- An attacker modifies administrative functions, such as creating or deleting users, changing settings, or viewing logs without being an admin user.
Let's see an example of broken access control using some Python code. The following function looks up an account by its ID and returns some information using a JSON Web Token (JWT) for authentication:
import jwt
def account_lookup(account_id, jwt_token):
try:
token = jwt.decode(jwt_token, 'secret', algorithm='HS256')
except Exception as e:
return "Invalid token"
if "logged_in" in token.keys() and token["logged_in"] == True:
# query the database for the account information
# return the account information
else:
return "User is not logged in"
This function has a flaw: it does not check if the user making the request is the account owner or has the right to access it. It only checks if the user is logged in with a valid token.
This means an attacker with a valid token can change the account_id parameter to any value and access any account's information.
To fix this vulnerability, we need to implement some form of authorization, such as role-based access control (RBAC) or attribute-based access control (ABAC). For example, we can add a check to see if the user's role is "admin" or if the user's ID matches the account ID:
import jwt
def account_lookup(account_id, jwt_token):
try:
token = jwt.decode(jwt_token, 'secret', algorithm='HS256')
except Exception as e:
return "Invalid token"
if "logged_in" in token.keys() and token["logged_in"] == True:
# check the user's role or ID
if token["role"] == "admin" or token["user_id"] == account_id:
# query the database for the account information
# return the account information
else:
return "Access denied"
else:
return "User is not logged in"
This way, we can prevent unauthorized users from accessing or modifying other users’ data or functionality.
To avoid these kinds of attacks, web developers should follow some best practices:
- Deny by default and principle of least privilege: Users should only have access to the minimum number of resources and actions needed to perform their tasks. Everything else should be denied by default.
- Do not trust the client to be secure: The server should verify the identity and authorization of the user before allowing any access or action and not rely on the client-side validation or parameters.
- Implement record-based access control (RBAC): Check the ownership or relationship of the user to the record before allowing any access or action. Consider using services like Oso, Clerk, and Zanzibar that offer dynamic authorization for RBAC.
- Use GUIDs or random identifiers for storing records and URLs: Globally unique identifiers (GUIDs) and random strings are much harder to guess and identify, making it harder for attackers to manipulate.
2. Cross-site Scripting (XSS)
Cross-site scripting (XSS) is a web security vulnerability in which an attacker injects harmful executable scripts into the code of a trusted application or website. The attacker can use the injected code to steal or manipulate the user's data or perform actions on their behalf.
For example, suppose a web application allows users to post comments on a blog. If the web application does not correctly validate or encode the user input, an attacker can post a comment that contains a script tag with some malicious code, such as:
<script>alert('XSS');</script>
When another user views and clicks on the comment, the browser will execute the malicious script on the user's system. The attacker can then use this to perform unwanted actions on the other user’s account.
To avoid XSS attacks, you should follow best practices, such as:
- Sanitizing and validating user input: Check the user input for any malicious or unexpected characters or values, and reject or sanitize them before processing or storing them.
For example, you can use HTML "safe sinks" like element.textContent = userInput;. These are HTML elements or attributes that treat data as text, preventing script execution even if malicious code is present.
2. Implementing content security policy (CSP): A CSP is an HTTP header that instructs the browser to only execute or load scripts or resources from trusted sources.
For example, the code “Content-Security-Policy: script-src 'self';” uses the script-src directive and sets the source list to 'self.' This CSP tells the browser to only allow scripts from the exact origin of the current page. If an attacker tries to inject a malicious script from a different origin, the browser automatically rejects it.
3. Cross-Site Request Forgery (CSRF)
Cross-Site Request Forgery (CSRF) is an attack that forces users to perform actions they do not intend to perform. This occurs when a user is logged into a website, and an attacker tricks them into unknowingly executing malicious actions, often by exploiting the website's trust in the user's browser.
A CSRF attack usually starts with an attacker creating a malicious link or form, often disguised as a harmless link or button on a website or within an email. The attacker then tricks the legitimate user into visiting their malicious website.
If the user is logged in to another application, such as a banking application, the malicious link will send a forged request to the banking application in the background. This request will appear to come from the legitimate user because the browser will automatically include the user's session cookie or ID.
Suppose the banking application does not have protection against CSRF. In that case, it will grant the attacker permission to perform unauthorized actions, such as transferring money, changing passwords, or deleting data without the legitimate user's knowledge.
You can prevent CSRF attacks by:
- Implementing anti-CSRF tokens: Always include unique, unpredictable tokens in each form or request to a server. Most programming languages and frameworks have libraries to generate anti-CSRF or JWT tokens that are generated and stored on the server side. Check the legitimacy of the token value on the server side before processing the request, and reject the request if the value does not match the expected value.
- Implementing same-site cookies: Use a cookie attribute to ensure the cookie is only sent along with requests originating from the same website that created it and not to cross-site requests. This prevents the browser from automatically sending the cookies or session tokens to the web application and, thus, prevents CSRF attacks.
4. SQL Injection Attacks
An SQL injection (SQLi) is a web security vulnerability that allows an attacker to interfere with the queries that an application makes to its database. An SQL injection allows the attacker to view or manipulate data they shouldn’t be able to.
This code uses SQL queries to authenticate users during the login process. It selects a user from the user table and checks if the username and password match what was supplied:
SELECT * FROM users WHERE username = 'inputUsername' AND password = 'inputPassword';
An attacker could input a username in the input field, such as:
' OR '1'='1'; –
This manipulates the original query to become:
SELECT * FROM users WHERE username = '' OR '1'='1'; --' AND password = 'inputPassword';
The double hyphen (--) signifies a comment in SQL, effectively ignoring the remainder of the original query. The modified query always evaluates to true since (OR '1'='1'). Because of this, the attacker is granted unauthorized access to the application.
You can prevent SQL injection attacks by:
1. Implementing parameterized queries: With parameterized queries, the user input will not be interpreted as part of the SQL statement but as a literal value.
For example, look at a query vulnerable to SQL injection, like:
sql = “SELECT * FROM users WHERE username = 'username’ AND password = ' password ”
Instead, you should write a parameterized query like this:
sql = “SELECT * FROM users WHERE username = ? AND password = ?”
Then supply the values for the placeholders (?) using a safe, language-specific method:
Python
values = ("alice", "1234")
c.execute(sql, values)
This way, the user input will not affect the structure of the SQL statement and will prevent SQL injection attacks.
2. Always sanitizing and validating user input: Check the user input for any malicious or unexpected characters or values, and reject or sanitize them before processing or storing them. For example, you can use a regular expression to check that the user input only contains alphanumeric characters and spaces and raise an exception to the user if it contains unwanted characters.
5. Security Misconfiguration
Security misconfiguration often results from using insecure settings for databases, servers, and other services. This can allow attackers to gain unauthorized access to the systems, data, or applications. Security misconfiguration can occur in many ways, such as:
- Using default settings and credentials for databases and servers
- Storing sensitive data like database connection strings and encryption keys in clear text
- Storing data in the cloud without adequate security controls
- Logging data incorrectly, leading to verbose error messages and sensitive information being stored in log files
To prevent security misconfiguration, you should:
- Change default settings immediately after system set-up: Don't leave systems with their out-of-the-box configurations. Attackers often target these known defaults, so reconfiguring critical settings for all services is important.
- Enforce strong password and authentication policies: Enforcing strong password and authentication policies can prevent unauthorized access and protect your system from brute force and credential-sniffing attacks. Use multi-factor authentication (MFA) for extra layers of protection.
- Use the least privilege principle: Give users only the access needed to perform their tasks. This can limit the damage caused by compromised accounts and reduce the risk of privilege escalation attacks.
- Ensure sensitive information is encrypted in log files: Log files contain valuable information about your system's activities and performance. However, they can also contain sensitive information such as user data, passwords, and keys. Encrypting sensitive information in log files can prevent data leakage and protect your system from unauthorized access.
- Use industry-standard algorithms for data encryption and hashing: Data encryption and hashing are essential for protecting your system's data integrity and confidentiality. Stick to well-established algorithms like AES-256 for data encryption and SHA-256 for hashing passwords and confidential data.
- Employ vulnerability scanning and configuration management tools: Vulnerability scanning and configuration management tools help you identify and address misconfigurations and vulnerabilities in your system before they become security breaches.
- Apply security patches and updates promptly: Regularly applying security patches can prevent attackers from exploiting known flaws and improve your system's security and performance.
Strengthen Your Application Security With Codacy
Codacy is an automated code quality and security platform that helps you ship better, more secure, and faster software. It offers security features such as:
- Static Application Security Testing (SAST): Scans your source code for software vulnerabilities, such as XSS, SQL injection, and other OWASP Top 10 issues.
- Hard-Coded Secrets Detection: Detects exposed API keys, passwords, certificates, and encryption keys that may compromise your application security.
- Supply Chain Security: Monitors your code for known vulnerabilities, common vulnerabilities and exposures (CVEs), and other risks in open-source libraries.
Codacy supports over 40 programming languages and frameworks and integrates with the most popular security tools, such as Semgrep, Trivy, Bandit, Brakeman, and FindBugs.
Start your free trial today and strengthen your application security through secure code reviews with Codacy.