User-provided data, such as URL parameters, POST data payloads, or cookies, should always be considered untrusted and tainted. Applications
constructing HTTP response headers based on tainted data could allow attackers to change security sensitive headers like Cross-Origin Resource Sharing
headers.
Web application frameworks and servers might also allow attackers to inject new line characters in headers to craft malformed HTTP response. In
this case the application would be vulnerable to a larger range of attacks like HTTP Response Splitting/Smuggling. Most of the time this type of
attack is mitigated by default modern web application frameworks but there might be rare cases where older versions are still vulnerable.
As a best practice, applications that use user-provided data to construct the response header should always validate the data first. Validation
should be based on a whitelist.
Noncompliant code example
Flask
from flask import Response, request
from werkzeug.datastructures import Headers
@app.route('/route')
def route():
content_type = request.args["Content-Type"]
response = Response()
headers = Headers()
headers.add("Content-Type", content_type) # Noncompliant
response.headers = headers
return response
Django
import django.http
def route(request):
content_type = request.GET.get("Content-Type")
response = django.http.HttpResponse()
response.__setitem__('Content-Type', content_type) # Noncompliant
return response
Compliant solution
Flask
from flask import Response, request
from werkzeug.datastructures import Headers
import re
@app.route('/route')
def route():
content_type = request.args["Content-Type"]
allowed_content_types = r'application/(pdf|json|xml)'
response = Response()
headers = Headers()
if re.match(allowed_content_types, content_type):
headers.add("Content-Type", content_type) # Compliant
else:
headers.add("Content-Type", "application/json")
response.headers = headers
return response
Django
import django.http
import re
def route(request):
content_type = request.GET.get("Content-Type")
allowed_content_types = r'application/(pdf|json|xml)'
response = django.http.HttpResponse()
if re.match(allowed_content_types, content_type):
response.__setitem__('Content-Type', content_type) # Compliant
else:
response.__setitem__('Content-Type', "application/json")
return response