Repository: nextcloud/appstore

Issue:
Normal and social signup needs a stricter CSP
ATM we disable CSP on both sign up pages because the Recaptcha widget breaks. This should be fixed by creating a custom recaptcha field and adding the exceptions listed in https://developers.google.com/recaptcha/docs/faq#im-using-content-security-policy-csp-on-my-website-how-can-i-configure-it-to-work-with-recaptcha to the CSP whitelist using @csp_update rather than @csp_exempt http://django-csp.readthedocs.io/en/latest/decorators.html#csp-update

https://github.com/praekelt/django-recaptcha/issues/101



Discussion:

Resolving patch:
diff --git a/nextcloudappstore/core/user/forms.py b/nextcloudappstore/core/user/forms.py
--- a/nextcloudappstore/core/user/forms.py
+++ b/nextcloudappstore/core/user/forms.py
@@ -1,16 +1,16 @@
-from allauth.account.models import EmailAddress
 from allauth.account.utils import filter_users_by_email
-from django import forms
 from captcha.fields import ReCaptchaField
-from django.conf import settings
+from django import forms
 from django.contrib.auth import get_user_model
 from django.forms import EmailField, CharField
 from django.utils.translation import ugettext_lazy as _
+from snowpenguin.django.recaptcha2.fields import ReCaptchaField
+from snowpenguin.django.recaptcha2.widgets import ReCaptchaWidget
 
 
 class SignupFormRecaptcha(forms.Form):
     """integrate a recaptcha field."""
-    recaptcha = ReCaptchaField()
+    recaptcha = ReCaptchaField(widget=ReCaptchaWidget())
     first_name = CharField(max_length=30, label=_('First name'))
     last_name = CharField(max_length=30, label=_('Last name'))
 
diff --git a/nextcloudappstore/settings/base.py b/nextcloudappstore/settings/base.py
--- a/nextcloudappstore/settings/base.py
+++ b/nextcloudappstore/settings/base.py
@@ -26,10 +26,10 @@
     'nextcloudappstore.core.apps.CoreConfig',
     'nextcloudappstore.core.user.apps.UserConfig',
     'parler',
-    'captcha',
     'rest_framework',
     'rest_framework.authtoken',
     'corsheaders',
+    'snowpenguin.django.recaptcha2',
     'allauth',
     'allauth.account',
     'allauth.socialaccount',
@@ -41,7 +41,7 @@
     'django.contrib.messages',
     'django.contrib.sites',
     'django.contrib.staticfiles',
-    'django.contrib.humanize'
+    'django.contrib.humanize',
 ]
 
 MIDDLEWARE_CLASSES = [
@@ -196,6 +196,13 @@
 CSP_STYLE_SRC = ('\'self\'',)
 CSP_FORM_ACTION = ('\'self\'',)
 
+CSP_SIGNUP = {
+    'SCRIPT_SRC': ['https://www.google.com/recaptcha/',
+                   'https://www.gstatic.com/recaptcha/'],
+    'CHILD_SRC': ['https://www.google.com/recaptcha/'],
+    'STYLE_SRC': '\'unsafe-inline\''
+}
+
 # use modern no Captcha reCaptcha
 NOCAPTCHA = True
 
diff --git a/nextcloudappstore/urls.py b/nextcloudappstore/urls.py
--- a/nextcloudappstore/urls.py
+++ b/nextcloudappstore/urls.py
@@ -1,6 +1,7 @@
 from allauth.account.views import signup
 from allauth.socialaccount.views import signup as social_signup
-from csp.decorators import csp_exempt
+from csp.decorators import csp_exempt, csp_update
+from django.conf import settings
 from django.conf.urls import url, include
 from django.conf.urls.i18n import i18n_patterns
 from django.contrib import admin
@@ -12,8 +13,9 @@
 
 urlpatterns = [
     url(r'^$', CategoryAppListView.as_view(), {'id': None}, name='home'),
-    url(r"^signup/$", csp_exempt(signup), name="account_signup"),
-    url(r"^social/signup/$", csp_exempt(social_signup),
+    url(r"^signup/$", csp_update(**settings.CSP_SIGNUP)(signup),
+        name="account_signup"),
+    url(r"^social/signup/$", csp_update(**settings.CSP_SIGNUP)(social_signup),
         name="socialaccount_signup"),
     url(r'^', include('allauth.urls')),
     url(r'^legal/?$', LegalNoticeView.as_view(), name='legal-notice'),


Test patch: