Skip to content

Commit

Permalink
Fix bug 1308645: Improve frontend security (#477)
Browse files Browse the repository at this point in the history
* Stop using JS in href attributes (Persona sign in blocked by CSP)
* Fix CSP-related homepage framing issues on a local setup using HTTP
  • Loading branch information
mathjazz committed Oct 13, 2016
1 parent 4016ef9 commit 2116b29
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 8 deletions.
8 changes: 8 additions & 0 deletions pontoon/base/static/js/main.js
Expand Up @@ -422,6 +422,14 @@ var Pontoon = (function (my) {
/* Main code */
$(function() {

// Sign in with Persona
$('#persona-sign-in').click(function(e) {
e.preventDefault();

var type = $(this).data('type') || 'login';
allauth.persona.login('', type);
});

// Show/hide menu on click
$('.selector').click(function (e) {
if (!$(this).siblings('.menu').is(':visible')) {
Expand Down
2 changes: 1 addition & 1 deletion pontoon/base/templates/django/base.html
Expand Up @@ -45,7 +45,7 @@
<li><a href="https://developer.mozilla.org/en-US/docs/Localizing_with_Pontoon" target="_blank">Help</a></li>
<li id="admin"{% if not perms.base.can_manage %} class="hidden"{% endif %}><a href="{% url "pontoon.admin" %}">Admin</a></li>
{% if not user.is_authenticated %}
<li id="sign-in">Sign in: <a href="{% provider_login_url 'fxa' %}">Firefox Accounts</a> &middot; <a href="{% provider_login_url 'persona' %}">Persona</a></li>
<li id="sign-in">Sign in: <a href="{% provider_login_url 'fxa' %}">Firefox Accounts</a> &middot; <a id="persona-sign-in" href="#">Persona</a></li>
{% else %}
<li id="sign-out"><a href="{% url 'account_logout' %}" title="{{ user.email|nospam }}">Sign out</a></li>
{% endif %}
Expand Down
Expand Up @@ -5,5 +5,5 @@
{% block content %}
<h1 id="title">Sign In Failure</h1>
<h2 id="subtitle">An error occurred while attempting to sign in.</h2>
<p id="action">Try again with <a href="{% provider_login_url 'fxa' %}">Firefox Accounts</a> or <a href="{% provider_login_url 'persona' %}">Persona</a></p>
<p id="action">Try again with <a href="{% provider_login_url 'fxa' %}">Firefox Accounts</a> or <a id="persona-sign-in" href="#">Persona</a></p>
{% endblock %}
2 changes: 1 addition & 1 deletion pontoon/base/templates/landing.html
Expand Up @@ -16,7 +16,7 @@
<li><a href="https://developer.mozilla.org/docs/Mozilla/Localization/Localizing_with_Pontoon" target="_blank">Help</a></li>
<li id="admin"{% if not perms.base.can_manage %} class="hidden"{% endif %}><a href="{{ url('pontoon.admin') }}">Admin</a></li>
{% if not user.is_authenticated() %}
<li id="sign-in">Sign in: <a href="{{ provider_login_url(request, "fxa") }}">Firefox Accounts</a> &middot; <a href="{{ provider_login_url(request, "persona") }}">Persona</a></li>
<li id="sign-in">Sign in: <a href="{{ provider_login_url(request, "fxa") }}">Firefox Accounts</a> &middot; <a id="persona-sign-in" href="#">Persona</a></li>
{% else %}
<li id="sign-out"><a href="{{ url('account_logout') }}" title="{{ user.email|nospam }}">Sign out</a></li>
{% endif %}
Expand Down
4 changes: 2 additions & 2 deletions pontoon/base/templates/translate.html
Expand Up @@ -126,12 +126,12 @@ <h2>Project info</h2>

{% if user.is_authenticated() %}
{% if user.logged_via('fxa') and not user.logged_via('persona') %}
<li><a href="{{ provider_login_url(request, "persona", process="connect") }}">Connect with Persona account</a></li>
<li><a id="persona-sign-in" data-type="connect" href="#">Connect with Persona account</a></li>
{% endif %}
<li class="sign-out"><a href="{{ url('account_logout') }}"><i class="fa fa-sign-out fa-fw"></i>Sign Out</a></li>
{% else %}
<li class="sign-in"><a href="{{ provider_login_url(request, "fxa") }}"><i class="fa fa-sign-in fa-fw"></i>Sign in: Firefox Accounts</a></li>
<li class="sign-in"><a href="{{ provider_login_url(request, "persona")}}"><i class="fa fa-sign-in fa-fw"></i>Sign in: Persona</a></li>
<li class="sign-in"><a id="persona-sign-in" href="#"><i class="fa fa-sign-in fa-fw"></i>Sign in: Persona</a></li>
{% endif %}
</ul>
</div>
Expand Down
46 changes: 43 additions & 3 deletions pontoon/settings/base.py
Expand Up @@ -136,6 +136,8 @@ def path(*args):
'session_csrf.CsrfMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'csp.middleware.CSPMiddleware',
)

CONTEXT_PROCESSORS = (
Expand Down Expand Up @@ -471,9 +473,6 @@ def _allowed_hosts():
if not os.environ.get('CI', False):
NOSE_ARGS.append('--with-progressive')

# Set X-Frame-Options to DENY by default on all responses.
X_FRAME_OPTIONS = 'DENY'

# General auth settings
LOGIN_URL = '/'
LOGIN_REDIRECT_URL = '/'
Expand All @@ -487,9 +486,50 @@ def _allowed_hosts():
# Always generate a CSRF token for anonymous users.
ANON_ALWAYS = True

# Set X-Frame-Options to DENY by default on all responses.
X_FRAME_OPTIONS = 'DENY'

# Use correct header for detecting HTTPS on Heroku.
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

# Strict-Transport-Security: max-age=63072000
# Ensures users only visit the site over HTTPS
SECURE_HSTS_SECONDS = 63072000

# X-Content-Type-Options: nosniff
# Disables browser MIME type sniffing
SECURE_CONTENT_TYPE_NOSNIFF = True

# x-xss-protection: 1; mode=block
# Activates the browser's XSS filtering and helps prevent XSS attacks
SECURE_BROWSER_XSS_FILTER = True

# Content-Security-Policy headers
CSP_DEFAULT_SRC = ("'none'",)
CSP_CHILD_SRC = ("https:",)
CSP_FRAME_SRC = ("https:",) # Older browsers
CSP_CONNECT_SRC = ("'self'",)
CSP_FONT_SRC = ("'self'",)
CSP_IMG_SRC = (
"'self'",
"https://*.wp.com/pontoon.mozilla.org/",
"https://ssl.google-analytics.com",
"https://www.gravatar.com/avatar/",
)
CSP_SCRIPT_SRC = (
"'self'",
"https://login.persona.org",
"'sha256-x3niK4UU+vG6EGT2NK2rwi2j/etQodJd840oRpEnqd4='",
"'sha256-fDsgbzHC0sNuBdM4W91nXVccgFLwIDkl197QEca/Cl4='",
"https://ssl.google-analytics.com/ga.js",
)
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'",)

# Needed if site not hosted on HTTPS domains (like local setup)
if not SITE_URL.startswith('https'):
CSP_IMG_SRC = CSP_IMG_SRC + ("http://www.gravatar.com/avatar/",)
CSP_CHILD_SRC = CSP_FRAME_SRC = CSP_FRAME_SRC + ("http:",)

# For absolute urls
try:
DOMAIN = socket.gethostname()
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Expand Up @@ -145,3 +145,5 @@ python-openid==2.2.5 \
requests-oauthlib==0.6.1 \
--hash=sha256:905306080ec0cc6b3c65c8101f471fccfdb9994c16dd116524fd3fc0790d46d7 \
--hash=sha256:7c708e8e2a4aa6a905cf91f28420d75db37270e0ec8fc951915c098dd8bde53e
django-csp==3.1 \
--hash=sha256:9208219c341ddbe371b5fd217ced0c916a5e2f7184bc603415074afcace6b51c

0 comments on commit 2116b29

Please sign in to comment.