mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-11-03 19:08:50 +03:00
@ZaphodB fix(security): message images loaded without consent through srcset and CSS
The sanitizer sends img src, and url() in background-image, list-style-image and content, through the remote content handling: blocked until the user allows it, rewritten to the proxy when it is on. Two other ways to load an image were left alone: - srcset (and sizes) were on the attribute allowlist and never rewritten, so <img srcset="https://..."> loaded when the message opened. With the proxy on, the CSP keeps img-src to 'self', but a relative srcset="?/ProxyExternal/<b64>" is same-origin and made the server fetch an attacker URL at render time: a read receipt with no click. With the proxy off the browser fetched directly. - url() in any other style property (border-image-source, mask-image, filter, ...) kept its value, with the same two outcomes. Drop srcset and sizes from the allowlist (attributes not on it are deleted), and remove any other style property whose value holds url(). Relative img src was already safe: anything not cid:, http(s) or data:image/ is parked as data-x-src-broken. Checked the style sweep in Chromium: border-image-source, mask-image (both spellings), filter and cursor lose the url(), other declarations survive, and the background/list-style shorthands are left to the existing handling.
This commit is contained in:
parent
3776ac6b32
commit
08f34b9954
1 changed files with 18 additions and 1 deletions
|
|
@ -283,7 +283,11 @@ export const
|
|||
// hr
|
||||
'noshade',
|
||||
// img
|
||||
'hspace', 'sizes', 'srcset', 'vspace',
|
||||
// srcset and sizes are not allowed: only src goes through the
|
||||
// remote content consent and proxy rewrite below, so a srcset
|
||||
// URL loaded without asking (directly, or relative to this
|
||||
// origin through ?/ProxyExternal as a read receipt).
|
||||
'hspace', 'vspace',
|
||||
// meter
|
||||
'low', 'high', 'optimum', 'value',
|
||||
// ol
|
||||
|
|
@ -578,6 +582,19 @@ export const
|
|||
oStyle.color = delAttribute('color');
|
||||
}
|
||||
|
||||
// Only the three properties below go through the remote content and
|
||||
// cid: handling. A url() in any other property (border-image-source,
|
||||
// mask-image, filter, ...) would load without consent, a relative one
|
||||
// through ?/ProxyExternal on this origin, so drop those outright.
|
||||
let i = oStyle.length;
|
||||
while (i--) {
|
||||
const property = oStyle[i];
|
||||
if (!['background-image', 'list-style-image', 'content'].includes(property)
|
||||
&& /url\s*\(/i.test(oStyle.getPropertyValue(property))) {
|
||||
oStyle.removeProperty(property);
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipStyle) {
|
||||
/*
|
||||
if ('fixed' === oStyle.position) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue