#220 Content-Type Header for SVGs

Open
opened 4 weeks ago by lhinderberger · 8 comments

Hello Codeberg Team,

when trying to add a logo in SVG format to the README to one of my projects here on Codeberg, I noticed that the image is not being displayed, albeit there being an img tag with the correct URL rendered.

After looking at the responses from the server, it seems that the Codeberg server returns Content-Type text/plain for SVG images, instead of image/svg+xml.

Example: 9ac2454955/README.md (which loads https://codeberg.org/lhinderberger/xeDB/media/branch/master/doc/logo.svg that is being returned with Content-Type text/plain).

Thank you for providing an excellent community-driven alternative to GitHub and similar services!

EDIT: Changed URL for example, due to adding a workaround

Hello Codeberg Team, when trying to add a logo in SVG format to the README to one of my projects here on Codeberg, I noticed that the image is not being displayed, albeit there being an img tag with the correct URL rendered. After looking at the responses from the server, it seems that the Codeberg server returns Content-Type `text/plain` for SVG images, instead of `image/svg+xml`. Example: https://codeberg.org/lhinderberger/xeDB/src/commit/9ac2454955d7f36656ec1f532a339f0472806c25/README.md (which loads https://codeberg.org/lhinderberger/xeDB/media/branch/master/doc/logo.svg that is being returned with Content-Type `text/plain`). Thank you for providing an excellent community-driven alternative to GitHub and similar services! EDIT: Changed URL for example, due to adding a workaround
hw added the
bug
label 4 weeks ago
hw added the
gitea-related issue
label 4 weeks ago
lhinderberger commented 4 weeks ago
Poster

Thanks @hw for clarifying that this is not an issue specific to Codeberg, thus I had a look at the Gitea issue tracker.

It seems that this has been a long-time issue with Gitea, with this issue going back to 2017: https://github.com/go-gitea/gitea/issues/1095 - the issue appears to be stalling due to security concerns.

I will use a PNG version of the Logo for now as a workaround.

Thanks @hw for clarifying that this is not an issue specific to Codeberg, thus I had a look at the Gitea issue tracker. It seems that this has been a long-time issue with Gitea, with this issue going back to 2017: https://github.com/go-gitea/gitea/issues/1095 - the issue appears to be stalling due to security concerns. I will use a PNG version of the Logo for now as a workaround.
hw commented 4 weeks ago
Owner

SVG as image should be safe: https://www.w3.org/wiki/SVG_Security

If you create a PR against https://codeberg.org/codeberg/gitea we can help with the arguement (if you like our support there).

cc: @6543

SVG as image should be safe: https://www.w3.org/wiki/SVG_Security If you create a PR against https://codeberg.org/codeberg/gitea we can help with the arguement (if you like our support there). cc: @6543
lhinderberger commented 4 weeks ago
Poster

The main concern with including SVGs seems to be the possibility of Cross-Site-Scripting and especially the ability to steal credentials, since the SVG would be served from the same origin as the Gitea instance.

When including an SVG using an image tag, modern browsers should disallow scripts, amongst other things, as indicated by @hw’s link. As far as I have understood it though, there are two main remaining risks:

  • Opening the SVGs directly (i.e. by having the user click on a harmless looking link like https://codeberg.org/lhinderberger/xeDB/media/branch/master/doc/logo.svg), which still allows scripts to be executed, unless there are Content-Security-Policy headers set (and respected by the client)
  • Older browsers or other HTTP clients that do not sandbox SVG image tags or that do not know/respect Content-Security-Policy headers

To isolate SVG images in a backwards-compatible way, one could either try to strip all unsafe elements out of the SVG image (which is hard to do right) or serve the SVGs from another (sub)domain.

The people over at Gitea have experimented with the former approach, with mixed results (https://github.com/go-gitea/gitea/pull/8024), while the latter approach isn’t generalizable, because not everyone who runs a Gitea instance will be able add a proxy on another (sub)domain for serving images. In the end the issue still remains unsolved and very non-trivial to solve at that level.

But while it is hard to find a solution that works for all Gitea instances, the good news is that Codeberg could still run a fairly simple proxy server for raw / media content, that serves the contents of the repositories in their raw form to a domain like raw.codeberg.org. This approach would work fine for public repositories but not for private repositories. If we could live with that restriction, seeing that Codeberg is focused primarily on public repositories anyway, we would then only need to add the ability to Gitea to serve media content from a proxy instead of from the /someone/repo/media/... path.

If you’re interested in the proxy-based solution, I could have a look into what changes to Gitea would be necessary and write a pull request.

(On a more radical note, I think that the Web needs a more lightweight and secure-by-default vector graphics format other than SVG 😉)

The main concern with including SVGs seems to be the possibility of Cross-Site-Scripting and especially the ability to steal credentials, since the SVG would be served from the same origin as the Gitea instance. When including an SVG using an image tag, modern browsers should disallow scripts, amongst other things, as indicated by @hw's link. As far as I have understood it though, there are two main remaining risks: - Opening the SVGs directly (i.e. by having the user click on a harmless looking link like https://codeberg.org/lhinderberger/xeDB/media/branch/master/doc/logo.svg), which still allows scripts to be executed, unless there are Content-Security-Policy headers set (*and* respected by the client) - Older browsers or other HTTP clients that do not sandbox SVG image tags or that do not know/respect Content-Security-Policy headers To isolate SVG images in a backwards-compatible way, one could either try to strip all unsafe elements out of the SVG image (which is hard to do right) or serve the SVGs from another (sub)domain. The people over at Gitea have experimented with the former approach, with mixed results (https://github.com/go-gitea/gitea/pull/8024), while the latter approach isn't generalizable, because not everyone who runs a Gitea instance will be able add a proxy on another (sub)domain for serving images. In the end the issue still remains unsolved and very non-trivial to solve at that level. *But* while it is hard to find a solution that works for *all* Gitea instances, the good news is that Codeberg could still run a fairly simple proxy server for raw / media content, that serves the contents of the repositories in their raw form to a domain like `raw.codeberg.org`. This approach would work fine for public repositories but not for private repositories. If we could live with that restriction, seeing that Codeberg is focused primarily on public repositories anyway, we would then only need to add the ability to Gitea to serve media content from a proxy instead of from the `/someone/repo/media/...` path. If you're interested in the proxy-based solution, I could have a look into what changes to Gitea would be necessary and write a pull request. (On a more radical note, I think that the Web needs a more lightweight and secure-by-default vector graphics format other than SVG 😉)
hw commented 4 weeks ago
Owner

Alternative: always wrap SVG in image tag, even if served due to onclick-target? (Yes, right-clicking or peeling out the URL still works).

  • Older browsers or other HTTP clients that do not sandbox SVG image tags or that do not know/respect Content-Security-Policy headers

Fixing/Improving CSP headers would be a great contribution.

But while it is hard to find a solution that works for all Gitea instances, the good news is that Codeberg could still run a fairly simple proxy server for raw / media content, that serves the contents of the repositories in their raw form to a domain like raw.codeberg.org. This approach would work fine for public repositories but not for private repositories. If we could live with that restriction, seeing that Codeberg is focused primarily on public repositories anyway, we would then only need to add the ability to Gitea to serve media content from a proxy instead of from the /someone/repo/media/... path.

If you’re interested in the proxy-based solution, I could have a look into what changes to Gitea would be necessary and write a pull request.

This would be great, every contribution in this area welcome.

> > - Opening the SVGs directly (i.e. by having the user click on a harmless looking link like https://codeberg.org/lhinderberger/xeDB/media/branch/master/doc/logo.svg), which still allows scripts to be executed, unless there are Content-Security-Policy headers set (*and* respected by the client) Alternative: always wrap SVG in image tag, even if served due to onclick-target? (Yes, right-clicking or peeling out the URL still works). > - Older browsers or other HTTP clients that do not sandbox SVG image tags or that do not know/respect Content-Security-Policy headers Fixing/Improving CSP headers would be a great contribution. > *But* while it is hard to find a solution that works for *all* Gitea instances, the good news is that Codeberg could still run a fairly simple proxy server for raw / media content, that serves the contents of the repositories in their raw form to a domain like `raw.codeberg.org`. This approach would work fine for public repositories but not for private repositories. If we could live with that restriction, seeing that Codeberg is focused primarily on public repositories anyway, we would then only need to add the ability to Gitea to serve media content from a proxy instead of from the `/someone/repo/media/...` path. > > If you're interested in the proxy-based solution, I could have a look into what changes to Gitea would be necessary and write a pull request. This would be great, every contribution in this area welcome.
lhinderberger commented 4 weeks ago
Poster

Alternative: always wrap SVG in image tag, even if served due to onclick-target? (Yes, right-clicking or peeling out the URL still works).

It would be very interesting to find out what happens when SVGs are served as a Data-URL, regarding XSS protection across different browsers. In theory it should behave exactly as in the “regular” image tag case. If that is the case, one could fetch an SVG as text/plain and then to convert it into a image/svg+xml Data-URL using JavaScript. That way we would never directly serve untrusted SVGs and could probably limit the use of SVG in image tags to browsers of which we know it’s safe.

This would be great, every contribution in this area welcome.

Well, in that case: Count me in! And while I’m at it, I think I’ll sign up for Codeberg e.V. as well 😎

> Alternative: always wrap SVG in image tag, even if served due to onclick-target? (Yes, right-clicking or peeling out the URL still works). It would be very interesting to find out what happens when SVGs are served as a Data-URL, regarding XSS protection across different browsers. In theory it should behave exactly as in the "regular" image tag case. If that is the case, one could fetch an SVG as `text/plain` and then to convert it into a `image/svg+xml` Data-URL using JavaScript. That way we would never directly serve untrusted SVGs and could probably limit the use of SVG in image tags to browsers of which we know it's safe. > This would be great, every contribution in this area welcome. Well, in that case: Count me in! And while I'm at it, I think I'll sign up for Codeberg e.V. as well 😎
lhinderberger commented 4 weeks ago
Poster

Browsing through the other issues: This could be solved using the same media proxy as the one needed for #196.

Browsing through the other issues: This could be solved using the same media proxy as the one needed for #196.
hw commented 4 weeks ago
Owner

It would be very interesting to find out what happens when SVGs are served as a Data-URL

This sounds like a very cool idea.

> It would be very interesting to find out what happens when SVGs are served as a Data-URL This sounds like a very cool idea.
lhinderberger commented 3 weeks ago
Poster

In the meantime, I’ve set up a local development instance of Gitea and have had a look at the code.

For the proxy solution, the core changes would need to be relatively minor: Gitea’s Markdown and HTML postprocessing functions both have a section where image URLs are rewritten, which would need to be extended with the option to rewrite the URLs to a proxy.

However, in order to configure the proxy URL there would need to be changes to large parts of the Markdown and HTML postprocessing function signatures, as there is currently no centralized “DRY” way to pass down generic configuration to where we need it.

Before considering to implement the proxy solution though, we should first consider the implications with setting up such a proxy. I will add my thoughts about that to #196 and wait for definitive feedback to that before taking any further steps.

I haven’t had a closer look at the Data URL approach yet other than having a brief glimpse at the frontend code of Gitea. Personally, I would strongly prefer the proxy approach, as that offers stronger isolation due to the content actually being on another origin and not requiring run-time JavaScript DOM manipulation.

In the meantime, I've set up a local development instance of Gitea and have had a look at the code. For the proxy solution, the core changes would need to be relatively minor: Gitea's Markdown and HTML postprocessing functions both have a section where image URLs are rewritten, which would need to be extended with the option to rewrite the URLs to a proxy. However, in order to configure the proxy URL there would need to be changes to large parts of the Markdown and HTML postprocessing function signatures, as there is currently no centralized "DRY" way to pass down generic configuration to where we need it. Before considering to implement the proxy solution though, we should first consider the implications with setting up such a proxy. I will add my thoughts about that to #196 and wait for definitive feedback to that before taking any further steps. I haven't had a closer look at the Data URL approach yet other than having a brief glimpse at the frontend code of Gitea. Personally, I would strongly prefer the proxy approach, as that offers stronger isolation due to the content actually being on another origin and not requiring run-time JavaScript DOM manipulation.
Sign in to join this conversation.
No Milestone
No Assignees
2 Participants
Notifications
Due Date

No due date set.

Dependencies

This issue currently doesn't have any dependencies.

Loading…
There is no content yet.