HIGH injection flawsflask

Injection Flaws in Flask

How Injection Flaws Manifests in Flask

Injection flaws in Flask applications typically occur when untrusted data is sent to an interpreter as part of a command or query. Flask's flexibility and Python's dynamic nature create several injection vectors that developers must guard against.

SQL Injection via Raw Queries

The most common injection flaw in Flask apps involves SQL injection through database queries. When developers use string formatting or concatenation to build SQL queries, attackers can manipulate input to execute arbitrary SQL:

@app.route('/search')
def search():
    query = request.args.get('q')
    # VULNERABLE: Direct string interpolation
    results = db.execute(f"SELECT * FROM users WHERE name LIKE '%{query}%'" ).fetchall()
    return render_template('results.html', results=results)

An attacker could submit q as %' OR '1'='1 to return all users, or %' UNION SELECT password FROM users-- to extract sensitive data.

Command Injection via Shell Operations

Flask applications that execute shell commands with user input are vulnerable to command injection. This often occurs when processing file uploads, system information, or external integrations:

@app.route('/convert')
def convert():
    filename = request.args.get('file')
    # VULNERABLE: Shell injection possible
    os.system(f'convert {filename} output.png')
    return 'Conversion complete'

An attacker could submit file as image.png; rm -rf / or image.png && cat /etc/passwd to execute arbitrary commands.

Template Injection via Jinja2

Flask uses Jinja2 templating, which can be vulnerable to Server-Side Template Injection (SSTI) when user input is rendered unsafely:

@app.route('/greet')
def greet():
    name = request.args.get('name')
    # VULNERABLE: SSTI if name contains template code
    return render_template('greet.html', name=name)

If name contains {{ 7 * 7 }}, the template will render 49. More sophisticated payloads can lead to remote code execution.

LDAP Injection

Applications using LDAP for authentication or directory services can suffer LDAP injection:

@app.route('/ldap_search')
def ldap_search():
    username = request.args.get('username')
    # VULNERABLE: LDAP injection possible
    filter = f"(&(objectClass=person)(uid={username}))"
    results = ldap.search_s(base_dn, ldap.SCOPE_SUBTREE, filter)
    return str(results)

An attacker could submit username as *)(uid=admin)(* to bypass authentication or extract sensitive directory information.

Flask-Specific Detection

Detecting injection flaws in Flask requires both static analysis and dynamic testing. Here's how to identify these vulnerabilities in your Flask applications.

Static Code Analysis

Review your Flask codebase for dangerous patterns. Use tools like bandit or custom scripts to find vulnerable code:

import ast
import os

def find_vulnerable_patterns(filepath):
    with open(filepath) as f:
        tree = ast.parse(f.read(), filename=filepath)
    
    vulnerabilities = []
    
    for node in ast.walk(tree):
        # SQL injection patterns
        if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Mod):
            if "execute" in ast.dump(node):
                vulnerabilities.append((node.lineno, "SQL injection via string formatting"))
        
        # Command injection patterns
        if isinstance(node, ast.Call) and isinstance(node.func, ast.Name):
            if node.func.id in ['system', 'popen', 'call']:
                vulnerabilities.append((node.lineno, "Command injection via shell execution"))
    
    return vulnerabilities

# Scan all Python files in Flask app
for root, dirs, files in os.walk('.'):
    for file in files:
        if file.endswith('.py'):
            results = find_vulnerable_patterns(os.path.join(root, file))
            for lineno, issue in results:
                print(f"{file}:{lineno} - {issue}")

Dynamic Testing with middleBrick

middleBrick provides automated black-box scanning that can detect injection flaws without requiring source code access or credentials:

# Scan your Flask API endpoints
middlebrick scan https://your-flask-app.com/api

# Scan with specific focus on injection vulnerabilities
middlebrick scan --focus injection https://your-flask-app.com

# Integrate into your development workflow
middlebrick scan --ci --fail-below B https://staging.your-flask-app.com

middleBrick tests for SQL injection, command injection, template injection, and other injection vectors by sending crafted payloads to your API endpoints and analyzing responses for vulnerability indicators.

Manual Testing Techniques

Complement automated scanning with manual testing:

  • SQL injection: Try ' OR '1'='1, '; DROP TABLE users; --, and numeric payloads
  • Command injection: Try || ls, && whoami, and semicolon-separated commands
  • Template injection: Try {{7*7}}, {{config}}, and Jinja2 filters
  • LDAP injection: Try asterisk patterns and parenthesis manipulation

Monitor application responses for error messages, unexpected behavior, or execution delays that indicate successful exploitation.

Flask-Specific Remediation

Fixing injection flaws in Flask requires using safe coding practices and Flask's built-in security features. Here are specific remediation techniques for common injection vulnerabilities.

SQL Injection Prevention

Always use parameterized queries or ORM methods instead of string concatenation:

from flask import Flask, request
from sqlalchemy import create_engine, text

app = Flask(__name__)
engine = create_engine('postgresql://user:pass@localhost/db')

@app.route('/search')
def search():
    query = request.args.get('q')
    
    # SECURE: Parameterized query
    results = engine.execute(
        text("SELECT * FROM users WHERE name ILIKE :pattern"),
        {'pattern': f'%{query}%'}
    ).fetchall()
    
    return render_template('results.html', results=results)

# Using SQLAlchemy ORM (even more secure)
from sqlalchemy.orm import sessionmaker
from models import User

@app.route('/search_orm')
def search_orm():
    query = request.args.get('q')
    
    Session = sessionmaker(bind=engine)
    session = Session()
    
    # SECURE: ORM handles parameterization automatically
    results = session.query(User).filter(User.name.ilike(f'%{query}%')).all()
    
    return render_template('results.html', results=results)

Command Injection Prevention

Never use shell=True or string interpolation for command execution. Use subprocess with argument lists:

import subprocess
from flask import Flask, request

app = Flask(__name__)

@app.route('/convert')
def convert():
    filename = request.args.get('file')
    
    # SECURE: subprocess with argument list, no shell=True
    try:
        result = subprocess.run(
            ['convert', filename, 'output.png'],
            capture_output=True,
            check=True
        )
        return 'Conversion complete'
    except subprocess.CalledProcessError as e:
        return f'Error: {e}', 400

# For file operations, validate filename strictly
import re

def is_valid_filename(name):
    # Only allow alphanumeric, hyphens, underscores, and single dots
    return bool(re.match(r'^[\-\w]+\.[a-zA-Z0-9]{1,4}$', name))

@app.route('/upload')
def upload():
    filename = request.args.get('file')
    
    if not is_valid_filename(filename):
        return 'Invalid filename', 400
    
    # Now it's safe to process the file
    return process_file(filename)

Template Injection Prevention

Escape user input in templates and validate template context:

from flask import Flask, request, escape

app = Flask(__name__)

@app.route('/greet')
def greet():
    name = request.args.get('name')
    
    # SECURE: Escape user input
    safe_name = escape(name)
    
    return render_template('greet.html', name=safe_name)

# For dynamic template rendering, use a whitelist
ALLOWED_TEMPLATES = {'greet', 'welcome', 'about'}

@app.route('/render')
def render():
    template_name = request.args.get('template')
    
    if template_name not in ALLOWED_TEMPLATES:
        return 'Invalid template', 400
    
    return render_template(template_name)

LDAP Injection Prevention

Use parameterized LDAP queries and validate input:

import ldap3
from flask import Flask, request

app = Flask(__name__)

server = ldap3.Server('ldap://localhost')
connection = ldap3.Connection(server)

@app.route('/ldap_search')
def ldap_search():
    username = request.args.get('username')
    
    # Validate input - only allow alphanumeric and certain characters
    if not re.match(r'^[a-zA-Z0-9_.@-]+$', username):
        return 'Invalid username format', 400
    
    # SECURE: Use safe search filters
    base_dn = "ou=users,dc=example,dc=com"
    search_filter = f"(uid={username})"
    
    if connection.search(base_dn, search_filter, attributes=['cn', 'mail']):
        return str(connection.entries)
    else:
        return 'User not found', 404

Input Validation and Sanitization

Implement comprehensive input validation using Flask-WTF or similar libraries:

from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField
from wtforms.validators import DataRequired, Length, NumberRange

class SearchForm(FlaskForm):
    query = StringField('Query', validators=[
        DataRequired(),
        Length(min=1, max=100),
        # Custom validator for SQL injection patterns
        Regexp(r'^[a-zA-Z0-9\s\-_\.]+$', 
               message="Invalid characters in search query")
    ])
    
    page = IntegerField('Page', validators=[
        NumberRange(min=1, max=100)
    ])

@app.route('/secure_search', methods=['GET', 'POST'])
def secure_search():
    form = SearchForm(request.form)
    
    if form.validate():
        query = form.query.data
        # Safe to use in database queries
        results = search_database(query)
        return render_template('results.html', results=results)
    else:
        return 'Invalid input', 400

Frequently Asked Questions

How does Flask's automatic escaping help prevent injection attacks?

Flask automatically escapes variables in Jinja2 templates by default, converting special characters to HTML entities (e.g., < becomes <). This prevents XSS attacks when rendering user input. However, this escaping only applies to template rendering, not to SQL, command, or LDAP injection vulnerabilities. Developers must still use parameterized queries, avoid shell=True, and validate all input for non-template contexts.

Can middleBrick detect injection flaws in my Flask application without access to the source code?

Yes, middleBrick performs black-box scanning that tests your Flask API endpoints without requiring source code or credentials. It sends malicious payloads to detect SQL injection, command injection, template injection, and other injection flaws by analyzing responses for vulnerability indicators like error messages, unexpected behavior, or execution delays. The scan takes 5-15 seconds and provides a security risk score with prioritized findings and remediation guidance.