Skip to content

Commit

Permalink
Specify Content-Type and Content-Disposition usage in the media r…
Browse files Browse the repository at this point in the history
…epo (#1935)
  • Loading branch information
zecakeh authored Sep 2, 2024
1 parent 7f2f100 commit 415fb43
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 11 deletions.
1 change: 1 addition & 0 deletions changelogs/client_server/newsfragments/1935.clarification
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Specify `Content-Type` and `Content-Disposition` usage in the media repo, as per [MSC2701](https://github.com/matrix-org/matrix-spec-proposals/pull/2701) and [MSC2702](https://github.com/matrix-org/matrix-spec-proposals/pull/2702).
47 changes: 47 additions & 0 deletions content/client-server-api/modules/content_repo.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,50 @@ Homeservers have additional content-specific concerns:
- Clients or remote homeservers may try to upload malicious files
targeting vulnerabilities in either the homeserver thumbnailing or
the client decoders.

##### Serving inline content

Clients with insecure configurations may be vulnerable to Cross-Site Scripting
attacks when served media with a `Content-Disposition` of `inline`. Clients
SHOULD NOT be hosted on the same domain as the media endpoints for the homeserver
to mitigate most of this risk. Servers SHOULD restrict `Content-Type` headers to
one of the following values when serving content with `Content-Disposition: inline`:

* `text/css`
* `text/plain`
* `text/csv`
* `application/json`
* `application/ld+json`
* `image/jpeg`
* `image/gif`
* `image/png`
* `image/apng`
* `image/webp`
* `image/avif`
* `video/mp4`
* `video/webm`
* `video/ogg`
* `video/quicktime`
* `audio/mp4`
* `audio/webm`
* `audio/aac`
* `audio/mpeg`
* `audio/ogg`
* `audio/wave`
* `audio/wav`
* `audio/x-wav`
* `audio/x-pn-wav`
* `audio/flac`
* `audio/x-flac`

These types are unlikely to cause Cross-Site Scripting issues when a `Content-Type`
header is provided, as clients will only try to render the data using that content
type. For example, if a HTML file is uploaded with a `Content-Type` of `image/png`,
clients will just assume that the image is corrupted, and won't render it as a
HTML page. Therefore, there is no risk in trusting the user-defined content type,
as long as the `Content-Disposition` is calculated based on that type.

Clients SHOULD NOT rely on servers returning `inline` rather than `attachment`
on `/download`. Server implementations might decide out of an abundance of
caution that all downloads are responded to with `attachment`, regardless of
content type - clients should not be surprised by this behaviour.
78 changes: 73 additions & 5 deletions data/api/client-server/authed-content-repo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,29 @@ paths:
Content-Type:
$ref: '#/components/headers/downloadContentType'
Content-Disposition:
description: The name of the file that was previously uploaded, if set.
x-changedInMatrixVersion:
"1.12": This header became required.
description: |
The [disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition)
of the returned content. MUST be one of `inline` or `attachment`,
and SHOULD contain a file name.
If the `Content-Type` is allowed in the [restrictions for serving
inline content](/client-server-api/#serving-inline-content),
servers SHOULD use `inline`, otherwise they SHOULD use
`attachment`.
If the upload was made with a `filename`, this header MUST
contain the same `filename`. Otherwise, `filename` is excluded
from the header. If the media being downloaded is remote, the
remote server's `filename` in the `Content-Disposition` header
is used as the `filename` instead. When the header is not
supplied, or does not supply a `filename`, the local download
response does not include a `filename`.
required: true
schema:
type: string
example: "inline; filename=\"filename.jpg\""
content:
application/octet-stream:
schema:
Expand Down Expand Up @@ -106,11 +126,21 @@ paths:
Content-Type:
$ref: '#/components/headers/downloadContentType'
Content-Disposition:
description: |-
The `fileName` requested or the name of the file that was previously
uploaded, if set.
x-changedInMatrixVersion:
"1.12": This header became required.
description: |
The [disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition)
of the returned content. MUST be one of `inline` or `attachment`,
and MUST contain the file name requested in the path.
If the `Content-Type` is allowed in the [restrictions for serving
inline content](/client-server-api/#serving-inline-content),
servers SHOULD use `inline`, otherwise they SHOULD use
`attachment`.
required: true
schema:
type: string
example: "inline; filename=\"filename.jpg\""
content:
application/octet-stream:
schema:
Expand Down Expand Up @@ -208,8 +238,24 @@ paths:
"200":
description: A thumbnail of the requested content.
headers:
Content-Disposition:
x-addedInMatrixVersion: "1.12"
description: |
The [disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition)
of the returned content. MUST be `inline`, and SHOULD contain a file name (e.g. `thumbnail.png`).
Servers should note the [Content-Type restrictions for serving inline content](/client-server-api/#serving-inline-content),
as these limitations imply which formats should be used for thumbnail generation.
required: true
schema:
type: string
example: "inline; filename=\"thumbnail.png\""
Content-Type:
x-changedInMatrixVersion:
"1.12": |
This header became required in order to support `Content-Disposition`.
description: The content type of the thumbnail.
required: true
schema:
type: string
enum:
Expand Down Expand Up @@ -512,7 +558,29 @@ components:
format: uri
headers:
downloadContentType:
description: The content type of the file that was previously uploaded.
description: |
The content type of the file that was previously uploaded.
The server MUST return a `Content-Type` which is either exactly the same
as the original upload, or reasonably close. The bounds of "reasonable"
are:
* Adding a charset to `text/*` content types.
* Detecting HTML and using `text/html` instead of `text/plain`.
* Using `application/octet-stream` when the server determines the
content type is obviously wrong. For example, an encrypted file being
claimed as `image/png`.
* Returning `application/octet-stream` when the media has an
unknown/unprovided `Content-Type`. For example, being uploaded before
the server tracked content types or when the remote server is
non-compliantly omitting the header entirely.
Actions not in the spirit of the above are not considered "reasonable".
x-changedInMatrixVersion:
"1.12": |
This header became required in order to support `Content-Disposition`,
and the behaviour to compute its value was clarified.
required: true
schema:
type: string

88 changes: 82 additions & 6 deletions data/api/client-server/content-repo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,29 @@ paths:
Content-Type:
$ref: '#/components/headers/downloadContentType'
Content-Disposition:
description: The name of the file that was previously uploaded, if set.
x-changedInMatrixVersion:
"1.12": This header became required.
description: |
The [disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition)
of the returned content. MUST be one of `inline` or `attachment`,
and SHOULD contain a file name.
If the `Content-Type` is allowed in the [restrictions for serving
inline content](/client-server-api/#serving-inline-content),
servers SHOULD use `inline`, otherwise they SHOULD use
`attachment`.
If the upload was made with a `filename`, this header MUST
contain the same `filename`. Otherwise, `filename` is excluded
from the header. If the media being downloaded is remote, the
remote server's `filename` in the `Content-Disposition` header
is used as the `filename` instead. When the header is not
supplied, or does not supply a `filename`, the local download
response does not include a `filename`.
required: true
schema:
type: string
example: "inline; filename=\"filename.jpg\""
content:
application/octet-stream:
schema:
Expand Down Expand Up @@ -309,11 +329,21 @@ paths:
Content-Type:
$ref: '#/components/headers/downloadContentType'
Content-Disposition:
description: |-
The `fileName` requested or the name of the file that was previously
uploaded, if set.
x-changedInMatrixVersion:
"1.12": This header became required.
description: |
The [disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition)
of the returned content. MUST be one of `inline` or `attachment`,
and MUST contain the file name requested in the path.
If the `Content-Type` is allowed in the [restrictions for serving
inline content](/client-server-api/#serving-inline-content),
servers SHOULD use `inline`, otherwise they SHOULD use
`attachment`.
required: true
schema:
type: string
example: "inline; filename=\"filename.jpg\""
content:
application/octet-stream:
schema:
Expand Down Expand Up @@ -412,8 +442,24 @@ paths:
"200":
description: A thumbnail of the requested content.
headers:
Content-Disposition:
x-addedInMatrixVersion: "1.12"
description: |
The [disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition)
of the returned content. MUST be `inline`, and SHOULD contain a file name (e.g. `thumbnail.png`).
Servers should note the [Content-Type restrictions for serving inline content](/client-server-api/#serving-inline-content),
as these limitations imply which formats should be used for thumbnail generation.
required: true
schema:
type: string
example: "inline; filename=\"thumbnail.png\""
Content-Type:
x-changedInMatrixVersion:
"1.12": |
This header became required in order to support `Content-Disposition`.
description: The content type of the thumbnail.
required: true
schema:
type: string
enum:
Expand Down Expand Up @@ -639,7 +685,15 @@ components:
contentType:
in: header
name: Content-Type
description: The content type of the file being uploaded
description: |
**Optional.** The content type of the file being uploaded.
Clients SHOULD always supply this header.
Defaults to `application/octet-stream` if it is not set.
x-changedInMatrixVersion:
"1.12": |
This header became explicitly optional with a default value.
example: application/pdf
schema:
type: string
Expand Down Expand Up @@ -782,7 +836,29 @@ components:
format: uri
headers:
downloadContentType:
description: The content type of the file that was previously uploaded.
description: |
The content type of the file that was previously uploaded.
The server MUST return a `Content-Type` which is either exactly the same
as the original upload, or reasonably close. The bounds of "reasonable"
are:
* Adding a charset to `text/*` content types.
* Detecting HTML and using `text/html` instead of `text/plain`.
* Using `application/octet-stream` when the server determines the
content type is obviously wrong. For example, an encrypted file being
claimed as `image/png`.
* Returning `application/octet-stream` when the media has an
unknown/unprovided `Content-Type`. For example, being uploaded before
the server tracked content types or when the remote server is
non-compliantly omitting the header entirely.
Actions not in the spirit of the above are not considered "reasonable".
x-changedInMatrixVersion:
"1.12": |
This header became required in order to support `Content-Disposition`,
and the behaviour to compute its value was clarified.
required: true
schema:
type: string

0 comments on commit 415fb43

Please sign in to comment.