To access material, start machines and answer questions login.
Welcome to this room, where you will follow Jessica, an enthusiastic junior application security engineer, as she conquers code security dilemmas using Snyk. Join forces with her and elevate your proficiency in securing modern software projects!
Learning Prerequisites
We recommend completing the DevSecOps path before diving into this room and completing the Snyk Open Source room.
Learning Objectives
- Identify security vulnerabilities and understand the significance of integrating security testing throughout the SDLC
- Set up, configure, and use Snyk Code for analysing codebases and discovering potential issues
- Remediate discovered vulnerabilities by applying recommended fixes
- Measure the impact of a security tool implementation via calculating returns on investment
In her ongoing mission to improve the security posture of Patch Corp, Jessica is now turning her attention to tackling another critical area: code-level security. With her successful implementation of Snyk Open Source, she has gained the confidence and support of the leadership team to continue enhancing the organisation's security measures. This time, Jessica will be introducing Snyk Code to identify and fix vulnerable patterns directly in the source code.
With Snyk Code, Jessica aims to shift security left, enabling developers to detect and remediate security issues early in the development lifecycle, reducing the risk of breaches and minimising the cost and effort required to address these issues in a later stage. To ensure a smooth rollout, Jessica plans to adopt a similar approach as before, starting with a pilot program involving a single project and expanding to other projects based on feedback and results.
Jessica knows it is evident that proprietary code, much like open-source components, has its security challenges. While leveraging proprietary code grants teams control and customisation, which is not always possible with open source, it introduces specific risks that must be managed with diligence and precision.
To help Jessica understand the gravity of security risks in proprietary code, let's see several factors contributing to their complexity and prevalence:
- Complex codebases: Unlike open-source projects, first-party codebases are often tailored to specific business needs, leading to highly complex and unique code structures. This complexity can obscure vulnerabilities, making them harder to detect and remediate. Each custom feature or optimisation might introduce security flaws that are not well-documented or understood outside the organisation.
- Insufficient security practises: Development teams may prioritise functionality and deadlines over security, especially in high-pressure environments. This can result in insufficient security practices during the coding phase, such as neglecting secure coding standards, inadequate code reviews, or skipping security testing altogether. Such oversights lay the groundwork for vulnerabilities that can remain undetected until exploited.
- Limited external scrutiny: First-party code doesn't benefit from the "many eyes" principle that supports open-source security through community contribution. The lack of external review means that security issues can go unnoticed for extended periods, increasing the risk of zero-day exploits.
- Dependency on third-party components: While first-party projects may control their core codebase, they often rely on third-party APIs, libraries, and tools, which can introduce vulnerabilities. These dependencies can become weak links in the security chain, exposing proprietary code to similar risks found in open-source ecosystems.
- Internal knowledge gaps: Teams may lack the necessary expertise in secure coding practices or be unaware of the latest security threats and mitigation strategies. This knowledge gap can lead to vulnerabilities in first-party code that are more about human error than technical complexity. Continuous education and training are essential to empower developers to write secure code.
Another New Feature in the Codebase
As the development team at Patch Corp continues to work on the Patch Chat App, they integrate advanced features to enhance the user experience and functionality. Leveraging the power of AI, specifically GitHub Copilot, the team accelerates the development process by generating code snippets for complex functionalities.
While GitHub Copilot significantly boosts productivity and assists in coding, Jessica knows that AI-generated code, like human-written code, can introduce security vulnerabilities.
The team has recently added a new feature to allow users to search for chat history in the Patch Chat App. This feature is developed using JavaScript with Node.js as the backend framework, enhancing the app's scalability and performance. However, Jessica suspects the rapid incorporation of AI-generated code snippets may have overlooked critical security checks.
The package.json file
{
"name": "patch-chat-app",
"version": "1.0.0",
"description": "A chat application.",
"main": "searchFeature.js",
"scripts": {
"start": "node searchFeature.js"
},
"dependencies": {
"express": "^4.17.1",
"body-parser": "^1.19.0",
"sqlite3": "^5.0.2"
}
}
The search-feature.js file
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
const { searchChatHistory } = require('./chat-controller');
app.post('/search', (req, res) => {
const { searchTerm } = req.body;
searchChatHistory(searchTerm).then(searchResults => {
res.json(searchResults);
}).catch(err => res.status(500).send('An error occurred'));
});
app.get('/display', (req, res) => {
const message = req.query.message;
res.send(`${message}`);
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
This code is a simple web server application developed using Express, a popular web application framework for Node.js. The application integrates several vital components and functionalities typical in web server development, such as handling HTTP requests, parsing request bodies, and defining routes.
The server defines two routes (/search and /display) to handle specific types of HTTP requests:
- POST /search: This route expects a POST request containing a JSON payload with a
searchTerm
. It uses thesearchTerm
to query chat history (the specifics of this operation are abstracted away in thesearchChatHistory
function imported from./chat-controller
). The search results are then sent back to the client in JSON format. If an error occurs during the search, a 500 status code and a generic error message are sent. - GET /display: This route handles GET requests and expects a query parameter named
message
. It sends a simple HTML response that displays the message content within a <div> element. This is a basic example of dynamically generating HTML content based on query parameters.
The chat-controller.js file
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database(':memory:');
db.serialize(() => {
db.run("CREATE TABLE chat_history (id INT, user TEXT, message TEXT)");
db.run("INSERT INTO chat_history (id, user, message) VALUES (1, 'user1', 'Hello world')");
});
function searchChatHistory(searchTerm) {
return new Promise((resolve, reject) => {
const query = `SELECT * FROM chat_history WHERE message LIKE '%${searchTerm}%'`;
db.all(query, [], (err, rows) => {
if (err) reject(err);
resolve(rows);
});
});
}
module.exports = { searchChatHistory };
This code demonstrates a simple application of SQLite, a lightweight disk-based database with Node.js for storing and querying chat history. The operation performed by this code involves setting up a SQLite database, creating a table, inserting a sample record, and defining a function to search through the chat history.
The db.serialize()
method ensures the database operations are sequenced. Inside this method, two SQL statements are executed:
- A
CREATE TABLE
statement to define a new table namedchat_history
with 3 columns (id
,user
, andmessage
). This table structure is intended to store chat messages, an identifier, and the user who sent each message. - An
INSERT INTO
statement to add a single record into thechat_history
table, simulating a chat message from user1 saying "Hello World". This is an initial data setup to facilitate testing or demonstration of the database's functionality. - The
searchChatHistory
function searches the chat history for messages containing a specific search term. It returns a promise, making it compatible with asynchronous operations. - The function constructs an SQL query using the
LIKE
operator to search for messages that contain thesearchTerm
within themessage
column of thechat_history
. - The
db.all
method executes the query. This method retrieves all rows that match the query and passes them to a callback function, which either resolves the promise with the retrieved rows (if the query is successful) or rejects the promise with an error (if the query fails).
Using the LIKE
operator and the wildcard %
allows for partial matching, meaning any messages containing the searchTerm
anywhere within its text will be returned.
Jessica decides to use Snyk Code to help her with this task.
Jessica had already set up Snyk at her company and had made sure that Snyk Code was enabled.
Note: If you've done the previous room - Snyk Open Source - you should already have a Snyk account and be able to proceed with scanning the vulnerable piece of code.
Sign Up and Log In
You can create a free Snyk account by signing up with your GitHub account at https://app.snyk.io/. This will integrate directly with the projects you are hosting on GitHub, and you can start scanning for vulnerabilities.
Import the Project
Note: You can clone the project to your own GitHub account from here.
Jessica clicks on Add projects in the top-right corner of the screen, then selects GitHub as the integration type.
She is looking for the repository to be added. She selects the repository and clicks on the green button at the top-right corner, Add selected repositories.
The project is now available on the dashboard. Jessica and the developers can see there are a few vulnerabilities.
Jessica will analyse the results in a second, but before that, she wants to ensure that the developers also have this visibility from their IDE.
Helping the Developers Have Visibility Into Their IDE
Note: If you've done the previous room - Snyk Open Source - you should already have a Snyk account and be able to proceed with scanning the vulnerable piece of code on your IDE as well.
Go to the extension tab on Visual Studio Code and type Snyk in the search bar. Install the extension and reload your IDE. A new icon should appear on the left sidebar. You will need to authenticate before starting a new scan. If you're not using VS Code as an IDE, you can have a look at the documentation to install it on another IDE.
Now that the development team has the integration set up, they have the same visibility on the vulnerabilities that are on the Snyk dashboard.
How many high-severity vulnerabilities are flagged on the search-feature.js file?
What are the two medium-severity vulnerabilities flagged on the search-feature.js file? (in alphabetical order)
Analyse Results From the Dashboard
Jessica is now looking at the results of the scan. She organises the results by severity level (high, medium, low), which allows her to prioritise critical issues first. She can explore individual entries to view additional details such as security information, an overview of the vulnerability, the affected files, and suggested remediations.
Jessica looks at the SQL injection vulnerability.
Jessica told the development team they could get more information on the vulnerability by clicking on the green button for the Full details. This gives them the entire data flow from the source to the sink.
Jessica starts by explaining that a SQL injection is a type of security vulnerability that happens when an attacker can insert or "inject" a malicious SQL query via the application input. She points to the searchChatHistory
function in their code, highlighting how it directly incorporates user input ('searchTerm') into a SQL query without validation or sanitisation.
She takes some time to explain the potential risks of this vulnerability:
- Data breach: Unauthorised access to user data, including private messages, user credentials, and personal information.
- Data loss or corruption: Malicious SQL commands could delete or corrupt the data, potentially disrupting the Patch Chat App's functionality.
- Unauthorised operations: Attackers could potentially escalate their access, allowing them to perform administrative operations on the database.
- Compromise of database server: In severe cases, SQL injection can compromise the underlying database server, affecting not just the application in question but also other applications sharing the same database.
To mitigate this vulnerability, Jessica suggests using parameterised queries or prepared statements to handle user input safely. Using this method, the database engine is aware of which parts of the query are code and which are data. It treats the data supplied by the user as a parameter value rather than executable code. This means attackers can't inject malicious SQL because the database engine will not execute these inputs as part of the SQL statement.
Note: If you want to learn more about SQL injection, you can do the Snyk Learn lesson on this vulnerability.
Analyse Results From the IDE
The developers asked for more details on another vulnerability - Cross-Site Scripting (XSS). This time, they are looking at the results from the IDE.
Jessica looks at the findings in the IDE. She begins by defining XSS as a security vulnerability that enables attackers to inject malicious scripts into web pages viewed by other users. She points to the /display
endpoint in search-feature.js, where the application directly incorporates user input ('message' query parameter) into the HTML content sent back to the user. She explains that this could allow an attacker to inject a script that will be executed in the context of the victim's browser version.
She continues by explaining the potential risks of this vulnerability:
- Stealing cookies/sessions: An attacker could steal tokens or cookies, allowing them to impersonate the victim.
- Phishing attacks: An attacker could trick users into divulging sensitive information by injecting fraudulent content.
- Defacement: An attacker could alter the web page's appearance, potentially harming the company's reputation.
To mitigate this vulnerability, Jessica recommends implementing input validation and sanitisation to check and clean user input before being reflected or incorporated into the DOM.
Note: If you want to learn more about XSS, you can do the Snyk Learn lesson on this vulnerability.
What is the CWE for SQL injection?
What is the unsanitised user input in the chat-controller.js file?
Advanced Remediation Strategies for Proprietary Code and AI-Generated Vulnerabilities
Jessica acknowledges with the developers the successful strategies applied in the past, emphasising that while the core principles of vulnerability management remain the same, the introduction of AI-generated code and the specificities of JavaScript demand an enhanced and nuanced approach to remediation.
AI-Generated Code Scrutiny
Given the reliance of the development team on GitHub Copilot for generating code snippets, Jessica stresses the importance of rigorous code reviews tailored to AI-generated output. She explains that while AI can significantly accelerate development, it lacks a seasoned developer's context and security awareness. She says particular attention should be given to reviewing AI-suggested code, especially for complex inputs and security-sensitive features.
She covers a few best practices for reviewing AI-generated code:
- Manual review: Encourage manual inspection of all AI-generated code, focusing on security-sensitive sections like input handling and data processing.
- Pair programming: Implement pair programming for AI-generated code sections, combining AI efficiency with human security insight.
- Security testing integration: Integrate automated security testing tools like Snyk Code, ensuring immediate feedback on potential vulnerabilities introduced by AI.
Strategic Vulnerability Prioritisation
Jessica reiterates the importance of addressing high-severity vulnerabilities first and highlights the nuances of prioritising vulnerabilities in JavaScript applications. She discusses the dynamic nature of JavaScript and how particular vulnerabilities, even if rated lower in severity, can have amplified impacts due to the language's widespread use in client-side applications.
- Business logic considerations: Prioritise vulnerabilities directly affecting business-critical features or exposing sensitive user data, even if their CVSS scores are not the highest.
- User input handling: Given the interactive nature of the Patch Chat App, particular emphasis is placed on sanitising and validating all user inputs, prioritising remediation efforts for vulnerabilities that exploit these inputs.
On the dashboard, Jessica shows the developers they can find some guidance to fix the vulnerability if they click on Fix analysis.
With the development team, Jessica applied a fix to the code in the /display
endpoint. The team transitioned from directly inserting user input into the HTML response via res.send
to using res.render
to serve user input through template rendering.
In the original code, the application used res.send(${message})
to directly include user-provided input (message
query parameter) in the HTML content sent back to the client. This approach is unsafe because it doesn't sanitise the input, allowing an attacker to inject malicious scripts by crafting a specially designed message
value. When the response is rendered in the user's browser, the injected script executes, leading to XSS attacks.
app.get('/display', (req, res) => {
const message = req.query.message;
res.render('display', { message });
});
In the updated code, the developers use Express's res.render
method to serve the message
to the user. This method renders a view (template) and sends the rendered HTML string to the client. User input can be automatically escaped using a templating engine (such as EJS, Handlebars, or Pug), which Express supports. This means that special characters in the 'message' variable, which could otherwise be interpreted as part of HTML or JavaScript, are converted to their entity equivalents (e.g., <
becomes < and >
becomes >). This escaping prevents any malicious scripts from being executed in the browser.
Jessica insists that while the use of res.render
with automatic escaping significantly reduces the risk of XSS attacks, it's part of a broader strategy of input validation and sanitisation. It's important for developers not solely to rely on templating engines for security but also to validate and sanitise all user input, especially in applications where user-generated content might be presented to other users.
The developers asked Jessica how they could fix the SQL injection vulnerability. They look at the dashboard for guidance on how to fix this vulnerability.
To fix this vulnerability in the chat-controller.js file, the developers must modify the code to use parameterised queries. Parameterised queries ensure that user input is treated strictly as data, not as part of the SQL command, and prevent attackers from altering the SQL statement's logic.
function searchChatHistory(searchTerm) {
return new Promise((resolve, reject) => {
const query = `SELECT * FROM chat_history WHERE message LIKE ?`;
db.all(query, [`%${searchTerm}%`], (err, rows) => {
if (err) reject(err);
resolve(rows);
});
});
}
This updated code uses parameterised queries with the SQLite database. This approach eliminates the vulnerability by ensuring user input cannot interfere with the query structure. The db.all
method is called with ?
as a placeholder in the SQL query, and the actual input (searchTerm
) is passed as an argument in an array. This ensures the SQLite driver safely handles searchTerm
, which properly escapes any potentially harmful characters.
The search-feature.js file does not need modifications for the SQL injection fix because the vulnerability and its mitigation are handled within the database interaction logic in chat-controller.js.
Jessica reminds the developers that beyond parameterisation, validating and sanitising user inputs can further reduce SQL injection risks and that the database connection used by the application should operate with the least privilege necessary.
Merging Changes and Pushing Remediations to Production
Jessica and the team realised that a new vulnerability appeared! The phenomenon where addressing one security vulnerability inadvertently introduces another is not uncommon in software development. This shows software systems' complex and interdependent nature, where changes in one area can have unforeseen impacts elsewhere. When the developers fixed the XSS vulnerability by transitioning from direct HTML content generation to template rendering, they might have introduced a new vulnerability related to the Allocation of resources without limits or throttling. This new vulnerability could manifest as the application failing to impose limits on the amount or size of resources that can be requested by a user, potentially leading to resource exhaustion and Denial of Service attacks.
Jessica proposes to implement comprehensive testing strategies that include security, performance, and usability testing whenever changes are made to the codebase. Automated security testing tools and manual code reviews should be part of the development lifecycle to catch new vulnerabilities early. Developers should make incremental changes and conduct thorough code reviews, especially when fixing vulnerabilities. Peer reviews can help identify potential issues introduced by a fix before merging the code into the main codebase.
When a fix introduces a new vulnerability, Jessica and the development team must treat it as a learning opportunity. They can improve their processes and prevent similar issues by analysing how the vulnerability was introduced. It's also essential for the team to prioritise the newly introduced vulnerability based on its severity and the risk it poses to the application and its users, ensuring that medium-severity vulnerabilities like resource allocation issues are effectively addressed.
Before pushing to production, the development team fixed the three medium vulnerabilities and moved to their development environment to ensure all the checks were green in their CI/CD pipeline.
If you want to understand more about the different steps involved in a software development lifecycle, we recommend doing the SDLC room.
Which Express method is used to fix the XSS vulnerability in the code snippet?
What is the updated code in the code snippet using to fix the SQL injection?
Jessica, in the previous room, implemented Snyk Open Source into their CI/CD pipeline using CircleCI Orbs and GitHub Actions. She also implemented ChatOps on Slack to improve visibility. You can review it here in task #7, Automating the process through CI/CD pipeline, and task #8, Implementing Continuous Monitoring.
Evolving CI/CD Integration Practices for Enhanced Security
Jessica reiterates the importance of integrating security scanning tools like Snyk into their CI/CD pipeline, as established with Snyk Open Source, to automate and streamline vulnerability detection and remediation. She highlights that with the introduction of Snyk Code, the team is now equipped to address a broader spectrum of vulnerabilities, including those specific to proprietary code and AI-generated content.
Advanced Integration Techniques
Jessica thought about how she could improve the existing CI/CD pipeline. She emphasises the addition of Snyk Code scans alongside Snyk Open Source in their CI/CD workflows to cover both open-source and proprietary codebases. This dual-layered approach ensures a more comprehensive vulnerability detection. She also discusses with the development team the implementation of security guardrails at various stages of the CI/CD pipeline, which checks for vulnerabilities and enforces coding standards and security policies specific to proprietary code and AI-generated snippets.
Continuous Monitoring, Beyond Basics
Jessica wants to address the need for specialised monitoring strategies for AI-generated code, recognising its unique challenges and potential for introducing unexpected vulnerabilities. She is also advocating for revisiting the alert thresholds and notification channels. She wants to discuss the customisation of alerting mechanisms to accommodate the nuances of detecting vulnerabilities in proprietary and AI-generated code.
Expanding Collaboration and Communication
Jessica wants to revisit the Slack integration while maintaining its use for alerts and monitoring and introduce advanced practices for managing alerts related to new vulnerability types. The development team encourages the use of dedicated channels for automated insights and recommendations on remediation strategies specific to the vulnerabilities identified by Snyk Code. Jessica stresses the importance of having closer collaboration between developers and security teams.
Note: If you want to learn more about CI/CD and build security, we recommend the CI/CD and Build Security room.
Establishing sensible alert thresholds in continuous monitoring practices involves considering the severity, frequency, and rate of change of vulnerabilities. (y/n)
Refining Security Policies for Proprietary and AI-Generated Code
Jessica's efforts to expand the policy scope to include proprietary codebases and AI-generated code segments emphasise the need for specialised security practices. She will introduce specific guidelines for reviewing and securing AI-generated code, highlighting the importance of human oversight in AI-assisted development processes. She outlines that incorporating Snyk Code into the development workflow will ensure that first-party code is regularly scanned and vulnerabilities are managed according to the policy.
Educational Initiatives Tailored to Snyk Code
Jessica will spend time developing onboarding modules focused on securing first-party code and effectively using Snyk Code for continuous security assessment. She will also organise workshops addressing security considerations for AI-generated code, providing developers with the tools and knowledge to identify and mitigate potential vulnerabilities AI introduces. She wants to create learning paths covering open-source and proprietary code security to ensure the development teams are well-versed in the unique challenges each presents and how Snyk tools can be leveraged for vulnerability management.
Enhancing Collaboration Across Teams
Jessica wants to establish cross-functional security forums where developers, security team members, and AI specialists from their data team can share insights, discuss new security trends, and collaborate on best practices for integrating Snyk Code into their projects. Jessica also would like to expand the Security Champions program to include champions from teams working on AI-driven development projects, ensuring that security best practices are embedded in all development lifecycle phases, including the integration and review of AI-generated code.
Note: If you want to learn more about the Security Champions program, you can refer to the OWASP Security Champions Playbook.
Setting the Stage for Continuous Improvement
Jessica wants to implement a structured schedule for reviewing and updating security policies and practices, considering the evolving landscape of proprietary and AI-generated code security. She also wants to encourage ongoing feedback from all stakeholders involved in the development process, using insights gained to refine security practices and educational initiatives continuously.
Measuring the Impact of Security Tool Implementation
Jessica knows that implementing security tools in the SDLC is a proactive measure against potential threats and a strategic investment in the organisation's operational integrity and reputation. Measuring the return on investment (ROI) of such implementations provides tangible data to validate these security measures' efficacy and financial impact.
She needs to consider both direct and indirect benefits. Direct benefits can be quantified by reducing the number of security incidents, decreasing downtime, and saving money by avoiding potential breaches. While more challenging to quantify, indirect benefits include enhanced compliance posture, improved customer trust, and a more robust security culture.
Jessica's CISO shared with her a calculation framework:
- Cost reduction: Jessica starts by calculating the costs associated with security incidents before and after the implementation of the security tool, including the cost of remediation, potential fines, and downtime. The difference represents the direct financial savings.
- Efficiency gains: She measures the time saved by developers and the security team in identifying and addressing vulnerabilities through automated scans and integrations into CI/CD pipelines. She translates time savings into cost savings by considering the average hourly rate of the involved developer.
- Preventive savings: She estimates the costs of potential security breaches that were avoided due to proactive vulnerability management. Industry averages and historical data can provide a baseline for possible losses.
- Intangible benefits: She attempts to assign value to intangible benefits such as improved reputation customer satisfaction, and regulatory compliance. Surveys, markets studies, and benchmarking against industry standards can offer insights into these areas.
- ROI formula: She can calculate the ROI using the following formula. "Total Benefits" includes direct and indirect benefits, and "Total Costs" encompasses the investment in the security tool and related operational costs.
Jessica knows that by evaluating the saved costs, efficiencies gained, and potential breaches avoided, Patch Corp can clearly understand the ROI from implementing security tools. It justifies the investment and underscores the value of integrating security practices deeply within the software development process.
What's Next for You
Now that you've gained some experience setting up Snyk Code on one project, you can use it on your side projects or even secure open-source projects!
Which OWASP framework serves as a guide to strengthen the relationship between development teams and information security teams?
Well done Jessica, she did it again!
Created by
Room Type
Free Room. Anyone can deploy virtual machines in the room (without being subscribed)!
Users in Room
4,390
Created
408 days ago
Ready to learn Cyber Security? Create your free account today!
TryHackMe provides free online cyber security training to secure jobs & upskill through a fun, interactive learning environment.
Already have an account? Log in