[Django] #24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN

52 views
Skip to first unread message

Django

unread,
Mar 17, 2015, 6:07:30 AM3/17/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+--------------------
Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Keywords: csrf
Triage Stage: Unreviewed | Has patch: 1
Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------
Right now, if you try to share a CSRF token across a subdomain without
https, everything works great since the Referer header isn't validated.

But over https, we want to be a bit more strict and make sure that the
Referer is from another secure site, and also that the Referer matches
where we think it should be coming from. The canonical source for where we
think it should be from is `CSRF_COOKIE_DOMAIN`.

If we set our `CSRF_COOKIE_DOMAIN` to `.example.com`, that means our CSRF
validation should work for `www.example.com` and `foo.example.com`. Not
just strictly the domain the request is coming from.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Mar 19, 2015, 12:32:35 PM3/19/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+--------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:

Keywords: csrf | Triage Stage: Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------
Changes (by timgraham):

* needs_better_patch: => 0
* needs_tests: => 0
* needs_docs: => 0


Comment:

Could you provide a reference for "we want to be a bit more strict ..."?
For example, is this part of an RFC or something that other frameworks
implement?

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:1>

Django

unread,
Mar 19, 2015, 12:36:29 PM3/19/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+--------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:

Keywords: csrf | Triage Stage: Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by mattrobenolt):

I think the comment in this file explains it best:
{{{
# Suppose user visits http://example.com/
# An active network attacker (man-in-the-middle, MITM) sends a
# POST form that targets https://example.com/detonate-bomb/ and
# submits it via JavaScript.
#
# The attacker will need to provide a CSRF cookie and token, but
# that's no problem for a MITM and the session-independent
# nonce we're using. So the MITM can circumvent the CSRF
# protection. This is true for any HTTP connection, but anyone
# using HTTPS expects better! For this reason, for
# https://example.com/ we need additional protection that treats
# http://example.com/ as completely untrusted. Under HTTPS,
# Barth et al. found that the Referer header is missing for
# same-domain requests in only about 0.2% of cases or less, so
# we can use strict Referer checking.
}}}

https://github.com/django/django/blob/668d53cd125175eb708cc0af143f47b42cd42153/django/middleware/csrf.py#L135-L149

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:2>

Django

unread,
Mar 19, 2015, 12:40:08 PM3/19/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+--------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:

Keywords: csrf | Triage Stage: Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by carljm):

Tim, the "we want to be more strict" is something Django already
implements (checking `REFERER` against the request host on an HTTPS
request). Matt's feature request is for that check to be against
`CSRF_COOKIE_DOMAIN` rather than the request host, to allow for cross-
subdomain requests. I think this is reasonable, and probably the way it
should have worked from the beginning.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:3>

Django

unread,
Mar 19, 2015, 12:51:52 PM3/19/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+--------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:

Keywords: csrf | Triage Stage: Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+--------------------------------------

Comment (by mattrobenolt):

I've opened a discussion on the mailing list as suggested by timgraham
since this is related to security: https://groups.google.com/forum/#!topic
/django-developers/tEEw02RhV0M

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:4>

Django

unread,
Mar 19, 2015, 1:01:33 PM3/19/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted

Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by timgraham):

* stage: Unreviewed => Accepted


--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:5>

Django

unread,
Mar 19, 2015, 11:18:29 PM3/19/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by streeter):

* cc: django@… (added)


--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:6>

Django

unread,
Mar 20, 2015, 4:49:24 AM3/20/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------

Comment (by aaugustin):

Looks reasonable to me.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:7>

Django

unread,
Mar 25, 2015, 6:58:44 PM3/25/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by timgraham):

* needs_better_patch: 0 => 1


Comment:

Marking as "patch needs improvement" per Luke's comments.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:8>

Django

unread,
May 6, 2015, 9:27:18 AM5/6/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by timgraham):

* needs_better_patch: 1 => 0


--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:9>

Django

unread,
May 22, 2015, 9:49:32 AM5/22/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by timgraham):

* needs_better_patch: 0 => 1


--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:10>

Django

unread,
May 26, 2015, 8:47:11 AM5/26/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by timgraham):

* needs_better_patch: 1 => 0


--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:11>

Django

unread,
May 26, 2015, 4:37:51 PM5/26/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 1

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by timgraham):

* needs_docs: 0 => 1


--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:12>

Django

unread,
Jul 12, 2015, 1:32:32 PM7/12/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 1
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------

Comment (by sggottlieb):

Submitted a pull request to mattrobenolt that added some documentation for
his fix.

If we decide to go with Troy Grosfield's suggestion of adding a
CSRF_WHITELIST_ORIGINS setting (which I like), I can document that
instead.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:13>

Django

unread,
Aug 31, 2015, 10:45:47 PM8/31/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner: nobody
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 1
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by joshkehn):

