reported via email on 13 June 2026:
I found an incomplete case in imgproxy's SVG sanitizer that allows attacker-controlled external resource loading to survive sanitization.
When IMGPROXY_SANITIZE_SVG=true (the default), imgproxy removes <script> tags, <iframe> tags, and on* event-handler attributes from SVG files before serving them. However, the sanitizer does not remove or neutralize:
- Content inside
<style> tags -- CSS @import url([https://attacker-server/)](https://attacker-server/)) and background: url(...) directives pass through unchanged.
<image href="[https://attacker-server/..](https://attacker-server/)."> SVG image elements.
<feImage href="[https://attacker-server/..](https://attacker-server/)."> inside SVG <filter> blocks.
When imgproxy serves the sanitized SVG with Content-Type: image/svg+xml and Content-Disposition: inline (the default), a browser opening the URL will perform GET requests to those external origins. The CSP header script-src 'none' that imgproxy already sets does not restrict CSS or image loading, so these requests are not blocked.
I confirmed this by running imgproxy v4.0.4 with default settings, fetching a test SVG containing the above elements, and observing that the response body contains the attacker URLs unchanged. The full test steps and observed output are shown below.
To reproduce:
- Run:
docker run -d -p 8080:8080 -e IMGPROXY_SANITIZE_SVG=true darthsim/imgproxy:latest
- Prepare an SVG with
<image href="https://attacker.example.com/"> and serve it locally.
- Request
http://localhost:8080/insecure/<base64-encoded-url>.svg.
- The response body contains the
<image> element with the attacker URL intact.
Suggested fix: Extend the sanitizeElement function in processing/svg/svg.go to either (a) strip <style> elements entirely, or sanitize their content to remove url() references and @import directives; (b) strip <image> elements or allow only data: URIs; and (c) strip <feImage> elements. Additionally, consider adding default-src 'none' or img-src 'none'; style-src 'none' to the existing CSP header as a defense-in-depth measure.
reported via email on 13 June 2026:
I found an incomplete case in imgproxy's SVG sanitizer that allows attacker-controlled external resource loading to survive sanitization.
When
IMGPROXY_SANITIZE_SVG=true(the default), imgproxy removes<script>tags,<iframe>tags, andon*event-handler attributes from SVG files before serving them. However, the sanitizer does not remove or neutralize:<style>tags -- CSS@import url([https://attacker-server/)](https://attacker-server/)) andbackground: url(...)directives pass through unchanged.<image href="[https://attacker-server/..](https://attacker-server/).">SVG image elements.<feImage href="[https://attacker-server/..](https://attacker-server/).">inside SVG<filter>blocks.When imgproxy serves the sanitized SVG with
Content-Type: image/svg+xmlandContent-Disposition: inline(the default), a browser opening the URL will perform GET requests to those external origins. The CSP headerscript-src 'none'that imgproxy already sets does not restrict CSS or image loading, so these requests are not blocked.I confirmed this by running imgproxy v4.0.4 with default settings, fetching a test SVG containing the above elements, and observing that the response body contains the attacker URLs unchanged. The full test steps and observed output are shown below.
To reproduce:
docker run -d -p 8080:8080 -e IMGPROXY_SANITIZE_SVG=true darthsim/imgproxy:latest<image href="https://attacker.example.com/">and serve it locally.http://localhost:8080/insecure/<base64-encoded-url>.svg.<image>element with the attacker URL intact.Suggested fix: Extend the
sanitizeElementfunction inprocessing/svg/svg.goto either (a) strip<style>elements entirely, or sanitize their content to removeurl()references and@importdirectives; (b) strip<image>elements or allow only data: URIs; and (c) strip<feImage>elements. Additionally, consider addingdefault-src 'none'orimg-src 'none'; style-src 'none'to the existing CSP header as a defense-in-depth measure.