Opened 3 weeks ago

Closed 13 days ago

Last modified 13 days ago

#37174 closed Bug (fixed)

Template fragment cache key collision for vary_on values containing ":"

Reported by: Jacob Walls Owned by: Jacob Walls
Component: Template system Version: 6.0
Severity: Normal Keywords: not-security
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

The documented example for template fragment caching demonstrates using request.user.username as a vary_on argument. If that username contained :, and another vary_on argument was present, then two cache keys might collide, and the wrong content could be served.

See this PoC provided to the Security Team:

from django.core.cache.utils import make_template_fragment_key
a = make_template_fragment_key("frag", ["alice", "b:c"])
b = make_template_fragment_key("frag", ["alice:b", "c"])
assert a == b   # same key

We decided against accepting this as a security issue given the unlikelihood of colons in the data most important to vary on from a security perspective, e.g. usernames in a username + language code vary_on pair, but there is a correctness issue to fix here.

One fix strategy would involve incorporating the lengths of the arguments into the cache key.

Since this will cause cache busting, we should probably document in the release note something similar to the note from 5cb3ed187b283059589cb442c56a66a795800cac.

Change History (14)

comment:1 by Natalia Bidart, 3 weeks ago

Triage Stage: UnreviewedAccepted

Thanks!

comment:2 by Amar, 3 weeks ago

Owner: set to Amar
Status: newassigned

comment:3 by Jacob Walls, 3 weeks ago

Owner: changed from Amar to Jacob Walls

Amar, this one is almost identical to #37101, so if you don't mind, I'm going to fix them both in the same PR to aid review.

comment:4 by Amar, 3 weeks ago

No worries at all, Jacob! You are welcome to take it. A combined PR makes perfect sense here.

comment:5 by Jacob Walls, 3 weeks ago

Has patch: set

comment:6 by Juan Pedro Roldán, 2 weeks ago

Hi,

I was reviewing this ticket while looking for a small code-related contribution to understand Django’s contribution workflow. I noticed that the ticket is already assigned, so I won’t work on it directly.

I tried to reproduce the issue locally using the example from the ticket description:

make_template_fragment_key("frag", ["alice", "b:c"])

and

make_template_fragment_key("frag", ["alice:b", "c"])

Both calls generated the same cache key:

template.cache.frag.486f15e693cb74d155ff00eb53e7e306

I also added a small local regression test in tests/cache/tests.py, inside TestMakeTemplateFragmentKey, to check that these two keys should be different. As expected, the test fails on the current branch, so the reported behavior is reproduced.

I hope this helps with testing or triage. I’d be happy to test a proposed patch if needed.

Last edited 2 weeks ago by Juan Pedro Roldán (previous) (diff)

comment:7 by Jacob Walls, 2 weeks ago

Hi there. The attached PR is almost mergable, awaiting re-review on the release note. Any comments welcome!

comment:8 by Natalia Bidart, 2 weeks ago

Triage Stage: AcceptedReady for checkin

comment:9 by Jacob Walls <jacobtylerwalls@…>, 13 days ago

Resolution: fixed
Status: assignedclosed

In 8acd000:

Fixed #37174 -- Used netstring delimiter between vary on arguments for cached template fragments.

This algorithm used a delimiter, but because it didn't use a length,
argument values containing the delimiter could still cause collisions.

comment:10 by Jacob Walls <jacobtylerwalls@…>, 13 days ago

In 02f94d2:

Refs #37101, #37174 -- Added release note for cache misses when varying on arguments.

Thanks Natalia Bidart for the review.

comment:11 by Jacob Walls <jacobtylerwalls@…>, 13 days ago

In 0383d12:

Refs #37174 -- Doc'd that args to the {% cache %} tag are stringified.

comment:12 by Jacob Walls <jacobtylerwalls@…>, 13 days ago

In 0317b61:

[6.1.x] Fixed #37174 -- Used netstring delimiter between vary on arguments for cached template fragments.

This algorithm used a delimiter, but because it didn't use a length,
argument values containing the delimiter could still cause collisions.

Backport of 8acd000725838f73c5e6781114f11866f6674c46 from main.

comment:13 by Jacob Walls <jacobtylerwalls@…>, 13 days ago

In e4cda2e:

[6.1.x] Refs #37101, #37174 -- Added release note for cache misses when varying on arguments.

Thanks Natalia Bidart for the review.

Backport of 02f94d2a899b6573f90dee5417afb8a763ee7f25 from main.

comment:14 by Jacob Walls <jacobtylerwalls@…>, 13 days ago

In d5ff018:

[6.1.x] Refs #37174 -- Doc'd that args to the {% cache %} tag are stringified.

Backport of 0383d12ea3451a75be184bd1783f11031903e7ee from main.

Note: See TracTickets for help on using tickets.
Back to Top