Boosting Web Security: Key Coding Practices to Mitigate Cybersecurity Threats
In the fast-paced digital era, where the reliance on web technologies is more than ever before, APIs, websites, or applications, keeping these tools safe and sound is super important. Whether you are a pro in tech or a novice in tech careers, awareness of potential cybersecurity issues that your website or application could face is very important. After all, it is not just about identification but acquiring the knowledge and tools that would help you shield yourself and build strong defenses from the risks.
Let's consider some of the common security vulnerabilities and understand their impacts in the real world. We will now take a closer look at practical steps you can take to harden your applications.
Let’s explore some common security vulnerabilities, understand their real-world impacts, and delve into practical steps you can take to fortify your applications.
SQL Injection
SQL injection vulnerabilities are a critical threat in web applications.They allow sneaky users to slip in and run all sorts of SQL code on your database. This could mean they get into your data without permission, mess around with it, or even wipe it out completely. The impact of a successful injection attack is severe, potentially resulting in identity theft, creation of new accounts with administrative privileges, unauthorized access to all data stored on the server, or even destruction and alteration of data rendering making it unusable.
This vulnerability is present if user input that is passed to an underlying SQL statement can change the meaning of the statement. For example, the following code is intended to list all users with a particular name (userName) that has been supplied from an HTML form:
statement = "SELECT * FROM users WHERE name = '" + userName + "';";
If the user specifies a real name, the statement will work as intended. However, a malicious user could completely change the behavior of this SQL statement to the new statement in the following example, by specifying a ';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't';
for the userName.
SELECT FROM users WHERE name = 'a';DROP TABLE users; SELECT FROM userinfo WHERE 't' = 't';
The modified statement creates a valid SQL statement that deletes the users table and selects all data from the user info table (which reveals the information of every user). This works because the first part of the injected text (a';) completes the original statement.
To prevent such attacks, it's crucial to make sure that any user data used in a query cannot alter the query itself. Here are some ways to protect against SQL injection.
Basic is to do input sanitisation
Input validation checks the format, length, and content of user inputs to ensure they meet expected criteria. Web frameworks will often take care of the character escaping for you. For instance, Django ensures that any user data passed to querysets (model queries) is escaped. Sanitization removes any potentially harmful characters from inputs to prevent them from affecting the structure of SQL queries. Encode user inputs before using them in SQL queries. Encoding converts potentially dangerous characters into their equivalent safe representations, preventing them from being interpreted as part of SQL syntax.
from flask import Flask, request, escape
app = Flask(__name__)
@app.route('/submit-post', methods=['POST'])
def submit_post():
user_post = request.form['post_content']
# Sanitize the post content to remove potentially dangerous HTML tags
safe_post = escape(user_post)
# Further processing like storing to database
store_post(safe_post)
return 'Post submitted successfully!'
def store_post(post_content):
# Assume function to store post to database
pass
if __name__ == '__main__':
app.run()
In this example, the escape
function of Flask is used to convert input to HTML-safe sequences, preventing any embedded scripts from executing when other users view the post.
Use Parameterized Queries and form framework
Instead of constructing SQL commands by simply joining strings together, a more secure approach is to employ parameterized queries. Parameterized queries treat user input strictly as data, preventing it from being executed as code. Utilize ORM libraries or frameworks provided by your programming language or platform. ORM tools abstract much of the SQL query construction and execution, automatically handling parameterization and sanitization.
import sqlite3
def get_user_profile(user_id):
connection = sqlite3.connect('example.db')
cursor = connection.cursor()
# Parameterized query
cursor.execute('SELECT * FROM users WHERE id=?', (user_id,))
user_profile = cursor.fetchone()
cursor.close()
connection.close()
return user_profile
This example uses parameterized queries to safely fetch user data without risking SQL injection, as the user input is treated only as data, not executable code.
Authenticate and authorise
To control access to API resources, we must carefully and comprehensively identify all related users and devices. This typically requires client-side applications to include a token in the API call so that the service can validate the client. Use standards such as OAuth 2.0, OpenID Connect, and JSON web tokens to authenticate API traffic and to define access control rules or grant types that determine which users, groups, and roles can access specific API resources.
For example- If a user just needs to read a blog or post a comment, those are the only permissions that should be assigned. Organisations that want to enable third parties to access internal data and systems through APIs must introduce and test controls to manage that access: who, what, and when, as well as checks on data access, creation, update, and deletion.
Least Privilege Principle with Database User Permissions
As it goes by the name, the least privilege principle states that users or processes should only be granted the minimum level of access or permissions necessary to perform their tasks. With proper restriction of permissions on the database user account used by your application, you essentially limit the potential impact of SQL injection attacks to your application. This gives the effect that attackers are blocked from being able to execute harmful SQL commands.
-- Create a new role with limited privileges
CREATE ROLE readonly_user;
-- Grant SELECT permission to readonly_user for the necessary tables
GRANT SELECT ON table_name TO readonly_user;
-- Create a user and assign them the readonly_user role
CREATE USER limited_user WITH PASSWORD 'secure_password';
GRANT readonly_user TO limited_user;
In this example, a new database role (readonly_user
) is created with limited permissions—specifically, only the ability to read data (SELECT
) from a specific table (table_name
). A user (limited_user
) is then created and assigned this role, ensuring they cannot modify, delete, or insert data, which limits the damage they could do if their account is compromised.
Enable database auditing features
Auditing could be of great importance to keep a record of and track all SQL queries run against the database. When enabled, detailed auditing log events are recorded concerning activities within a database, such as the suspicion of querying patterns against the database. This log can be important if one wants to identify and investigate possible SQL injection attacks, hence it can facilitate proactive measures for improved security of the database.
You can use database auditing tools with full audit and reporting for Oracle databases, such as Oracle Audit Vault, or Microsoft's SQL Server Audit, with tooling allowing you to have extensive audit logs with SQL Server instances. Other toolsets and free, open-source tools like pgAudit for PostgreSQL offer robust options for tracking and logging database activities.
Cross-Site Scripting (XSS)
Cross-site scripting is a generic condition or kind of attack whereby injurious scripts are injected into websites, after which they affect the innocent users' browsers. In particular, this condition allows attackers to exploit the trust the site has garnered to inject their own code and hence gain access to vital information. For instance, they can hijack the user's site authorization cookie and use it to impersonate the user, thereby gaining access that is not authorized to personal data, such as credit card details and contact information, with the possibility of changing passwords.
It is worth noting that XSS basically exploits a client-side vulnerability that allows an attacker to execute a malicious script within the user's browser. Injection in SQL, on the other hand, exploits server-side vulnerabilities that allow an attacker to tamper with SQL queries, which may compromise an application's database through making access that is not allowed or changing the content of the database.
Despite advances in web technologies and the adoption of modern JavaScript frameworks that inherently manage many security concerns, XSS vulnerabilities still persist. These vulnerabilities often arise due to improper handling of user inputs, misconfigurations, and failure to adhere to best practices in security.
Modern websites using JavaScript frameworks like React, Vue, Angular, or Next.js are not automatically immune to XSS; however, these frameworks do provide tools and mechanisms that can help mitigate such vulnerabilities if used correctly. Here are some strategies and practices that can be particularly effective:
Input Validation and Output Encoding
Sanitize Inputs: Make sure to check and clean up all user inputs to avoid any sneaky malicious code. Just say no to input that looks like code.
Output Encoding: When showing user input on a webpage, encode it properly to make sure special characters just show up as they are, not as sneaky code. Think HTML, JavaScript, and URL encoding.
Use Security Headers
Content Security Policy (CSP): When you set up CSP, you're basically telling your website where it can load content from. This helps stop cross-site scripting (XSS) attacks by controlling what scripts can run on your site.
X-XSS-Protection Header: This header tells the browser to watch out for and stop XSS attacks. Even though some browsers don't rely on this header as much now, it can still add another shield of protection in some cases.
Escape User Input in Templates
When using templating engines, ensure that user input is properly escaped. Many modern frameworks provide built-in mechanisms to automatically escape dangerous characters when generating HTML.
Avoid Dangerous JavaScript Functions
Functions like eval()
, innerHTML
, document.write()
, and others that directly manipulate the DOM can be exploited by attackers. Instead, use safer alternatives like textContent
or avoid direct DOM manipulation.
Use Trusted Libraries
Rely on well-established libraries for input validation and sanitization. These libraries are usually vetted for security and updated regularly to address new vulnerabilities.
Secure Cookies and Sessions
Set the HttpOnly
and Secure
flags on cookies to prevent client-side scripts from accessing session cookies. This helps mitigate XSS-related session hijacking.
Cross-Site Request Forgery (CSRF)
Imagine you're building an e-commerce site where users frequently update personal details like their email or password. These sites are prime targets for a type of attack called Cross-Site Request Forgery (CSRF). Here's how it might play out:
Picture this: Eve, an attacker, sets up a fake website that looks just like your e-commerce platform's email update form. She makes the form submit automatically using JavaScript as soon as someone loads the page. Then, she sends emails pretending to be your site, offering special discounts to lure users in. When a logged-in user clicks the link and lands on her page, their browser automatically sends their session cookies along with the form. The server thinks this is a legitimate request from the user and updates the email address to one controlled by Eve. Just like that, Eve can reset the user's password and hijack their account.
Support in Modern Frameworks
Django: This Python-based framework automatically applies CSRF protection via middleware. It attaches CSRF tokens to every form and AJAX request that can modify state. However, we must remember to include {% csrf_token %}
in our templates for the protection to be effective.
Ruby on Rails: Rails uses CSRF tokens by default for forms, but we must ensure that APIs receiving AJAX calls are also protected, as Rails’ default mechanisms cover HTML forms primarily.
Spring Security (Java): Provides CSRF tokens automatically for forms but requires manual configuration if you're using AJAX-heavy front ends or REST APIs.
Express.js (Node.js): Does not include CSRF protection out of the box. We need to integrate middleware like csurf to handle CSRF tokens manually.
Even with framework support, there are additional practices you should adopt:
Always ensure that all forms and state-changing requests within your application include a CSRF token, not just those automatically handled by the framework.
Configure your cookies to be sent only in requests originating from the same site the cookie was set on.
Conclusion
Securing our web applications and APIs is not about dealing with vulnerabilities already in existence; it's about making security a proactive culture. As technology advances, so do the evil schemes targeting its vulnerabilities. Probably, that is why vigilance and continuous learning are of equal importance with the implementation of the security practices.
Security is a dynamic field and requires a commitment to ongoing learning and adaptation. To stay ahead of potential threats, it is essential to keep abreast of the latest security trends and updates. Websites like The Hacker News, Krebs on Security, and CSO Online provide up-to-date information on cybersecurity developments and can be invaluable resources for any developer looking to deepen their understanding of how to protect their applications.
Thank you for taking the time to read this blog. Remember, every step you take towards improving security is a step towards safeguarding our and our users' digital experience. Keep learning, stay updated, and continue to fortify your applications against the ever-evolving landscape of cyber threats. Our proactive efforts today are the backbone of a safer digital tomorrow.