python.django.security.injection.tainted-url-host.tainted-url-host

Author
unknown
Download Count*
License
User data flows into the host portion of this manually-constructed URL. This could allow an attacker to send data to their own server, potentially exposing sensitive data such as cookies or authorization information sent with this request. They could also probe internal servers or other resources that the server runnig this code can access. (This is called server-side request forgery, or SSRF.) Do not allow arbitrary hosts. Instead, create an allowlist for approved hosts hardcode the correct host.
Run Locally
Run in CI
Defintion
rules:
- id: tainted-url-host
languages:
- python
message: User data flows into the host portion of this manually-constructed URL.
This could allow an attacker to send data to their own server, potentially
exposing sensitive data such as cookies or authorization information sent
with this request. They could also probe internal servers or other
resources that the server runnig this code can access. (This is called
server-side request forgery, or SSRF.) Do not allow arbitrary hosts.
Instead, create an allowlist for approved hosts hardcode the correct host.
metadata:
cwe:
- "CWE-918: Server-Side Request Forgery (SSRF)"
owasp:
- A10:2021 - Server-Side Request Forgery (SSRF)
references:
- https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
category: security
technology:
- flask
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
cwe2022-top25: true
cwe2021-top25: true
subcategory:
- audit
impact: MEDIUM
likelihood: LOW
confidence: LOW
mode: taint
pattern-sinks:
- patterns:
- pattern-either:
- patterns:
- pattern: '"$URLSTR" % ...'
- metavariable-pattern:
metavariable: $URLSTR
language: generic
patterns:
- pattern-either:
- pattern: $SCHEME://%s
- pattern: $SCHEME://%r
- patterns:
- pattern: '"$URLSTR".format(...)'
- metavariable-pattern:
metavariable: $URLSTR
language: generic
pattern: $SCHEME:// { ... }
- patterns:
- pattern: '"$URLSTR" + ...'
- metavariable-regex:
metavariable: $URLSTR
regex: .*://$
- patterns:
- pattern: f"$URLSTR{...}..."
- metavariable-regex:
metavariable: $URLSTR
regex: .*://$
- patterns:
- pattern-inside: |
$URL = "$URLSTR"
...
- pattern: $URL += ...
- metavariable-regex:
metavariable: $URLSTR
regex: .*://$
pattern-sources:
- patterns:
- pattern: request.$ANYTHING
- pattern-not: request.build_absolute_uri
severity: WARNING
Examples
tainted-url-host.py
from django.http import HttpResponse
import requests
class Person(models.Model):
first_name = models.CharField(...)
last_name = models.CharField(...)
birth_date = models.DateField(...)
##### True Positives #########
def ex1(request):
env = request.POST.get('env')
user_name = request.POST.get('user_name')
# ruleid: tainted-url-host
user_age = requests.get("https://%s/%s/age" % (env, user_name))
return HttpResponse(user_age)
def ex2(request):
env = request.POST.get('env')
user_name = request.POST.get('user_name')
# ruleid: tainted-url-host
user_age = requests.get("https://{}/{}/age".format(env, user_name))
return HttpResponse(user_age)
def ex3(request):
env = request.POST.get('env')
user_name = request.POST.get('user_name')
# ruleid: tainted-url-host
user_age = requests.get(f"https://{env}/{user_name}/age")
return HttpResponse(user_age)
def ex4(request):
env = request.POST.get('env')
user_name = request.POST.get('user_name')
# ruleid: tainted-url-host
user_age = requests.get(f"https://" + env + "/" + user_name + "/age")
return HttpResponse(user_age)
def ex5(request):
env = request.POST.get('env')
user_name = request.POST.get('user_name')
# ruleid: tainted-url-host
url = "https://{}/{}/age".format(env, user_name)
user_age = requests.get(url)
return HttpResponse(user_age)
def ex6(request):
env = request.POST.get('env')
user_name = request.POST.get('user_name')
# ruleid: tainted-url-host
url = "https://{}/{}/age".format(env, user_name)
user_age = requests.get(url)
return HttpResponse(user_age)
def ex7(request):
env = request.POST.get('env')
user_name = request.POST.get('user_name')
url = "https://%s/%s/age"
# ruleid: tainted-url-host
user_age = requests.get(url % (env, user_name))
return HttpResponse(user_age)
def ex8(request):
env = request.POST.get('env')
user_name = request.POST.get('user_name')
url = "https://{}/{}/age"
# ruleid: tainted-url-host
user_age = requests.get(url.format(env, user_name))
return HttpResponse(user_age)
##### True Negatives #########
def ok1(request):
env = request.POST.get('env')
user_name = request.POST.get('user_name')
# ok: tainted-url-host
user_age = requests.get("https://example.com/%s/%s/age" % (env, user_name))
return HttpResponse(user_age)
def ok2(request):
env = request.POST.get('env')
user_name = request.POST.get('user_name')
# ok: tainted-url-host
user_age = requests.get("https://example.com/%s/%s/age".format(env, user_name))
return HttpResponse(user_age)
Short Link: https://sg.run/oYz6