Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/appengine/handlers/base_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
# https://github.com/google/closure-library/blob/
# 3037e09cc471bfe99cb8f0ee22d9366583a20c28/closure/goog/html/safeurl.js
_SAFE_URL_PATTERN = re.compile(
r'^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))', flags=re.IGNORECASE)
r'^(?:(?:https?|mailto|ftp):|(?!//)[^:/?#]*(?:[/?#]|$))', flags=re.IGNORECASE)


def add_jinja2_filter(name, fn):
Expand Down Expand Up @@ -130,6 +130,15 @@ def make_logout_url(dest_url):

def check_redirect_url(url):
"""Check redirect URL is safe."""
# Browsers strip ASCII tab/newline and leading control/whitespace characters
# from URLs and normalize backslashes to forward slashes, so inputs such as
# `/\\evil.com`, ` //evil.com` or `\r\n//evil.com` would otherwise pass the
# scheme-relative check below and then navigate to an external host. Reject
# any input with a backslash, a control character, or leading/trailing
# whitespace before validating.
if (not url or url != url.strip() or '\\' in url
or any(ord(char) < 0x20 for char in url)):
raise helpers.EarlyExitError('Invalid redirect.', 403)
if not _SAFE_URL_PATTERN.match(url):
raise helpers.EarlyExitError('Invalid redirect.', 403)

Expand Down