Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make sure djangosaml2 works in csp-enabled applications too (fix #391) #392

Merged
merged 7 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
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
24 changes: 23 additions & 1 deletion djangosaml2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,26 @@

logger = logging.getLogger("djangosaml2")

# Update Content-Security-Policy headers for POST-Bindings
try:
from csp.decorators import csp_update
except ModuleNotFoundError:
# If csp is not installed, do not update fields as Content-Security-Policy
# is not used
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a logger.warning here. It would be appreciated to make aware the deployers that they may haev security risks (if CSP is not configured at least in the httpd)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I polished up the security page and added a warning, please check if I am missing important information :)

Comment on lines +85 to +86
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# If csp is not installed, do not update fields as Content-Security-Policy
# is not used
logger.warning(
"django-csp is not installed, djangosaml2 cannot handle the Content-Security-Policy."
)

def saml2_csp_update(view):
return view

logger.warning("django-csp could not be found, not updating Content-Security-Policy. Please "
"make sure CSP is configured at least by httpd or setup django-csp. See "
"https://djangosaml2.readthedocs.io/contents/security.html#content-security-policy"
" for more information")
else:
# script-src 'unsafe-inline' to autosubmit forms,
# form-action https: to send data to IdPs
saml2_csp_update = csp_update(
SCRIPT_SRC=["'unsafe-inline'"], FORM_ACTION=["https:"]
)


def _set_subject_id(session, subject_id):
session["_saml2_subject_id"] = code(subject_id)
Expand Down Expand Up @@ -123,6 +143,7 @@ def get_state_client(self, request: HttpRequest):
return state, client


@method_decorator(saml2_csp_update, name='dispatch')
class LoginView(SPConfigMixin, View):
"""SAML Authorization Request initiator.

Expand Down Expand Up @@ -671,6 +692,7 @@ def get(self, request, *args, **kwargs):
)


@method_decorator(saml2_csp_update, name='dispatch')
class LogoutInitView(LoginRequiredMixin, SPConfigMixin, View):
"""SAML Logout Request initiator

Expand Down Expand Up @@ -749,7 +771,7 @@ def handle_unsupported_slo_exception(self, request, exception, *args, **kwargs):
return HttpResponseRedirect(getattr(settings, "LOGOUT_REDIRECT_URL", "/"))


@method_decorator(csrf_exempt, name="dispatch")
@method_decorator([saml2_csp_update, csrf_exempt], name="dispatch")
class LogoutView(SPConfigMixin, View):
"""SAML Logout Response endpoint

Expand Down
35 changes: 35 additions & 0 deletions docs/source/contents/security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
Introduction
============

Authentication and Authorization are quite security relevant topics on its own.
Make sure you understand SAML2 and its implications, specifically the
separation of duties between Service Provider (SP) and Identity Provider (IdP):
this library aims to support a Service Provider in getting authenticated with
with one or more Identity Provider.

Communication between SP and IdP is routed via the Browser, eliminating the
need for direct communication between SP and IdP. However, for security the use
of cryptographic signatures (both while sending and receiving messages) must be
examined and the private keys in use must be kept closely guarded.

Content Security Policy
=======================

When using POST-Bindings, the Browser is presented with a small HTML-Form for
every redirect (both Login and Logout), which is sent using JavaScript and
sends the Data to the selected IdP. If your application uses technices such as
Content Security Policy, this might affect the calls. Since Version 1.9.0
djangosaml2 will detect if django-csp is installed and update the Content
Security Policy accordingly.

[Content Security Policy](https://content-security-policy.com/) is an important
HTTP-Extension to prevent User Input or other harmful sources from manipulating
application data. Usage is strongly advised, see
[OWASP Control](https://owasp.org/www-community/controls/Content_Security_Policy).

To enable CSP with [django-csp](https://django-csp.readthedocs.io/), simply
follow their [installation](https://django-csp.readthedocs.io/en/latest/installation.html)
and [configuration](https://django-csp.readthedocs.io/en/latest/configuration.html)
guides: djangosaml2 will automatically blend in and update the headers for
POST-bindings, so you must not include exceptions for djangosaml2 in your
global configuration.
6 changes: 6 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@ under the `Apache 2.0 <https://en.wikipedia.org/wiki/Apache_License>`_.
:caption: FAQ

contents/faq.md

.. toctree::
:maxdepth: 2
:caption: Security considerations

contents/security.md
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def read(*rnames):

setup(
name="djangosaml2",
version="1.8.0",
version="1.9.0",
description="pysaml2 integration for Django",
long_description=read("README.md"),
long_description_content_type="text/markdown",
Expand Down
Loading