Injection: SQL, Commands and Templates
Injection happens when user input is treated as code: SQL, shell commands, templates or LDAP queries.
SQL injection
Vulnerable:
query = f"SELECT * FROM users WHERE email = '{email}'"
db.session.execute(text(query))
Input ' OR '1'='1 returns every user; worse payloads read or modify any data.
Fixed with parameters:
db.session.execute(text("SELECT * FROM users WHERE email = :email"), {"email": email})
# or simply use the ORM:
User.query.filter_by(email=email).first()
Parameters send data separately from SQL, so input can never change the query's structure. For dynamic ORDER BY columns (which cannot be parameters), map user choices to a fixed allowlist.
Command injection
Vulnerable:
os.system(f"convert {filename} thumb.png") # filename = "x.png; rm -rf /"
Fixed:
subprocess.run(["convert", safe_path, "thumb.png"], check=True, timeout=30)
Pass a list of arguments (no shell), validate inputs and prefer libraries (Pillow) over shell commands.
Template injection
Rendering user input as a template lets attackers run code:
render_template_string("Hello " + request.args["name"]) # dangerous
render_template("hello.html", name=request.args["name"]) # safe: name is data
Finding injection
- Review code for string-built queries,
shell=True,os.system,eval,render_template_string. - Use static analysis (Bandit for Python, Semgrep rules).
- Automated scanners send crafted payloads to parameters and look for errors or behaviour changes; treat their findings as leads to verify, not final answers.
Defence checklist
- [ ] ORM or parameterised queries everywhere
- [ ] No shell commands built from input; argument lists only
- [ ] No
eval/execon user data - [ ] Database user with least privilege (cannot drop tables)
- [ ] Errors do not reveal SQL or stack traces to users
Exercise
Search a project (yours or an intentionally vulnerable practice app such as OWASP Juice Shop or DVWA running locally) for injection patterns with Bandit or Semgrep, fix each finding with parameterisation or argument lists, and add a test that the old payload no longer works.
Check yourself
- Why do parameterised queries prevent SQL injection?
- How do you safely allow user-chosen sort columns?
- Why is
render_template_stringwith user input dangerous?
Ihr Fortschritt wird nur in diesem Browser gespeichert.
Zur Prüfung einreichen (10 Punkte)