On Flask, Semgrep, and Secure Coding

Michael Hidalgo
5 min readSep 4, 2021

--

First steps with Flask and Static Code Analysis with Semgrep

Photo by Gary Butterfield on Unsplash

This week I spent a bit of time learning something new. Flask has been on my list of things to learn for a while. So the time came and I attended some of the Pluralsight courses on this topic.

One of the characteristics I’ve heard from Flask has to do with its simplicity and power; so I decided to give it a try at the time I attended the training tutorial.

For the sake of clarification, I’m attending the Building Web Applications with Flask path at Pluralsight.

Writing the first lines of code

Dennis Ritchie in the K&R book stated that “The only way to learn a new programming language is by writing programs in it”.

Here is the first simple and “innocent” application:

Insecure Flask application

Given a safe input, the application runs fine:

Running the application from the terminal

And when visiting the print endpoint, it returns the expected output:

Flask application returning a dynamic string

What is wrong with this code?

Writing insecure code is easy. You don’t need to be an expert to understand that this code is vulnerable. First, it receives an input from an untrusted source, and it concatenates to the output string that is rendered in the browser.

The name of the parameter name, which is sent via HTTP GET, is stored in a variable called name and then processed in the output, all of this without any sort of input data validation, sanitization or output encoding.

name = request.args.get('name')return f'Hello {name}!,  Welcome to Flask'

Since this is the case, we can easily exploit a Cross-Site Scripting (XSS) vulnerability here:

XSS in Flask application

How to perform input sanitization in Flask?

Flask provides a method called escape which essentially performs output encoding of the problematic HTML entities &, <, >, ‘, and ” and returns HTML safe entities instead.

As per Flask’s documentation:

Flask escape documentation

We can add the escape function to our input as follows:

name = escape(request.args.get('name'))
Using Flask escape function

And if we attempt to run our XSS payload, this time it is not exploited

XSS attempt

Flask and Static Code Analysis

I’m a big fan of Semgrep, a fast, open-source, static analysis tool. However, I haven’t had the chance, until now, to play with it and understand its power. So let’s see if we can write some detection rules for our vulnerable code.

The open community (r2c in particular) already provides ~18 out-of-the-box Flask security rules that can be used in your project. Interestingly, those rules did not find a security issue in my code:

Semgrep rules for Flasy by r2c

However, this rule does not identify a vulnerability in the code, we can trigger the flask ruleset by running:

semgrep --config "p/flask"

As displayed below, there are 0 findings

Flask’s semgrep rules

Let’s create our own rule

Semgrep’s documentation is great and with their live playground you can come up with your own rules very quickly.

Our first version of the rule tries to map those cases in which HTTP GET parameters are being used in the back end (in the case of Flask it is via the requests.args.get).

So our rule definition looks like this:

rules:- id: my_pattern_idpatterns:- pattern: flask.request.args.get(...)message: Flask, Improper input data validation!languages: [python]severity: ERROR

If we want to evaluate our rule, we can save the above rule in a YML format and then run it like this:

semgrep --config semgrep-rule.yml

And there you have it, our first semgrep rule in action showing an error in red with the description of our rule in action.

semgrep rule in action

Dealing with False Positives

There is an issue with this rule because if we apply our escaping function, it still triggers the detection:

semgrep rule still triggers

The good thing is that semgrep is very powerful and we can add multiple patterns in the rule definition. It looks like we can use the pattern-not-inside operator here to address the issue. According to the documentation:

So let’s modify our rule to ignore the detection of the previous pattern is surrounded (is inside) the escape function

rules:- id: my_pattern_idpatterns:- pattern: flask.request.args.get(...)- pattern-not-inside: escape(...)message: Flask, Improper input data validation!languages: [python]severity: ERROR

As you can see, this time the rule does not trigger:

Semgrep rule multiple patterns

Conclusion

This is a very simple use case of writing insecure code and how to write a trivial semgrep rule for detecting it within Flask.

Semgrep is very powerful and certainly, you can have cases much more complicated than this one. However, with this trivial example, we can see the capabilities of the tool, which provides an interesting ecosystem for static analysis, research, and automation.

I’ll be doing a dive on semgrep and try to come up with a more sophisticated rule pack that can be used to detect multiple vulnerabilities in an advanced fashion.

--

--

Michael Hidalgo
Michael Hidalgo

Written by Michael Hidalgo

Michael is Software and Application Security Engineer focused on Cybersecurity, Web Application Security, Research and Development. Based in Dublin, Ireland

No responses yet