* Attachment "24496.diff" added.

Django

unread,
Aug 31, 2015, 10:47:42 PM8/31/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by joshkehn):

* owner: nobody => joshkehn
* needs_docs: 1 => 0
* status: new => assigned


Old description:

> Right now, if you try to share a CSRF token across a subdomain without
> https, everything works great since the Referer header isn't validated.
>
> But over https, we want to be a bit more strict and make sure that the
> Referer is from another secure site, and also that the Referer matches
> where we think it should be coming from. The canonical source for where
> we think it should be from is `CSRF_COOKIE_DOMAIN`.
>
> If we set our `CSRF_COOKIE_DOMAIN` to `.example.com`, that means our CSRF
> validation should work for `www.example.com` and `foo.example.com`. Not
> just strictly the domain the request is coming from.

New description:

Right now, if you try to share a CSRF token across a subdomain without
https, everything works great since the Referer header isn't validated.

But over https, we want to be a bit more strict and make sure that the
Referer is from another secure site, and also that the Referer matches

where we think it should be coming from. Django should validate that the
Referer header matches one of the domains listed in
`CSRF_TRUSTED_ORIGINS`, including the currently responding `ALLOWED_HOST`.

--

Comment:

Based on the mailing list discussion here
https://groups.google.com/forum/#!topic/django-developers/eQeaNzSlSbw I'm
updating the ticket description and including a PR with tests and updated
documentation that allows expanding the allowed HTTP Referer domains via a
setting called `CSRF_TRUSTED_ORIGINS`.

PR #5218 is open: https://github.com/django/django/pull/5218

A patch has been added to this ticket as well.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:14>

Django

unread,
Sep 1, 2015, 10:31:12 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------

Comment (by carljm):

The new patch looks good, thanks Josh! After giving it some more thought
overnight, I think we should actually combine the two proposed patches for
this ticket. `CSRF_TRUSTED_ORIGINS` is broader in scope for true cross-
origin requests from different domains, but using `CSRF_COOKIE_DOMAIN` as
the default (if set) is just sensible, and easier if you have quite a few
subdomains that should all be trusted (since we don't have any sort of
wildcarding in `CSRF_TRUSTED_ORIGINS`).

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:15>

Django

unread,
Sep 1, 2015, 10:55:36 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------

Comment (by joshkehn):

Updated PR based on comments, I'll update the attached patch as well after
this comment.

Regarding including the `CSRF_COOKIE_DOMAIN`, wouldn't that be required
anyways for the request to pass `ALLOWED_HOSTS`? I'm thinking that should
be checked after the `CSRF_TRUSTED_ORIGINS` check fails, correct? I'm
attaching two flows.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:16>

Django

unread,
Sep 1, 2015, 10:56:24 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by joshkehn):

* Attachment "24496.diff" added.

Django

unread,
Sep 1, 2015, 10:58:12 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------

Comment (by joshkehn):

Existing flow (with proposed patch): http://l.kehn.io/image/0z2X3j2k082c

`CSRF_COOKIE_DOMAIN` flow: http://l.kehn.io/image/3p3J142b3j3H

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:17>

Django

unread,
Sep 1, 2015, 10:59:56 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by joshkehn):

* Attachment "Cookie Domain Flow.png" added.

New CSRF_COOKIE_DOMAIN flow

Django

unread,
Sep 1, 2015, 11:00:10 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by joshkehn):

* Attachment "Existing Flow (with patch).png" added.

Existing (with patch) flow

Django

unread,
Sep 1, 2015, 11:12:09 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------

Comment (by carljm):

I think the logic should look more like "construct a list of valid
referers, where that list consists of X plus CSRF_TRUSTED_ORIGINS, where X
is CSRF_COOKIE_DOMAIN if set and otherwise request.get_host()".

In other words, it's basically the same logic as in your current PR,
except that if CSRF_COOKIE_DOMAIN is set, it replaces request.get_host()
entirely. But you'll also need the other changes in Matt's pull request in
order to get the correct host matching (where initial dot allows
subdomains).

I would probably start with Matt's PR and layer the addition of
CSRF_TRUSTED_ORIGINS on top of that.

We could even merge them as separate fixes? Maybe I was wrong to suggest
doing them both in this ticket.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:18>

Django

unread,
Sep 1, 2015, 11:23:27 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------

Comment (by joshkehn):

I think the domain check complicates matters enough to warrant a separate
fix. I've essentially repurposed this ticket to accommodate the
`CSRF_TRUSTED_ORIGINS` additions so I'd suggest creating a new ticket.

I don't know about the `get_host()` vs. `CSRF_COOKIE_DOMAIN` check. Let's
say `CSRF_COOKIE_DOMAIN` is set to `.example.com` and `ALLOWED_HOSTS` is
set to `['api.example.com', 'api-dev.example.com']`. To allow cross origin
requests also set `CSRF_TRUSTED_ORIGINS` to `['dashboard.example.com']`.

