Skip to content

DEP0021 -- Subresource Integrity (SRI) - #122

Open
codingjoe wants to merge 4 commits into
django:mainfrom
codingjoe:sri
Open

codingjoe wants to merge 4 commits into
django:mainfrom
codingjoe:sri

Conversation

@codingjoe

@codingjoe codingjoe commented Sep 24, 2026 •

Copy link
Copy Markdown

Ref django/new-features#125

Product of Djanog on the Med 2026

@codingjoe codingjoe changed the title Added Subresource Integrity (SRI) Subresource Integrity (SRI) Sep 24, 2026
@codingjoe codingjoe changed the title Subresource Integrity (SRI) DEP0021 -- Subresource Integrity (SRI) Sep 24, 2026
@codingjoe
codingjoe marked this pull request as ready for review September 25, 2026 07:14

@matthiask matthiask left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

One general comment: The hash cache might have to hook into the autoreloader in development, or not do any caching at all, because hashes might change all the time there when editing static files.

Comment thread draft/0021-subresource-integrity.md Outdated
Comment thread draft/0021-subresource-integrity.md
Comment thread draft/0021-subresource-integrity.md Outdated
Comment thread draft/0021-subresource-integrity.md Outdated

@RealOrangeOne RealOrangeOne left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

As the author of django-sri, I absolutely love this! Great work all! I had to do some bodges to get this working 3rd party, so built in avoids a lot of that.

```python
class Storage:
def integrity(self, path) -> str:
"""Return 1..N space-separated <algorithm>-<base64-digest> pairs."""

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thought: should the storage return an intermediary format, for easier internal use? Eg a dict? Otherwise it couples the storage API to the HTML format

@matthiask matthiask Sep 28, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We discussed this at the sprint. My initial hunch was to use a dict for this with the algorithm as the key and the hash as the value. The problem is that SRI allows using multiple hashes with the same algorithm. Which means either using a list of tuples or a multi value dict or something like this.

It seems not worth it given that people can just do hashes.split() when they really want to look at individual values. The format is easy to parse after all.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, we argued that except for the enthusiastic third-party vendors like us, nobody will care about what's in the string as long as it's a valid policy that can be dropped into the browser. 🤷

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

multiple hashes with the same algorithm

Those will always be duplicates though right? If the algorithm is the same, so will the hash, so duplicate entries in the dict wouldn't make sense.

Being able to access a file's "integrity" feels like a useful concept for reasons outside SRI. Coupling the implementation to SRI (the initial implementation) sounds like it hinders flexibility.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

multiple hashes with the same algorithm

Those will always be duplicates though right? If the algorithm is the same, so will the hash, so duplicate entries in the dict wouldn't make sense.

I believe not. You can deliver different files to different clients, for example when doing rolling deployments or something like that, and include the hashes of all possible variants.

@codingjoe codingjoe Oct 9, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

They don't have to be identical. A server may service different resources based on the request. For example, it could vary on User-Agent for polyfills. So Interator[tuple[str,str]] just like an HTTP header.

This is also now documented on MDN correctly. We updated the example to reflect the duplication, see also https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity#setting_the_integrity_attribute

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I keep just assuming static files are immutable 😅 Ok that does make sense. In that case, returning the Iterable[tuple[str, str]] feels better to me, so the format isn't tied to the browser representation.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Since I don't want users to pipe this through a method to render it in a template, I'd suggest we add explicit types that allow you to access them individually, but str renders the integrity metadata according to spec, see also: https://www.w3.org/TR/sri-2/#integrity-metadata

So we get the best of both worlds :)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I extended the draft to use stringable objects, which store the raw data. Please review. Its certainly more complicated, but a normal user shouldn't notice.

## Specification

### Phase 1: Providing integrity digests

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Question: Whilst the API will ship, do you imagine all built-in storage back ends implementing this in phase 1?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, that was the idea. And to my knowledge, whitenoise inherits from Django's manifest backend. And the hashing methods could be reused by 3rd-party vendors.

cc @jschneier

Comment thread draft/0021-subresource-integrity.md Outdated
Comment thread draft/0021-subresource-integrity.md
Comment thread draft/0021-subresource-integrity.md Outdated
- `check_integrity_policy_settings()` reports an `Error` when a setting is not a
mapping, or when `build_policy()` rejects it. `manage.py check` catches a bad
policy instead of a request.
- `check_integrity_policy_debug()` warns when a policy is enforced with `DEBUG`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Question: Why is this a bad thing worth warning about? If cached properly, it should work fine. Otherwise the warning implies it can't (or shouldn't) be tested locally


### Adoption

1. Serve static files from a storage that can produce integrity digests, or run

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

if the storage pre-computes them

Question: will Django in this phase? This will need a manifest version rev (and bump its size a fair bit)

CORS-enabled request, and browsers refuse a `no-cors` request that carries
integrity metadata.[^cors] So `integrity` without `crossorigin` blocks the
file. Does the render path add `crossorigin` when `STATIC_URL` has a host, or
does the project pass it per asset?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Question: can't crossorigin=anonymous be added by default? That should be a safe default

a client on slow hardware stays free to take the short digest. Third-party
storages can return any number.

Attributes on by default. A feature behind an opt-in setting stays unused, and the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Question: is the intended use that people who don't want attributes output override the storage backend and return no integrity data?

@codingjoe

codingjoe commented Oct 5, 2026 •

Copy link
Copy Markdown
Author

FYI https://bugs.webkit.org/show_bug.cgi?id=326341, Safari has a bug and ignores the integrity hashes in importmaps when trying to load ES modules. Speaks for the poor adoption and confirms that we really need to push this.

cc @matthiask @RealOrangeOne

Comment on lines +71 to +77
class IntegrityMetadataList(list[IntegrityMetadata]):

def __str__(self):
return " ".join(str(metadata) for metadata in self)

def __html__(self):
return str(self)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Question: Why the extra class for the collection? It feels like a lot of extra code to avoid a few " ".join(str(metadata) for metadata in self) in a few places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants