Python

A1- Injections

SQL Injection

  • Escaping all user-supplied input.
  • Performing whitelist input validation as a secondary defence.
  • Use of prepared statements (with parameterized queries).
  • Use of database ORMs is generally safe as most implementations rely on prepared statements.
  • If using SQLAlchemy, upgrade sqlalchemy to version 1.2.18 or higher.

When using SQLAlchemy module as ORM

Noncompliant Code

  • Flask application
stmt = text("SELECT * FROM users where id=%s" % id) # Query is constructed based on user inputs
query = SQLAlchemy().session.query(User).from_statement(stmt) # Noncompliant
user = query.one()

Compliant Code

  • Flask application

stmt = text("SELECT * FROM users where id=:id")
query = SQLAlchemy().session.query(User).from_statement(stmt).params(id=id) # Compliant
user = query.one()

When using Django ORM

Noncompliant Code

  • Django application
from django.db import connection

cursor = connection.cursor()
cursor.execute("SELECT username FROM auth_user WHERE id=%s" % id) # Noncompliant; Query is constructed based on user inputs
row = cursor.fetchone()

Compliant Code

  • Django application
from django.db import connection

cursor = connection.cursor()
cursor.execute("SELECT username FROM auth_user WHERE id=:id", {"id": id}) # Compliant
row = cursor.fetchone()
return HttpResponse("Hello %s" % row[0])

When using native SQLite3 database driver (sqlite3) for python

Noncompliant Code

cursor.execute("SELECT admin FROM users WHERE username = '" + username + '");
cursor.execute("SELECT admin FROM users WHERE username = '%s' % username);
cursor.execute("SELECT admin FROM users WHERE username = '{}'".format(username));
cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'");

Compliant Code

cursor.execute("SELECT admin FROM users WHERE username = ?", (username, ));
cursor.execute("SELECT admin FROM users WHERE username = :username", {'username': username});

When using native MySQL driver(mysql)/ Postgresql driver (psycopg2) for python

Noncompliant Code

cursor.execute("SELECT admin FROM users WHERE username = '" + username + '");
cursor.execute("SELECT admin FROM users WHERE username = '%s' % username);
cursor.execute("SELECT admin FROM users WHERE username = '{}'".format(username));
cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'");

Compliant Code

cursor.execute("SELECT admin FROM users WHERE username = %s", (username, ));
cursor.execute("SELECT admin FROM users WHERE username = %(username)s", {'username': username});

When using OracleDB native driver (cx_Oracle) for python

Noncompliant Code

cursor.execute("SELECT admin FROM users WHERE username = '" + username + '");
cursor.execute("SELECT admin FROM users WHERE username = '%s' % username);
cursor.execute("SELECT admin FROM users WHERE username = '{}'".format(username));
cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'");

Compliant Code

cursor.execute("SELECT admin FROM users WHERE username = :username", {'username': username});
cursor.execute("SELECT admin FROM users WHERE username = :username", (username,));

When using MSSQL native driver (pymssql) for python

Noncompliant Code

cursor.execute("SELECT admin FROM users WHERE username = '" + username + '");
cursor.execute("SELECT admin FROM users WHERE username = '%s' % username);
cursor.execute("SELECT admin FROM users WHERE username = '{}'".format(username));
cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'");

Compliant Code

cursor.execute("SELECT admin FROM users WHERE username = %s", (username, ));

NOSQL Injection

When using MongoDB native module (pymongo) in python

  • When dealing with MongoDB database with its native driver (pymongo) don’t let user-supplied input to control $where operator or map_reduce option without sanitizing user input and converting or banning use of special characters like (& | ; $ > < ` \ ! ' “).
  • Never parse the user-supplied string to a JSON object and use it do MongoDB operations.

Noncompliant Code

client = MongoClient()
db = client.test_database
collection = db.test
from flask import request
import json

@route.app("/getwhere")
def getWhere():
# Get data from fields
condition = json.load("{ name: "+request.form.getvalue('name'))
if condition:
output =collection.find(condition) # Non compliant inserting of user supplied input
else:
output = ""
return output

Compliant Code

client = MongoClient()
db = client.test_database
collection = db.test

@route.app("/getwhere")
def getWhere():
# Get data from fields
condition = request.form.getvalue('name')
if condition:
output = collection.find({"name": condition } # compliant
else:
output = ""

return output

OS Command Injection

  • Avoid calling OS commands directly
  • Escape values added to OS commands specific to each OS
  • Sanitized untrusted user input (& | ; $ > < ` \ !) properly
  • Parameterization in conjunction with Input Validation
  • Use structured mechanisms that automatically enforce the separation between data and command.

When using python os module for running system commands

Noncompliant Code

  • os
import os

cmd = "ping -c 1 %s" % address
os.popen(cmd) # Noncompliant

Compliant Code

  • os
import os
import shlex

address = shlex.quote("address") # address argument is shell-escaped
cmd = "ping -c 1 %s" % address
os.popen(cmd ) # Compliant

When using the python subprocess module for running system commands

Noncompliant Code

  • subprocess
import subprocess

cmd = "ping -c 1 %s" % address
subprocess.Popen(cmd, shell=True) # Noncompliant; using shell=true is unsafe

Compliant Code

  • subprocess
import subprocess

args = ["ping", "-c1", address]
subprocess.Popen(args) # Compliant

Server-Side Template Injection

When using Jinja2 or Mako with flask framework

  • Never use render_template_string() to render a string containing template which was supplied with the user-supplied input without proper filtering.
  • Whatever operations and tags to be used should be hardcoded in the separate template file and rendered by render_template().
  • All the user-supplied inputs should be properly escaped using escape().

Noncompliant Code

@app.errorhandler(404)
def page_not_found(e):

template = '''{ extends "layout.html" }
{ block body }
<div class="center-content error">
<h1>Oops! That page doesn't exist.</h1>
<h3>%s</h3>
</div>
{ endblock }
''' % (request.url)

return render_template_string(template), 404

Compliant code

* templates/404.html

{ extends "layout.html" }
{ block body }
<div class="center-content error">
<h1>Oops! That page doesn't exist.</h1>
<h3>{{url}}</h3>
</div>
{ endblock }

* app.py

from flask import escape
@app.errorhandler(404)
def page_not_found(e):

return render_template('404.html', url=escape(request.url)), 404

  • Enable auto-escaping by default and continue to review the use of inputs to be sure that the chosen auto-escaping strategy is the right one.

Noncompliant Code

from jinja2 import Environment

env = Environment() # Sensitive: New Jinja2 Environment has autoescape set to false
env = Environment(autoescape=False) # Noncompliant

Compliant Code

from jinja2 import Environment
env = Environment(autoescape=True) # Compliant

When using Django Template

  • String templates should not be passed with user-supplied input directly as a format string and rendered.
  • Use Context() to handle user-supplied variables which need to be inserted into template strings.

Noncompliant Code

from django.template import Template

template = Template("My name is {{ %my_name }}."% userInput)

template.render(template)

Compliant code

from django.template import Context, Template

template = Template("My name is {{ %my_name }})
context = Context({"my_name": userInput})
template.render(context)

XPath Injection

  • Treat all user input as untrusted, and perform appropriate sanitization.
  • When sanitizing user input, verify the correctness of the data type, length, format, and content. For example, use a regular expression that checks for XML tags and special characters in user input. This practice corresponds to input sanitization.
  • In a client-server application, perform validation at both the client and the server sides.
  • Extensively test applications that supply, propagate, or accept user input.
  • Use lxml module instead of xml.etree.ElementTree.

Noncompliant Code

from flask import request
import xml.etree.ElementTree as ET

tree = ET.parse('users.xml')
root = tree.getroot()

@app.route('/user')
def user_location():
username = request.args['username']
query = "./users/user/[@name='"+username+"']/location"
elmts = root.findall(query) # Noncompliant
return 'Location %s' % list(elmts)

Compliant Code

from flask import request
from lxml import etree

parser = etree.XMLParser(resolve_entities=False)
tree = etree.parse('users.xml', parser)
root = tree.getroot()

@app.route('/user')
def user_location():
username = request.args['username']
query = "/collection/users/user[@name = $paramname]/location/text()"
elmts = root.xpath(query, paramname = username)
return 'Location %s' % list(elmts)

LDAP Injection

  • The special characters “''', ‘#’, ‘"’, ‘+’, ‘,’, ‘;’, ‘<’, ‘>’, ‘','*’, ‘(’, ‘)'” and null must be escaped.
  • Use Frameworks that Automatically Protect from LDAP Injection
  • Minimize the privileges assigned to the LDAP binding account in your environment.
  • Input validation can be used to detect unauthorized input before it is passed to the LDAP query.

Noncompliant Code

from flask import request
import ldap

@app.route("/user")
def user():
dn = request.args['dn']
username = request.args['username']

search_filter = "(&(objectClass=*)(uid="+username+"))"
ldap_connection = ldap.initialize("ldap://127.0.0.1:389")
user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) # Noncompliant
return user[0]

Compliant Code

from flask import request
import ldap
import ldap.filter
import ldap.dn

@app.route("/user")
def user():
dn = "dc=%s" % ldap.dn.escape_dn_chars(request.args['dc']) # Escape distinguished names special characters
username = ldap.filter.escape_filter_chars(request.args['username']) # Escape search filters special characters

search_filter = "(&(objectClass=*)(uid="+username+"))"
ldap_connection = ldap.initialize("ldap://127.0.0.1:389")
user = ldap_connection.search_s(dn, ldap.SCOPE_SUBTREE, search_filter) # Compliant
return user[0]

Log Injection

  • Filter the user input used to prevent injection of Carriage Return (CR) or Line Feed (LF) characters.
  • Limit the size of the user input value used to create the log message.
  • Encode to proper context, or sanitize user input
  • Make sure all XSS defences are applied when viewing log files in a web browser.

Noncompliant Code

from flask import request, current_app
import logging

@app.route('/log')
def log():
input = request.args.get('input')
current_app.logger.error("%s", input) # Noncompliant

Compliant Code

from flask import request, current_app
import logging

@app.route('/log')
def log():
input = request.args.get('input')
if input.isalnum():
current_app.logger.error("%s", input) # Compliant

A2- Broken Authentication & Session Management

Broken Authentication

  • Where possible, implement multi-factor authentication to prevent automated, credential stuffing, brute force, and stolen credential re-use attacks.
  • Ensure registration, credential recovery, and API pathways are hardened against account enumeration attacks by using the same messages for all outcomes.
  • Limit or increasingly delay failed login attempts. Log all failures and alert administrators when credential stuffing, brute force, or other attacks are detected.
  • Minimum passwords length should be at least eight (8) characters long. Combining this length with complexity makes a password difficult to guess and/or brute force.
  • Do not ship or deploy with any default credentials, particularly for admin users.
  • The application may should different HTTP Error code depending on the authentication attempt response.
  • Use a side-channel to communicate the method to reset their password.
  • Ensure that generated tokens or codes in forgot/reset password are:
  • Randomly generated using a cryptographically safe algorithm.
  • Sufficiently long to protect against brute-force attacks.
  • Stored securely.
  • Single-use and expire after an appropriate period.
  • A stronger password can be enforced using the regex below, which requires at least 8 character password with numbers and both lowercase and uppercase letters.
var PASS_RE =/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/;

Session Management

  • Use a server-side, secure, built-in session manager that generates a new random session ID with high entropy after login.
  • Session IDs should not be exposed in the URL.
  • Session IDs should timeout. User sessions or authentication tokens should get properly invalidated during logout.
  • Passwords, session IDs, and other credentials should not be sent over unencrypted connections.
  • Session IDs should never be under the control of users and clients to create. They should all be generated, controlled, and secured centrally by the authentication and authorisation mechanism.
  • Session IDs should not be stored between browser sessions. They should be destroyed when a browser is closed. This prevents an attacker from retrieving a valid session by using the history in a Browser or by finding the Session ID on the client’s local storage.

A3- Sensitive Data Exposure

  • Classify data processed, stored or transmitted by an application. Identify which data is sensitive according to privacy laws, regulatory requirements, or business needs.
  • Apply controls as per the classification.
  • Don’t store sensitive data unnecessarily. Discard it as soon as possible or use PCI DSS compliant tokenization or even truncation. Data that is not retained cannot be stolen.
  • Ensure up-to-date and strong standard algorithms, protocols, and keys are in place; use proper key management.
  • Encrypt all data in transit with a secure protocol such as TLS with perfect forward secrecy (PFS) ciphers, cipher prioritization by the server, and security parameters. Enforce encryption using directives like HTTP Strict Transport Security (HSTS).
  • Disable caching for the response that contains sensitive data.
  • Store passwords using strong adaptive and salted hashing functions with a work factor (delay factor), such as Argon2, scrypt, bcrypt or PBKDF2.
  • Verify independently the effectiveness of configuration and settings.
  • Make sure to encrypt all sensitive data at rest.

A4- XML External Entities (XXE)

  • Disable XML external entity and DTD processing in all XML parsers in the application.
  • Implement positive (“whitelisting”) server-side input validation, filtering, or sanitization to prevent hostile data within XML documents, headers, or nodes.
  • Verify that XML or XSL file upload functionality validates incoming XML using XSD validation or similar.
  • Patch or upgrade all XML processors and libraries in use by the application or on the underlying operating system
  • Use XML parsers such as Etree, Minidom, Xmlrpc, and Genshi through defusedxml.

Noncompliant Code

from xml.dom import pulldom

data = pulldom.parse('rq.xml')

Compliant Code

from defusedxml import pulldom

data = parse('rq.xml')

A5- Broken Access Control

  • Model access controls should enforce record ownership, rather than accepting that the user can create, read, update, or delete any record.
  • Unique application business limit requirements should be enforced by domain models.
  • Disable webserver directory listing and ensure file metadata (e.g. .git) and backup files are not present within web roots.
  • Log access control failures, alert admins when appropriate (e.g. repeated failures).
  • Rate limit API and controller access to minimize the harm from automated attack tooling.
  • JWT tokens should be invalidated on the server after logout.
  • When a user changes their role to another one, the administrator must make sure that the earlier access is revoked such that at any given point of time, a user is assigned to only those roles on a need to know basis.
  • Access decisions should be made by checking if the current user has the permission associated with the requested application action.
  • The authentication mechanism should deny all access by default, and provide access to specific roles for every function.
  • In a workflow-based application, verify the users’ state before allowing them to access any resources.
  • Developers should use only one user or session for indirect object references.
  • It is also recommended to check the access before using a direct object reference from an untrusted source.

A6- Security Misconfiguration

  • A minimal platform without any unnecessary features, components, documentation, and samples. Remove or do not install unused features and frameworks.
  • A task to review and update the configurations appropriate to all security notes, updates and patches as part of the patch management process.
  • An automated process to verify the effectiveness of the configurations and settings in all environments.
  • Ensure that a strong application architecture is being adopted that provides effective, secure separation between components.
  • Review default in HTTP Response headers to prevent internal implementation disclosure.
  • Use generic session cookie names

A7- Cross-Site Scripting (XSS)

  • Validate user-provided data based on a whitelist.
  • Sanitize user-provided data from any characters that can be used for malicious purposes.
  • HTML Escape Before Inserting Untrusted Data into HTML Element Content. Escape the following characters with HTML entity encoding to prevent switching into any execution context, such as script, style, or event handlers
& --> &amp;
< --> &lt;
> --> &gt;
" --> &quot;
' --> &#x27;
/ --> &#x2F;
  • Encode user-provided data being reflected as output. Adjust the encoding to the output context so that, for example, HTML encoding is used for HTML content, HTML attribute encoding is used for attribute values, and JavaScript encoding is used for server-generated JavaScript.
  • Implement Content Security Policy.
Content-Security-Policy: default-src: 'self'; script-src: 'self' static.domain.tld
  • When sanitizing or encoding data, it is recommended to only use libraries specifically designed for security purposes. Also, make sure that the library you are using is being actively maintained and is kept up-to-date with the latest discovered vulnerabilities

Noncompliant Code

templates/xss_shared.html

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}

xss.py

@xss.route('/insecure/no_template_engine_replace', methods =['GET'])
def no_template_engine_replace():
param = request.args.get('param', 'not set')

html = open('templates/xss_shared.html').read()
response = make_response(html.replace('{{ name }}', param)) # Noncompliant: param is not sanitized
return response

Compliant Code

templates/xss_shared.html

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello, World!</h1>
{% endif %}

xss.py

@xss.route('/secure/no_template_engine_sanitized_Markup_escape', methods =['GET'])
def no_template_engine_sanitized_Markup_escape():
param = request.args.get('param', 'not set')

param = Markup.escape(param)

html = open('templates/xss_shared.html').read()
response = make_response(html.replace('{{ name }}', param )) # Compliant: 'param' is sanitized by Markup.escape
return response

A8- Insecure Deserialization

  • The pickle and jsonpickle module is not secure, never use it to deserialize untrusted data.
  • Only use the PyYAML module with the default safe loader.
  • Instead of using a native data interchange format, use a safe, standard format such as untyped JSON or structured data approaches such as Google Protocol Buffers.
  • To ensure integrity is not compromised, add a digital signature (HMAC) to the serialized data that is validated before deserialization (this is only valid if the client doesn’t need to modify the serialized data)
  • Restrict deserialization to be possible only to specific, whitelisted classes.
  • Isolating and running code that deserializes in low privilege environments when possible.
  • Logging deserialization exceptions and failures, such as where the incoming type is not the expected type, or the deserialization throws exceptions.
  • Restricting or monitoring incoming and outgoing network connectivity from containers or servers that deserialize.
  • Monitoring deserialization, alerting if a user deserializes constantly.
  • Use safe libraries that do not allow code execution at deserialization.
  • Not communicate with the outside world using serialized objects
  • Limit access to the serialized source
  • if it is a file, restrict the access to it.
  • if it comes from the network, restrict who has access to the process, such as with a Firewall or by authenticating the sender first.

Noncompliant Code

from flask import request
import pickle
import yaml

@app.route('/pickle')
def pickle_loads():
file = request.files['pickle']
pickle.load(file) # Noncompliant; Never use pickle module to deserialize user inputs

@app.route('/yaml')
def yaml_load():
data = request.GET.get("data")
yaml.load(data, Loader=yaml.Loader) # Noncompliant; Avoid using yaml.load with unsafe yaml.Loader

Compliant Code

from flask import request
import yaml

@app.route('/yaml')
def yaml_load():
data = request.GET.get("data")
yaml.load(data) # Compliant; Prefer using yaml.load with the default safe loader

A9- Using Components with known vulnerabilities

  • Run application with the least privilege user
  • Prefer packages that contain comprehensive unit tests and review tests for the functions our application uses
  • Review code for any unexpected file or database access
  • Research about how popular the package is, what other packages use it if any other packages are written by the author, etc
  • Use the latest stable version of the packages.
  • Watch Github repositories for notifications. This will inform us if any vulnerabilities are discovered in the package in future
  • Remove unused dependencies, unnecessary features, components, files, and documentation.
  • Continuously inventory the versions of both client-side and server-side components (e.g. frameworks, libraries) and their dependencies using tools like versions, DependencyCheck, retire.js, etc. Continuously monitor sources like CVE and NVD for vulnerabilities in the components. Use software composition analysis tools to automate the process. Subscribe to email alerts for security vulnerabilities related to components you use.
  • Only obtain components from official sources over secure links. Prefer signed packages to reduce the chance of including a modified, malicious component.
  • Monitor for libraries and components that are unmaintained or do not create security patches for older versions. If patching is not possible, consider deploying a virtual patch to monitor, detect, or protect against the discovered issue.
  • Every organization must ensure that there is an ongoing plan for monitoring, triaging, and applying updates or configuration changes for the lifetime of the application or portfolio.

A10- Insufficient Logging and Monitoring

  • Ensure all login, access control failures, and server-side input validation failures can be logged with sufficient user context to identify suspicious or malicious accounts, and held for sufficient time to allow delayed forensic analysis.
  • Ensure that logs are generated in a format that can be easily consumed by a centralized log management solutions.
  • Ensure high-value transactions have an audit trail with integrity controls to prevent tampering or deletion, such as append-only database tables or similar.
  • Establish effective monitoring and alerting such that suspicious activities are detected and responded to in a timely fashion.
  • Establish or adopt an incident response and recovery plan, such as NIST 800-61 rev 2 or later. There are commercial and open-source application protection frameworks such as OWASP AppSensor, web application firewalls such as ModSecurity with the OWASP ModSecurity Core Rule Set, and log correlation software with custom dashboards and alerting.

Unvalidated Redirects and Forwards

  • Simply avoid using redirects and forwards.
  • If used, do not allow the URL as user input for the destination.
  • Where possible, have the user provide short name, ID or token which is mapped server-side to a full target URL.
  • This provides the highest degree of protection against the attack tampering with the URL.
  • Be careful that this doesn’t introduce an enumeration vulnerability where a user could cycle through IDs to find all possible redirect targets
  • If user input can’t be avoided, ensure that the supplied value is valid, appropriate for the application, and is authorized for the user.
  • Sanitize input by creating a list of trusted URLs (lists of hosts or a regex).
  • This should be based on a white-list approach, rather than a blacklist.
  • Force all redirects to first go through a page notifying users that they are going off of your site, with the destination displayed, and have them click a link to confirm.

Noncompliant Code

  • Flask
from flask import request, redirect

@app.route('move')
def move():
url = request.args["next"]
return redirect(url) # Noncompliant
  • Django
from django.http import HttpResponseRedirect

def move(request):
url = request.GET.get("next", "/")
return HttpResponseRedirect(url) # Noncompliant

Compliant Code

  • Flask
from flask import request, redirect, url_for

@app.route('move')
def move():
endpoint = request.args["next"]
return redirect(url_for(endpoint)) # Compliant
  • Django
from django.http import HttpResponseRedirect
from urllib.parse import urlparse

DOMAINS_WHITELIST = ['www.example.com', 'example.com']

def move(request):
url = request.GET.get("next", "/")
parsed_uri = urlparse(url)
if parsed_uri.netloc in DOMAINS_WHITELIST:
return HttpResponseRedirect(url) # Compliant
return HttpResponseRedirect("/")

Server Side Request Forgery(SSRF)

  • Use a whitelist of allowed domains, resources and protocols from where the webserver can fetch resources.
  • Any input accepted from the user should be validated and rejected if it does not match the positive specification expected.
  • If possible, do not accept user input in functions that control where the webserver can fetch resources.
  • Disable unused URL schemas.
  • Migrate to IMDSv2 and disable old IMDSv1. IMDSv2 is an additional defence-in-depth mechanism for AWS that mitigates some of the instances of SSRF.

Noncompliant Code

from flask import request
import urllib

@app.route('/proxy')
def proxy():
url = request.args["url"]
return urllib.request.urlopen(url).read() # Noncompliant

Compliant Code

from flask import request
import urllib

DOMAINS_WHITELIST = ['domain1.com', 'domain2.com']

@app.route('/proxy')
def proxy():
url = request.args["url"]
if urllib.parse.urlparse(url).hostname in DOMAINS_WHITELIST:
return urllib.request.urlopen(url).read() # Compliant

Insecure File Upload

  • Whitelist allowed extensions. Only allow safe and critical extensions for business functionality
  • Validate the file type, don’t trust the Content-Type header as it can be spoofed
  • Change the filename to something generated by the application
  • The application should perform filtering and content checking on any files which are uploaded to the server. Files should be thoroughly scanned and validated before being made available to other users. If in doubt, the file should be discarded.
  • It is necessary to have a list of only permitted extensions on the web application. And, the file extension can be selected from the list.
  • All the control characters and Unicode ones should be removed from the filenames and their extensions without any exception. Also, the special characters such as “;”, “:”, “>”, “<”, “/” ,"", additional “.”, “*”, “%”, “$”, and so on should be discarded as well. If it is applicable and there is no need to have Unicode characters, it is highly recommended to only accept Alpha-Numeric characters and only 1 dot as an input for the file name and the extension; in which the file name and also the extension should not be empty at all (regular expression: [a-zA-Z0-9]{1,200}.[a-zA-Z0-9]{1,10}).
  • Limit the filename length. For instance, the maximum length of the name of a file plus its extension should be less than 255 characters (without any directory) in an NTFS partition.
  • Uploaded directory should not have any “execute” permission and all the script handlers should be removed from these directories.
  • Limit the file size to a maximum value to prevent denial of service attacks (on file space or other web application’s functions such as the image resizer).
  • In case of having compressed file extract functions, contents of the compressed file should be checked one by one as a new file.
  • If it is possible, consider saving the files in a database rather than on the filesystem.
  • File uploaders should be only accessible to authenticated and authorised users if possible.
  • Adding the “Content-Disposition: Attachment” and “X-Content-Type-Options: nosniff” headers to the response of static files will secure the website against Flash or PDF-based cross-site content-hijacking attacks.

Cross-Site Request Forgery (CSRF)

  • Check if your framework has built-in CSRF protection and use it. If the framework does not have built-in CSRF protection add CSRF tokens to all state-changing requests (requests that cause actions on the site) and validate them on the backend.
  • Always use SameSite Cookie Attribute for session cookies
  • Use custom request headers
  • Verify the origin with standard headers
  • Use unpredictable Synchronizer Token Pattern. In this method, the website generates a random token in each form as a hidden value. This token is associated with the user’s current session.
  • Use double submit cookies
  • Remember that any Cross-Site Scripting (XSS) can be used to defeat all CSRF mitigation techniques.
  • Do not use GET requests for state-changing operations.
  • CSRF can be avoided by creating a unique token in a hidden field which would be sent in the body of the HTTP request rather than in an URL, which is more prone to exposure.
  • Forcing the user to re-authenticate or proving that they are users to protect CSRF. For example, CAPTCHA.
  • For a Django application, it is recommended to protect all the views with django.middleware.csrf.CsrfViewMiddleware and to not disable the CSRF protection on specific views:
def example(request): # Compliant
return HttpResponse("default")
  • For a Flask application, the CSRFProtect module should be used (and not disabled further with WTF_CSRF_ENABLED set to false):
app = Flask(__name__)
csrf = CSRFProtect()
csrf.init_app(app) # Compliant
  • It is recommended to not disable the CSRF protection on specific views or forms:
@app.route('/example/', methods=['POST']) # Compliant
def example():
return 'example '

class unprotectedForm(FlaskForm):
class Meta:
csrf = True # Compliant

name = TextField('name')
submit = SubmitField('submit')

Cross-origin resource sharing

When using Django

  • The Access-Control-Allow-Origin header should be set only for a trusted origin and for specific resources.

Noncompliant Code

CORS_ORIGIN_ALLOW_ALL = True # Noncompliant

Compliant Code

CORS_ORIGIN_ALLOW_ALL = False # Compliant

When using Flask

  • Use flask_cors.CORS() to set appropriate access configuration. All the trusted domain name should be mentioned who can access that specific resource.
  • Wildcard should be set to False (“send_wildcard”: “False”).
  • Null origin should not be set.
  • User-supplied origin should not be used as origin unless developer wants that resource to be accessible by anyone.

Noncompliant Code

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}}) # Noncompliant

Compliant Code

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*", "send_wildcard": "False"}}) # Compliant
  • It is recommended to use HTTPs everywhere so setting the secure flag to true should be the default behaviour when creating cookies.
  • Set the secure flag to true for session-cookies.

When using Flask

Noncompliant code

from flask import Response

@app.route('/')
def index():
    response = Response()
    response.set_cookie('key', 'value') # noncompliant
    return response

Compliant Code

from flask import Response

@app.route('/')
def index():
    response = Response()
    response.set_cookie('key', 'value', secure=True) # Compliant
    return response

When using Django

  • Set the SESSION_COOKIE_SECURE = True in the settings file

Noncompliant code

from django.conf import settings

settings.configure(SESSION_COOKIE_SECURE = False) # noncompliant

Compliant Code

from django.conf import settings

settings.configure(SESSION_COOKIE_SECURE = True) # Compliant
  • By default the HttpOnly flag should be set to true for most of the cookies and it’s mandatory for session / sensitive-security cookies and csrf cookies.

When using Flask

Noncompliant Code

from flask import Response

@app.route('/')
def index():
    response = Response()
    response.set_cookie('key', 'value') # noncompliant
    return response

Compliant Code

from flask import Response

@app.route('/')
def index():
    response = Response()
    response.set_cookie('key', 'value', httponly=True) # Compliant
    return response

When using Django

  • Set SESSION_COOKIE_HTTPONLY = True and CSRF_COOKIE_HTTPONLY = True in settings file of Django.

NonCompliant Code

from django.conf import settings

settings.configure(SESSION_COOKIE_HTTPONLY = False) # noncompliant
settings.configure(CSRF_COOKIE_HTTPONLY = Flase) # noncompliant

Compliant Code

from django.conf import settings

settings.configure(SESSION_COOKIE_HTTPONLY = True) # noncompliant
settings.configure(CSRF_COOKIE_HTTPONLY = True) # noncompliant

References