At this point Django would allow a request referred from
`badhost.example.com`, since it's part of the `CSRF_TRUSTED_ORIGINS` +
`CSRF_COOKIE_DOMAIN`. Is that acceptable?

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:19>

Django

unread,
Sep 1, 2015, 11:29:50 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------

Comment (by carljm):

Replying to [comment:19 joshkehn]:


> I think the domain check complicates matters enough to warrant a
separate fix. I've essentially repurposed this ticket to accommodate the
`CSRF_TRUSTED_ORIGINS` additions so I'd suggest creating a new ticket.

I think instead we should revert the changes to ticket title and
description here, and open a new ticket for `CSRF_TRUSTED_ORIGINS`, since
this ticket was originally about `CSRF_COOKIE_DOMAIN` and that's its
history. Sorry I led you down the wrong path yesterday.

> I don't know about the `get_host()` vs. `CSRF_COOKIE_DOMAIN` check.
Let's say `CSRF_COOKIE_DOMAIN` is set to `.example.com` and
`ALLOWED_HOSTS` is set to `['api.example.com', 'api-dev.example.com']`. To
allow cross origin requests also set `CSRF_TRUSTED_ORIGINS` to
`['dashboard.example.com']`.
>
> At this point Django would allow a request referred from
`badhost.example.com`, since it's part of the `CSRF_TRUSTED_ORIGINS` +
`CSRF_COOKIE_DOMAIN`. Is that acceptable?

Yes, that's fine. `ALLOWED_HOSTS` is not relevant here. If
`CSRF_COOKIE_DOMAIN` is set to `.example.com`, that's a declaration that
all subdomains of `.example.com` are trusted to submit unsafe requests
(well, they still have to pass the CSRF token check, but they're trusted
enough to pass the referrer check).

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:20>

Django

unread,
Sep 1, 2015, 11:34:15 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master

Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------

Comment (by joshkehn):

Right, my question was with `CSRF_TRUSTED_ORIGINS` explicitly set wouldn't
someone reasonably assume that it's a filter on top of
`CSRF_COOKIE_DOMAIN`?

Example: Set `CSRF_COOKIE_DOMAIN` to `.example.com` for convenience of
configuration so that the cookie is shared among any subdomain. Set
`CSRF_TRUSTED_ORIGINS` to only allow certain referers to pass the CSRF
checks.

My suggestion would be to only use `CSRF_COOKIE_DOMAIN` if
`CSRF_TRUSTED_ORIGINS` is empty and make that a separate branch of logic.

Carl, I'm not sure if there's an easy way to split out into a new ticket.
Do you have one? If not, I can manually do it a bit later today.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:21>

Django

unread,
Sep 1, 2015, 11:53:35 AM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_TRUSTED_ORIGINS
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------

Comment (by carljm):

Replying to [comment:21 joshkehn]:


> Right, my question was with `CSRF_TRUSTED_ORIGINS` explicitly set
wouldn't someone reasonably assume that it's a filter on top of
`CSRF_COOKIE_DOMAIN`?
>
> Example: Set `CSRF_COOKIE_DOMAIN` to `.example.com` for convenience of
configuration so that the cookie is shared among any subdomain. Set
`CSRF_TRUSTED_ORIGINS` to only allow certain referers to pass the CSRF
checks.
>
> My suggestion would be to only use `CSRF_COOKIE_DOMAIN` if
`CSRF_TRUSTED_ORIGINS` is empty and make that a separate branch of logic.

It's a reasonable point, but I don't think so. Any host you are willing to
share the CSRF cookie with is implicitly trusted to make use of it.
Sharing the cookie is actually already a much deeper level of trust than
that provided by CSRF_TRUSTED_ORIGINS alone, because it allows that host
to get past both parts of the CSRF check, referrer and token.
CSRF_TRUSTED_ORIGINS is a technique for adding _additional_ hosts who can
bypass the referrer check, it's not a mechanism for restriction.

With your proposed behavior, it would be impossible to both have many
wildcarded subdomains AND an external CORS-using host. That would be
pretty frustrating.

Certainly the interaction of the two settings should be documented
clearly.

> Carl, I'm not sure if there's an easy way to split out into a new
ticket. Do you have one? If not, I can manually do it a bit later today.

No, there's no secret technique for that. Shouldn't be too hard; I
wouldn't try to copy all the comments or anything. Just open a new ticket
and say "some previous discussion in #24496", and re-title your pull
request so it gets linked up with the new ticket.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:22>

Django

unread,
Sep 1, 2015, 12:06:03 PM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN

------------------------------+------------------------------------
Reporter: mattrobenolt | Owner: joshkehn
Type: New feature | Status: assigned
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 1

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by carljm):

