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

Disabling server-initiated integrity checks #27

Open
ddworken opened this issue Dec 5, 2024 · 6 comments
Open

Disabling server-initiated integrity checks #27

ddworken opened this issue Dec 5, 2024 · 6 comments

Comments

@ddworken
Copy link
Contributor

ddworken commented Dec 5, 2024

Currently, if a server sets signature-based SRI headers on a response, the signature is validated even if the request has not specified an integrity value. This is a useful capability since it allows services hosting JS to enable signature-based SRI as a by-default defense mechanism against tampering.

But I'm wondering if we should offer a toggle to disable this? Enabling integrity checks for all requests may or may not be the right behavior in all circumstances, so it seems like it could make sense to make this configurable. I also think this is especially important if we don't pursue #21 since there should be a way for servers to enable SRI for a subset of clients without impacting other clients.

@mikewest
Copy link
Member

mikewest commented Dec 5, 2024

  1. We can certainly make this optional, possibly by creating a new tag value for the Signature-Input header (though that also changes the signature, so we might want some mechanism that doesn't show up in the signature base? Not sure what that would be... perhaps a parameter on the signature itself? Perhaps a distinct header? Did you have a preferred spelling?

  2. When would it be the wrong behavior to reject signed resources whose signatures can't be verified? My intuition is that servers that are making assertions about the resource's provenance/integrity are doing so because those assertions are in some way load-bearing for the security posture they'd like to achieve. The reference to Public keys in request headers #21 doesn't really help me understand, as clients that don't support this mechanism presumably also wouldn't support the server-initiated checks in the first place. Delivering signatures to those clients wouldn't have any negative effect, would it?

@ddworken
Copy link
Contributor Author

ddworken commented Dec 5, 2024

  1. We can certainly make this optional, possibly by creating a new tag value for the Signature-Input header (though that also changes the signature, so we might want some mechanism that doesn't show up in the signature base? Not sure what that would be... perhaps a parameter on the signature itself? Perhaps a distinct header? Did you have a preferred spelling?

Yeah, I'd prefer for this not to be part of the signature input, so that this parameter can be changed at runtime without having to re-sign anything. In terms of the preferred spelling, I'd lean towards just putting it in a separate header: E.g. something like Validate-Signature: false.

  1. When would it be the wrong behavior to reject signed resources whose signatures can't be verified? My intuition is that servers that are making assertions about the resource's provenance/integrity are doing so because those assertions are in some way load-bearing for the security posture they'd like to achieve. The reference to Public keys in request headers #21 doesn't really help me understand, as clients that don't support this mechanism presumably also wouldn't support the server-initiated checks in the first place. Delivering signatures to those clients wouldn't have any negative effect, would it?

With the current spec, rolling out signature-based SRI would consist of:

  1. Attach signatures to 100% of responses, triggering server-initiated integrity checks
  2. Gradually update clients (i.e. the websites including the JS resources) to pin to a specific public key

This is riskier than necessary since step 1 means starting to validate integrity even for existing clients, which could lead to breakages (or performance degradation). #21 and this issue make it possible to have a safer rollout strategy consisting of:

  1. Add support for attaching signatures, but only attach them if the client requests them (via Public keys in request headers #21). This is a safe change.
  2. Gradually update clients to pin to a specific public key, which will trigger the backend to attach signatures. This way the breakage risk is constrained to individual clients who can rollback the change as needed.
  3. Gradually roll out attaching signatures even for non-opted-in clients but with Validate-Signature: false. This should be a safe no-op.
  4. Gradually roll out Validate-Signature: true. This should be safe and can be rolled back if any issues are reported.

@mikewest
Copy link
Member

mikewest commented Dec 6, 2024

Yeah, I'd prefer for this not to be part of the signature input, so that this parameter can be changed at runtime without having to re-sign anything. In terms of the preferred spelling, I'd lean towards just putting it in a separate header: E.g. something like Validate-Signature: false.

Ok, sounds reasonable, but I don't feel like I understand the rollout strategy this would support. The system as it's currently designed has three parts, each of which is enforced in parallel in order to give the guarantees at the end of the day:

  1. Identity-Digest makes an assertion about the resource content, and we enforce that constraint prior to script execution.
  2. Signature and Signature-Input bundle up properties of the request and response, including the Identity-Digest header, and we enforce validation through the key they include.
  3. <script integrity> asserts a set of keys that are expected for a given resource.

Do you need the ability to make all of those optional? It's not clear what you'd want the user agent to do in a case where one of more of the constraints isn't enforced. Perhaps this is wrapped up in the reporting discussion from #28?

With the current spec, rolling out signature-based SRI would consist of:

  1. Attach signatures to 100% of responses, triggering server-initiated integrity checks

I agree that pushing to 100% tomorrow would be somewhat risky, but I'd imagine your actual plan here as being just as gradual as the next step: you can roll out message signature headers to a subset of the population, gather experimental data about client-side error rates and performance metrics, and ramp the rollout accordingly.

Performance data in particular seems like it would be unaffected by enforcement, since the core costs you'll incur are parsing and signature validation on the one hand, and generating a digest of the response body on the other. You'd pay those costs in any event, assuming you'd want reports, so you'd want to ramp over time.

  1. Gradually update clients (i.e. the websites including the JS resources) to pin to a specific public key

This is riskier than necessary since step 1 means starting to validate integrity even for existing clients, which could lead to breakages (or performance degradation).

I have a different intuition here. Presumably you'd be able to ramp signature attachment to 100% over time, giving you the opportunity to monitor performance regressions, gain confidence in your signature pipeline, etc.

#21 and this issue make it possible to have a safer rollout strategy consisting of:

  1. Add support for attaching signatures, but only attach them if the client requests them (via Public keys in request headers #21). This is a safe change.
  2. Gradually update clients to pin to a specific public key, which will trigger the backend to attach signatures. This way the breakage risk is constrained to individual clients who can rollback the change as needed.

This sounds to me like the riskiest way to start. Rather than validating the signature mechanism in itself, you'd only attach signatures when failure meant breakage.

  1. Gradually roll out attaching signatures even for non-opted-in clients but with Validate-Signature: false. This should be a safe no-op.
  2. Gradually roll out Validate-Signature: true. This should be safe and can be rolled back if any issues are reported.

As above, can you help me understand what you want Validate-Signature to do? It sounds like you want it to turn off SRI enforcement on the client, such that both <script integrity> and Validate-Signature: true need to be present in order for enforcement to take place. Is that right?

@ddworken
Copy link
Contributor Author

ddworken commented Dec 7, 2024

As above, can you help me understand what you want Validate-Signature to do? It sounds like you want it to turn off SRI enforcement on the client, such that both <script integrity> and Validate-Signature: true need to be present in order for enforcement to take place. Is that right?

Yeah, that is what I had in mind.

The system as it's currently designed has three parts, each of which is enforced in parallel in order to give the guarantees at the end of the day:

  1. Identity-Digest makes an assertion about the resource content, and we enforce that constraint prior to script execution.
  2. Signature and Signature-Input bundle up properties of the request and response, including the Identity-Digest header, and we enforce validation through the key they include.
  3. <script integrity> asserts a set of keys that are expected for a given resource.

Do you need the ability to make all of those optional? It's not clear what you'd want the user agent to do in a case where one of more of the constraints isn't enforced. Perhaps this is wrapped up in the reporting discussion from #28?

Ah, I hadn't realized that Identity-Digest was enforced independently of the signatures. But my guess is that we don't need the ability to be granular about what we're opting out of, and we really just want a response header to opt-out of server-initiated checks altogether (so 1 and 2 from the above).

And just to be clear, once a site sets <script integrity> then I don't think there should be any way for a site to opt-out of that.

This is riskier than necessary since step 1 means starting to validate integrity even for existing clients, which could lead to breakages (or performance degradation).

I have a different intuition here. Presumably you'd be able to ramp signature attachment to 100% over time, giving you the opportunity to monitor performance regressions, gain confidence in your signature pipeline, etc.

The concern here is also about performance regressions on the client-side, which is harder to monitor. So overall I do think it would be quite reasonable for sites to say that they only care about client-initiated integrity checks (since those are the ones that deliver the real security value), and that they don't want to have server-initiated integrity checks.

@mikewest
Copy link
Member

mikewest commented Dec 7, 2024

As above, can you help me understand what you want Validate-Signature to do? It sounds like you want it to turn off SRI enforcement on the client, such that both <script integrity> and Validate-Signature: true need to be present in order for enforcement to take place. Is that right?

Yeah, that is what I had in mind.

This seems to be in tension with the suggestion below that "... once a site sets <script integrity> then I don't think there should be any way for a site to opt-out of that."

I think what you're suggesting is something like this:

  1. If a server sends signed responses and disables enforcement, and the page requesting the resource does not assert any integrity requirements, then the headers are simply ignored. They have no effect on the client, no hashing or verification is done, etc.

  2. If a server sends signed responses and disables enforcement, but the page requesting the resource asserts integrity requirements, then the response ... is allowed because the page requested enforcement of something the server asked us not to enforce? Is blocked because the page's requirements take precedent?

Help me out with the second scenario? :)

For the first scenario, it sounds like there's no delta between simply not sending the headers, and sending the headers with another header that disables them. Why add the complication?

This is riskier than necessary since step 1 means starting to validate integrity even for existing clients, which could lead to breakages (or performance degradation).

I have a different intuition here. Presumably you'd be able to ramp signature attachment to 100% over time, giving you the opportunity to monitor performance regressions, gain confidence in your signature pipeline, etc.

The concern here is also about performance regressions on the client-side, which is harder to monitor.

Presumably you'll want to hook into the PerformanceResourceTiming data you'd get from Performance.getEntriesByType() (and I guess the timing of the script's onload event?) to obtain information about the performance impact on the client. @yoavweiss probably has more experience/better ideas here than I do.

So overall I do think it would be quite reasonable for sites to say that they only care about client-initiated integrity checks (since those are the ones that deliver the real security value), and that they don't want to have server-initiated integrity checks.

Assuming we gave you what you're asking for in #21, you could get this same result by simply not sending the headers unless they're asked for. It's not clear to me why that's not a solution you're happy with.

@ddworken
Copy link
Contributor Author

ddworken commented Dec 9, 2024

  1. If a server sends signed responses and disables enforcement, but the page requesting the resource asserts integrity requirements, then the response ... is allowed because the page requested enforcement of something the server asked us not to enforce? Is blocked because the page's requirements take precedent?

Then the response should be checked against the signed response. So if the integrity check passes, then it is allowed. Otherwise, it is blocked.

This header would only be about turning off the server-initiated checks. Client-initiated checks would still be processed.

For the first scenario, it sounds like there's no delta between simply not sending the headers, and sending the headers with another header that disables them. Why add the complication?

Yeah, this is true iff we address #21. But without #21, the server doesn't know if client-initiated checks are enabled so it has to always send the headers.

Assuming we gave you what you're asking for in #21, you could get this same result by simply not sending the headers unless they're asked for. It's not clear to me why that's not a solution you're happy with.

I think you are right. So if we fix #21, closing this out as won't fix seems reasonable to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants