/mar 26, 2024

Resolving Simple Cross-Site Scripting Flaws with Veracode Fix

By Robert Haynes

In the last blog on fixing vulnerabilities with Veracode Fix, we looked at SQL Injection remediation in a Java application. Since then, we have released Fix support for Python (and PHP) and launched a new VS Code plugin that includes support for Fix.

It seems appropriate, therefore, to look at resolving a problem in a Python app using Veracode Fix in the VS Code IDE. This time let’s examine a simple cross-site scripting (XSS) weakness.

What is an XSS Vulnerability?

An XSS vulnerability occurs when an attacker injects malicious code into a trusted website, which is then executed by unsuspecting users. This can lead to unauthorized access, data theft, or manipulation of user sessions. XSS vulnerabilities are commonly found in input fields, comments sections, or poorly validated user-generated content.  A simple demonstration example is often to enter the following text in a user input field:

<script>alert('XSS Attack!');</script>

If an application does not sanitize this input and later displays it in an HTML response, the browser will interpret the output as code and display a dialog box with the ‘XSS Attack’ message. Although this example is harmless, it’s obviously a way to do something far more dangerous.  

Although simple XSS vulnerabilities are generally easy to resolve, XSS weaknesses are still amongst the most common problems detected by Veracode’s Static Analysis tool.

If you’re a Veracode customer and you’d like to follow along, take a look at this blog where we introduce Veracode Scan for VS Code, or check out the documentation.

Generating (Deliberately) Vulnerable Code with AI

There are plenty of deliberately (and accidentally) vulnerable web applications available. For this simple example, however, it was just as easy (and more entertaining) to use a couple of AI services to generate deliberately vulnerable code.

Since the author is not yet much of a prompt engineer, the first task is to use ChatGPT to generate a good prompt for GitHub Copilot to use:

Acting as a prompt engineer, create an optimized prompt for GitHub copilot to create a deliberately vulnerable python app using flask to demonstrate a cross-site scripting vulnerability.

Here’s the optimized prompt:

Create a simple Python web application using Flask that demonstrates a cross-site scripting (XSS) vulnerability. The application should have a single route ("/") that renders an HTML form with a text input field. Upon submitting the form, the input value should be reflected back to the user on the same page.

The goal is to demonstrate a potential XSS vulnerability where user-supplied input is not properly sanitized or escaped before being rendered back to the user. This could allow an attacker to inject malicious JavaScript code that gets executed in the context of other users' browsers when they visit the page.

Your task is to generate code that achieves the following:

1. Set up a Flask application with a single route ("/") that renders an HTML form.

2. The form should have a text input field.

3. When the form is submitted, the input value should be echoed back to the user on the same page.

4. Ensure that the input value is not properly sanitized or escaped, leaving the application vulnerable to XSS attacks.

5. Add comments to explain the purpose of each part of the code.

Generate code snippets that demonstrate the vulnerable parts of the application, including where user input is accepted and where it is reflected back to the user without proper sanitization.

Bonus: Provide a short description of how an attacker could exploit this XSS vulnerability to execute arbitrary JavaScript code in the context of other users' browsers.

Next, we’re going to feed that into Copilot in our VS Code IDE and generate an app:

from flask import Flask, request, render_template_string



app = Flask(__name__)



@app.route('/', methods=['GET', 'POST'])

def index():

    if request.method == 'POST':

        # Retrieve the user input from the form

        user_input = request.form.get('input')



        # Render the template with the user input

        return render_template_string('<h1>Input: {{ user_input }}</h1>', user_input=user_input)



    # Render the HTML form

    return '''

        <form method="POST">

            <input type="text" name="input">

            <input type="submit" value="Submit">

        </form>

    '''



if __name__ == '__main__':

    app.run()

You can see from the code that we are just returning the user input from the form without any sanitization, making it an easy target for an XSS attack:

return render_template_string('<h1>Input: {{ user_input }}</h1>', user_input=user_input)

To make it look a little more like a project the following directory structure was used:

pyxss

├── requirements.txt
└── src
    ├── templates
    │   └── index.html
    └── xss.py

(The "templates->index.html"  directory and file are just an artifact from a previous project, but might be useful later if we build this demo app out.)

Next, we need to set up our directory as a git repository with git init, git add, and git commit. 

Scanning the Code and Resolving Flaws with Veracode Fix 

Now we’re ready to scan the project using Veracode Scan, by clicking on the Veracode Plugin icon, then hitting the ‘Start Scanning’ button. In the background, the agent is packaging and uploading the deployment artifact to the Veracode platform, which performs a static scan and returns the results:

VS Code with Veracode Scan

Reassuringly, there is only one discovered flaw and it’s CWE-80, which is a basic XSS weakness.   

Scan results

You will notice the blue dot at the beginning of the finding line, indicating that there might be a Veracode Fix suggestion available. To activate the Fix, click on the ⓘ symbol at the end of the line:

Veracode Fix in Vs Code

Fix offers us the following suggestion as option 1 (of 4):

+from html import escape
…
- return render_template_string('<h1>Input: {{ user_input }}</h1>', user_input=user_input)

+ return render_template_string(escape('<h1>Input: {{ user_input }}</h1>'), user_input=user_input)

All that has been changed is to add an escape() function around the returned content, but this will be sufficient to protect against a lot of XSS attacks as the Python HTML escape function converts special HTML characters into their corresponding HTML entities, preventing XSS attacks by ensuring safe rendering of user-provided text in HTML documents. In addition, the correct import line has been added to make the escape function available.

This looks good, so now all we need to do is to hit the ‘Apply Fix’ button to add this to our code, and hit ‘Rescan’.  Unfortunately, this change generates a new flaw -  CWE-1336 (server-side template injection). Rather than spend too much time researching this flaw, let’s revert the change, rescan, and look at the other Fix suggestions.

Option 3, which shifts the escape() function to the whole returned string, not just the part that came from the user looks like a good resolution:

+  return escape(render_template_string('<h1>Input: {{ user_input }}</h1>', user_input=user_input))

Applying this fix and rescanning gives us the happy result of no flaws found!

Conclusion

In this case, the first Fix suggestion did not produce a perfect result, but one of the other suggestions gave us the result we needed to successfully pass a Veracode Static Scan, and, more importantly, to secure our app.

If you’re interested in how Veracode Fix can help you resolve flaws faster, why not contact us, or request a personalized demo.

 

 

Related Posts

By Robert Haynes

Robert’s quarter-century working in IT has progressed (or is that regressed?) through helpdesk, UNIX sysadmin, backup, storage, application security,  technical sales, and marketing.  He now spends his time hanging out at the intersection of artificial intelligence and human ingenuity, waving a sign that says: “This way for secure software."