* needs_docs: 0 => 1


Old description:

> Right now, if you try to share a CSRF token across a subdomain without
> https, everything works great since the Referer header isn't validated.
>
> But over https, we want to be a bit more strict and make sure that the
> Referer is from another secure site, and also that the Referer matches

> where we think it should be coming from. Django should validate that the
> Referer header matches one of the domains listed in
> `CSRF_TRUSTED_ORIGINS`, including the currently responding
> `ALLOWED_HOST`.

New description:

Right now, if you try to share a CSRF token across a subdomain without
https, everything works great since the Referer header isn't validated.

But over https, we want to be a bit more strict and make sure that the
Referer is from another secure site, and also that the Referer matches

where we think it should be coming from. The canonical source for where we
think it should be from is `CSRF_COOKIE_DOMAIN`.

If we set our `CSRF_COOKIE_DOMAIN` to `.example.com`, that means our CSRF
validation should work for `www.example.com` and `foo.example.com`. Not
just strictly the domain the request is coming from.

--

Comment:

Resetting the title/summary of this ticket to again be specific to the
`CSRF_COOKIE_DOMAIN` fix. Also resetting the "needs docs" flag, since I
think that's all that the original PR for that
(https://github.com/django/django/pull/4337) is waiting on.

Sorry again for causing confusion and extra work by mixing these two
slightly different issues together in one ticket.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:23>

Django

unread,
Sep 1, 2015, 12:10:26 PM9/1/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner:
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 1
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by joshkehn):

* owner: joshkehn =>
* status: assigned => new


Comment:

Done, thanks Carl.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:24>

Django

unread,
Sep 2, 2015, 10:30:46 AM9/2/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
-------------------------------------+-------------------------------------

Reporter: mattrobenolt | Owner:
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by timgraham):

* needs_docs: 1 => 0

* stage: Accepted => Ready for checkin


Comment:

Pending some release notes and a `versionchanged` annotation.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:25>

Django

unread,
Sep 5, 2015, 9:27:28 AM9/5/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------

Reporter: mattrobenolt | Owner:
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 1
Needs tests: 0 | Patch needs improvement: 1

Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by timgraham):

* needs_better_patch: 0 => 1
* stage: Ready for checkin => Accepted


* needs_docs: 0 => 1


Comment:

Patch now doesn't merge cleanly either.

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:26>

Django

unread,
Sep 7, 2015, 8:56:01 AM9/7/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner:
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf 1.9 | Triage Stage: Accepted

Has patch: 1 | Needs documentation: 1
Needs tests: 0 | Patch needs improvement: 1
Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by timgraham):

* keywords: csrf => csrf 1.9


--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:27>

Django

unread,
Sep 14, 2015, 7:51:36 PM9/14/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
------------------------------+------------------------------------
Reporter: mattrobenolt | Owner:
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf 1.9 | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
------------------------------+------------------------------------
Changes (by timgraham):

* needs_better_patch: 1 => 0


* needs_docs: 1 => 0


Comment:

[https://github.com/django/django/pull/5286 Updated PR]

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:28>

Django

unread,
Sep 16, 2015, 12:45:11 PM9/16/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
-------------------------------------+-------------------------------------

Reporter: mattrobenolt | Owner:
Type: New feature | Status: new
Component: CSRF | Version: master
Severity: Normal | Resolution:
Keywords: csrf 1.9 | Triage Stage: Ready for
| checkin

Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by timgraham):

* stage: Accepted => Ready for checkin


--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:29>

Django

unread,
Sep 16, 2015, 1:13:27 PM9/16/15
to django-...@googlegroups.com
#24496: Check CSRF Referer against CSRF_COOKIE_DOMAIN
-------------------------------------+-------------------------------------
Reporter: mattrobenolt | Owner: Tim
| Graham <timograham@…>
Type: New feature | Status: closed
Component: CSRF | Version: master
Severity: Normal | Resolution: fixed

Keywords: csrf 1.9 | Triage Stage: Ready for
| checkin
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Tim Graham <timograham@…>):

* status: new => closed
* owner: => Tim Graham <timograham@…>
* resolution: => fixed


Comment:

In [changeset:"b0c56b895fd2694d7f5d4595bdbbc41916607f45" b0c56b89]:
{{{
#!CommitTicketReference repository=""
revision="b0c56b895fd2694d7f5d4595bdbbc41916607f45"
Fixed #24496 -- Added CSRF Referer checking against CSRF_COOKIE_DOMAIN.

Thanks Seth Gottlieb for help with the documentation and
Carl Meyer and Joshua Kehn for reviews.
}}}

--
Ticket URL: <https://code.djangoproject.com/ticket/24496#comment:30>

Reply all
Reply to author
Forward
0 new messages