Change handling of headers that indicate SSL requests.

Instead of hardcoding X-Forwarded-Protocol and X-Forwarded-SSL, make the
header and value configurable, with no default that would enable a
client to spoof secure requests if the reverse proxy is not configured
to strip the header used.
This commit is contained in:
John Hensley 2011-02-06 11:06:30 -05:00 committed by benoitc
parent 1a796ebbc6
commit 7e9f8b5b02
3 changed files with 73 additions and 19 deletions

View File

@ -336,6 +336,29 @@ int(value, 0) (0 means Python guesses the base, so values like "0",
<p>This path should be writable by the process permissions set for Gunicorn
workers. If not specified, Gunicorn will choose a system generated
temporary directory.</p>
</div>
<div class="section" id="secure-scheme-headers">
<h4><a class="toc-backref" href="#contents">secure_scheme_headers</a></h4>
<ul class="simple">
<li><tt class="docutils literal">{
"X-FORWARDED-PROTOCOL": "ssl",
"X-FORWARDED-SSL": "on"
}
</tt></li>
</ul>
<p>A dictionary containing headers and values that the front-end proxy
uses to indicate HTTPS requests. These tell gunicorn to set
wsgi.url_scheme to "https", so your application can tell that the
request is secure.</p>
<p>The dictionary should map upper-case header names to exact string
values. The value comparisons are case-sensitive, unlike the header
names, so make sure they're exactly what your front-end proxy sends
when handling HTTPS requests.</p>
<p>It is important that your front-end proxy configuration ensures that
the headers defined here can not be passed directly from the client.</p>
</div>
</div>
<div class="section" id="logging">
@ -549,28 +572,29 @@ the just-exited Worker.</p>
<li><a class="reference internal" href="#group" id="id27">group</a></li>
<li><a class="reference internal" href="#umask" id="id28">umask</a></li>
<li><a class="reference internal" href="#tmp-upload-dir" id="id29">tmp_upload_dir</a></li>
<li><a class="reference internal" href="#secure-scheme-headers" id="id30">secure_scheme_headers</a></li>
</ul>
</li>
<li><a class="reference internal" href="#logging" id="id30">Logging</a><ul>
<li><a class="reference internal" href="#logfile" id="id31">logfile</a></li>
<li><a class="reference internal" href="#loglevel" id="id32">loglevel</a></li>
<li><a class="reference internal" href="#logconfig" id="id33">logconfig</a></li>
<li><a class="reference internal" href="#logging" id="id31">Logging</a><ul>
<li><a class="reference internal" href="#logfile" id="id32">logfile</a></li>
<li><a class="reference internal" href="#loglevel" id="id33">loglevel</a></li>
<li><a class="reference internal" href="#logconfig" id="id34">logconfig</a></li>
</ul>
</li>
<li><a class="reference internal" href="#process-naming" id="id34">Process Naming</a><ul>
<li><a class="reference internal" href="#proc-name" id="id35">proc_name</a></li>
<li><a class="reference internal" href="#default-proc-name" id="id36">default_proc_name</a></li>
<li><a class="reference internal" href="#process-naming" id="id35">Process Naming</a><ul>
<li><a class="reference internal" href="#proc-name" id="id36">proc_name</a></li>
<li><a class="reference internal" href="#default-proc-name" id="id37">default_proc_name</a></li>
</ul>
</li>
<li><a class="reference internal" href="#server-hooks" id="id37">Server Hooks</a><ul>
<li><a class="reference internal" href="#on-starting" id="id38">on_starting</a></li>
<li><a class="reference internal" href="#when-ready" id="id39">when_ready</a></li>
<li><a class="reference internal" href="#pre-fork" id="id40">pre_fork</a></li>
<li><a class="reference internal" href="#post-fork" id="id41">post_fork</a></li>
<li><a class="reference internal" href="#pre-exec" id="id42">pre_exec</a></li>
<li><a class="reference internal" href="#pre-request" id="id43">pre_request</a></li>
<li><a class="reference internal" href="#post-request" id="id44">post_request</a></li>
<li><a class="reference internal" href="#worker-exit" id="id45">worker_exit</a></li>
<li><a class="reference internal" href="#server-hooks" id="id38">Server Hooks</a><ul>
<li><a class="reference internal" href="#on-starting" id="id39">on_starting</a></li>
<li><a class="reference internal" href="#when-ready" id="id40">when_ready</a></li>
<li><a class="reference internal" href="#pre-fork" id="id41">pre_fork</a></li>
<li><a class="reference internal" href="#post-fork" id="id42">post_fork</a></li>
<li><a class="reference internal" href="#pre-exec" id="id43">pre_exec</a></li>
<li><a class="reference internal" href="#pre-request" id="id44">pre_request</a></li>
<li><a class="reference internal" href="#post-request" id="id45">post_request</a></li>
<li><a class="reference internal" href="#worker-exit" id="id46">worker_exit</a></li>
</ul>
</li>
</ul>

View File

@ -179,6 +179,11 @@ def validate_bool(val):
else:
raise ValueError("Invalid boolean: %s" % val)
def validate_dict(val):
if not isinstance(val, dict):
raise TypeError("Value is not a dictionary: %s " % val)
return val
def validate_pos_int(val):
if not isinstance(val, (types.IntType, types.LongType)):
val = int(val, 0)
@ -522,6 +527,30 @@ class TmpUploadDir(Setting):
temporary directory.
"""
class SecureSchemeHeader(Setting):
name = "secure_scheme_headers"
section = "Server Mechanics"
validator = validate_dict
default = {
"X-FORWARDED-PROTOCOL": "ssl",
"X-FORWARDED-SSL": "on"
}
desc = """\
A dictionary containing headers and values that the front-end proxy
uses to indicate HTTPS requests. These tell gunicorn to set
wsgi.url_scheme to "https", so your application can tell that the
request is secure.
The dictionary should map upper-case header names to exact string
values. The value comparisons are case-sensitive, unlike the header
names, so make sure they're exactly what your front-end proxy sends
when handling HTTPS requests.
It is important that your front-end proxy configuration ensures that
the headers defined here can not be passed directly from the client.
"""
class Logfile(Setting):
name = "logfile"
section = "Logging"

View File

@ -68,6 +68,8 @@ def create(req, sock, client, server, cfg):
url_scheme = "http"
script_name = os.environ.get("SCRIPT_NAME", "")
secure_headers = getattr(cfg, "secure_scheme_headers")
for hdr_name, hdr_value in req.headers:
if hdr_name == "EXPECT":
# handle expect
@ -75,9 +77,8 @@ def create(req, sock, client, server, cfg):
sock.send("HTTP/1.1 100 Continue\r\n\r\n")
elif hdr_name == "X-FORWARDED-FOR":
forward = hdr_value
elif hdr_name == "X-FORWARDED-PROTOCOL" and hdr_value.lower() == "ssl":
url_scheme = "https"
elif hdr_name == "X-FORWARDED-SSL" and hdr_value.lower() == "on":
elif (hdr_name.upper() in secure_headers and
hdr_value == secure_headers[hdr_name.upper()]):
url_scheme = "https"
elif hdr_name == "HOST":
server = hdr_value