Skip to content
/ orb Public

Opaque Response Blocking (CORB++)

License

Notifications You must be signed in to change notification settings

annevk/orb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 

Repository files navigation

Opaque Response Blocking (ORB, aka CORB++)

Status

This repository is being upstreamed to the Fetch Standard through PR #1442. As indicated there you can preview a version of the Fetch Standard with the new text integrated.

The PR is primarily blocked on resolving the mvp issues. Help appreciated!

The PR is in a more advanced state than the text below and should be the starting point for implementers and reviewers.

Objective

To block as many opaque responses as possible while remaining web compatible.

High-level idea

CSS, JavaScript, images, and media (audio and video) can be requested across origins without CORS. Except for CSS there is no MIME type enforcement. Ideally we still block as many responses as possible that are not one of these types to avoid leaking their contents through side channels.

Processing model

New MIME type sets

An opaque-safelisted MIME type is a JavaScript MIME type or a MIME type whose essence is "text/css" or "image/svg+xml".

An opaque-blocklisted MIME type is an HTML MIME type, JSON MIME type, or XML MIME type.

An opaque-blocklisted-never-sniffed MIME type is a MIME type whose essence is one of

  • "application/dash+xml"
  • "application/gzip"
  • "application/msexcel"
  • "application/mspowerpoint"
  • "application/msword"
  • "application/msword-template"
  • "application/pdf"
  • "application/vnd.apple.mpegurl"
  • "application/vnd.ces-quickpoint"
  • "application/vnd.ces-quicksheet"
  • "application/vnd.ces-quickword"
  • "application/vnd.ms-excel"
  • "application/vnd.ms-excel.sheet.macroenabled.12"
  • "application/vnd.ms-powerpoint"
  • "application/vnd.ms-powerpoint.presentation.macroenabled.12"
  • "application/vnd.ms-word"
  • "application/vnd.ms-word.document.12"
  • "application/vnd.ms-word.document.macroenabled.12"
  • "application/vnd.msword"
  • "application/vnd.openxmlformats-officedocument.presentationml.presentation"
  • "application/vnd.openxmlformats-officedocument.presentationml.template"
  • "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
  • "application/vnd.openxmlformats-officedocument.spreadsheetml.template"
  • "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
  • "application/vnd.openxmlformats-officedocument.wordprocessingml.template"
  • "application/vnd.presentation-openxml"
  • "application/vnd.presentation-openxmlm"
  • "application/vnd.spreadsheet-openxml"
  • "application/vnd.wordprocessing-openxml"
  • "application/x-gzip"
  • "application/x-protobuf"
  • "application/x-protobuffer"
  • "application/zip"
  • "audio/mpegurl"
  • "multipart/byteranges"
  • "multipart/signed"
  • "text/event-stream"
  • "text/csv"
  • "text/vtt"

Changes to requests and media elements

A request has an associated no-cors media request state ("N/A", "initial", or "subsequent"). It is "N/A" unless explicitly stated otherwise.

We adjust the way media element fetching is done to more clearly separate between the initial and any subsequent range fetches:

  • For its initial range request a media element sets request's no-cors media request state to "initial" and it follows redirects. That yields (after any redirects) an initial response.
  • For its subsequent range requests the URL of the initial response is used as request's URL, it no longer follows redirects, and request's no-cors media request state is set to "subsequent". Note: redirects here resulted in an error in Chrome until recently. We could somewhat easily allow same-origin redirects by adjusting the check performed against request's no-cors media request state, but it's not clear that's desirable.

(These changes are not needed when CORS is used, but it might make sense to align these somewhat, to the extent they are not already.)

ORB's algorithm

To determine whether to allow response response to a request request, run these steps:

  1. Let mimeType be the result of extracting a MIME type from response's header list.
  2. Let nosniff be the result of determining nosniff given response's header list.
  3. If mimeType is not failure, then:
    1. If mimeType is an opaque-safelisted MIME type, then return true.
    2. If mimeType is an opaque-blocklisted-never-sniffed MIME type, then return false.
    3. If response's status is 206 and mimeType is an opaque-blocklisted MIME type, then return false.
    4. If nosniff is true and mimeType is an opaque-blocklisted MIME type or its essence is "text/plain", then return false.
  4. If request's no-cors media request state is "subsequent", then return true.
  5. If response's status is 206 and validate a partial response given 0 and response returns invalid, then return false.
  6. Wait for 1024 bytes of response or end-of-file, whichever comes first and let bytes be those bytes.
  7. If the audio or video type pattern matching algorithm given bytes does not return undefined, then:
    1. If requests's no-cors media request state is not "initial", then return false.
    2. If response's status is not 200 or 206, then return false.
    3. Return true.
  8. If requests's no-cors media request state is not "N/A", then return false.
  9. If the image type pattern matching algorithm given bytes does not return undefined, then return true.
  10. If nosniff is true, then return false.
  11. If response's status is not an ok status, then return false.
  12. If mimeType is failure, then return true.
  13. If mimeType's essence starts with "audio/", "image/", or "video/", then return false.
  14. Wait for end-of-file of response's body. Note: as discussed in GitHub's annevk/orb #22 partially parsing JavaScript is unfortunately infeasible. This might end up leaking the size of responses that hit this step.
  15. If response's body parses as JavaScript and does not parse as JSON, then return true.
  16. Return false.

Note: responses for which the above algorithm returns true and contain secrets are strongly encouraged to be protected using Cross-Origin-Resource-Policy.

Implementation considerations

Setting request's no-cors media request state to "subsequent" ideally happens in a process that is not easily compromised, because such a spoofed value can be used to bypass ORB. In particular, "subsequent" is only to be allowed and used if a trustworthy process can verify that the media element (or its node document, or its node document's origin) has previously received a response with the same URL that has sniffed as audio or video.

Findings

  • It's unfortunate X-Content-Type-Options mostly kicks in after image/media sniffing, but it was not web compatible for Firefox to enforce it for images back in the day.
  • Due to the way style sheet fetching works we cannot protect responses without an extractable MIME type.
  • Media elements always make range requests.

Acknowledgments

Many thanks to Jake Archibald, Lukasz Anforowicz, Nathan Froyd, and those involved in Chromium's CORB project.