A quote's allowed images bypass the image proxy #412

Open
opened 2026-09-19 22:57:32 +00:00 by jcoffey-dev · 0 comments
Owner

Noticed while fixing #410, and deliberately left alone there.

Reading a message routes its remote images through the server's proxy: MessageView passes proxyRemote: imageProxy (the session's ihasmail.imageProxy, on by default), so an image is fetched by the ihasmail server rather than by the browser, and the sender learns nothing about the reader's address or client.

Quoting the same message into a reply does not. reply() passes proxyRemote: false, so once the images are allowed — by policy, a trusted sender, a contact, or Show images — the composer fetches them straight from wherever the mail points. Same pixel, same reader, but the request carries their IP and user agent, which is what the proxy exists to withhold. openDraftEmail and composeAsNew do the same.

proxyRemote: false is not an oversight: a proxied URL is app-relative (/api/image?url=…), so quoting with the proxy on and sending the reply unchanged would hand the recipient a quote whose images point at this server. They would break for anyone outside it, and act as a beacon for anyone inside.

So the fix has two halves, and the second is the reason this is filed rather than done:

  1. Quote with proxyRemote set the way the reader has it, so nothing is fetched directly while the reply is being written.
  2. Turn the proxied URLs back into the originals when the reply is sent, in buildEmailObject — beside the pass that already restores images blocked under #410 and the one that turns editor blob URLs back into cid: references.

Worth checking while doing it: a signature or template that legitimately references an image through the proxy, and a forwarded message whose images were allowed, should both come out the same way.

Rebuilt from: session transcript.

Noticed while fixing #410, and deliberately left alone there. Reading a message routes its remote images through the server's proxy: `MessageView` passes `proxyRemote: imageProxy` (the session's `ihasmail.imageProxy`, on by default), so an image is fetched by the ihasmail server rather than by the browser, and the sender learns nothing about the reader's address or client. Quoting the same message into a reply does not. `reply()` passes `proxyRemote: false`, so once the images are allowed — by policy, a trusted sender, a contact, or *Show images* — the composer fetches them straight from wherever the mail points. Same pixel, same reader, but the request carries their IP and user agent, which is what the proxy exists to withhold. `openDraftEmail` and `composeAsNew` do the same. `proxyRemote: false` is not an oversight: a proxied URL is app-relative (`/api/image?url=…`), so quoting with the proxy on and sending the reply unchanged would hand the recipient a quote whose images point at this server. They would break for anyone outside it, and act as a beacon for anyone inside. So the fix has two halves, and the second is the reason this is filed rather than done: 1. Quote with `proxyRemote` set the way the reader has it, so nothing is fetched directly while the reply is being written. 2. Turn the proxied URLs back into the originals when the reply is sent, in `buildEmailObject` — beside the pass that already restores images blocked under #410 and the one that turns editor blob URLs back into `cid:` references. Worth checking while doing it: a signature or template that legitimately references an image through the proxy, and a forwarded message whose images were allowed, should both come out the same way. <sub>Rebuilt from: session transcript.</sub>
This repo is archived. You cannot comment on issues.