IntraVox supports SVG uploads with comprehensive sanitization to prevent security attacks. This document explains the security measures implemented to safely handle SVG files.
SVG (Scalable Vector Graphics) files are XML-based and can contain:
- JavaScript code via
<script>tags - Event handlers (onclick, onload, etc.)
- External entity references (XXE attacks)
- HTML injection via
<foreignObject>elements - Remote resource loading (privacy/tracking concerns)
Without proper sanitization, malicious SVG files could execute JavaScript in users' browsers, leading to XSS (Cross-Site Scripting) attacks.
| Attack Vector | Risk Level | How We Prevent It |
|---|---|---|
XSS via <script> tags |
Critical | Removed by svg-sanitize library |
| Event handlers | Critical | Stripped (onclick, onload, onerror, etc.) |
| XXE attacks | High | DOCTYPE declarations rejected |
HTML injection via <foreignObject> |
High | foreignObject elements removed |
| Remote resource loading | Medium | External references stripped |
Library: enshrined/svg-sanitize v0.20+
- Industry-standard PHP library
- Used by WordPress, Drupal, and other major platforms
- MIT licensed, actively maintained
Process:
- File upload received
- MIME type validated via PHP
finfo_file()(not file extension) - SVG content sanitized through library
- Additional DOCTYPE check (XXE prevention)
- Sanitized content stored to disk
Code Location:
lib/Service/PageService.php-sanitizeSVG()method (lines 3632-3662)- Integration in
uploadMedia()(line 1692-1698) - Integration in
uploadMediaWithOriginalName()(line 3780-3786)
- MIME Type Validation: Server-side detection using
finfo_file()prevents file extension spoofing - Content Sanitization: Removes malicious elements and attributes
- DOCTYPE Rejection: Blocks XML external entity (XXE) attacks
- img Tag Rendering: Frontend renders SVG via
<img>tag (scripts won't execute even if sanitization bypassed) - File Size Limits: Maximum 50MB prevents DoS attacks
- Permission Checks: Existing Nextcloud permission system respected
The sanitizer removes or neutralizes:
<script>elements- Event handler attributes (onclick, onmouseover, onerror, onload, etc.)
<foreignObject>elements<iframe>elements- External stylesheets (
<link>) - Data URIs in certain contexts
- JavaScript protocol handlers (
javascript:) - DOCTYPE declarations
Safe SVG features are preserved:
- Basic shapes (rect, circle, path, polygon, etc.)
- Gradients and patterns
- Text elements
- Transformations and animations (SMIL, not JavaScript-based)
- Internal styles (style attribute,
<style>elements with CSS only) - Accessibility attributes (aria-*, role)
- Source Trust: Only upload SVG files from trusted sources
- Review Before Upload: Check SVG content if received from external parties
- Report Issues: If an SVG upload fails, contact your administrator (file may contain malicious content)
- Keep Updated: Regularly update IntraVox to get latest security patches
- Monitor Logs: Check Nextcloud logs for SVG sanitization errors
- Composer Dependencies: Keep
enshrined/svg-sanitizelibrary updated - File Permissions: Ensure proper Nextcloud ACLs for _resources folder
These will pass sanitization:
<!-- Simple shapes -->
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="blue"/>
</svg>
<!-- Gradients -->
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100">
<defs>
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
</linearGradient>
</defs>
<rect width="200" height="100" fill="url(#grad)" />
</svg>These will fail or be stripped:
<!-- Script injection (REMOVED) -->
<svg xmlns="http://www.w3.org/2000/svg">
<script>alert('XSS')</script>
</svg>
<!-- Event handler (REMOVED) -->
<svg xmlns="http://www.w3.org/2000/svg">
<circle onclick="alert('XSS')" cx="50" cy="50" r="40"/>
</svg>
<!-- XXE attack (REJECTED) -->
<!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<svg xmlns="http://www.w3.org/2000/svg">
<text>&xxe;</text>
</svg>private function sanitizeSVG(string $svgContent): string {
try {
$sanitizer = new Sanitizer();
$sanitizer->removeRemoteReferences(true);
$cleanSvg = $sanitizer->sanitize($svgContent);
if ($cleanSvg === false || empty($cleanSvg)) {
throw new \Exception('SVG sanitization failed - file may contain malicious content');
}
// Additional security: reject DOCTYPE (XXE attack vector)
if (stripos($cleanSvg, '<!DOCTYPE') !== false) {
throw new \Exception('SVG contains DOCTYPE declaration (not allowed)');
}
return $cleanSvg;
} catch (\Exception $e) {
$this->logger->error('SVG sanitization error: ' . $e->getMessage());
throw new \Exception('Invalid SVG file: ' . $e->getMessage());
}
}When SVG sanitization fails:
- Error logged to Nextcloud log file
- Exception thrown with clear message
- Upload rejected (file not saved)
- User receives error notification
- Complex Animations: JavaScript-based SVG animations will be removed (use CSS/SMIL instead)
- Interactive Features: onclick handlers and similar interactivity stripped
- External Resources: Remote images, fonts, and stylesheets removed
- DOCTYPE: Any DOCTYPE declaration causes rejection (security measure)
This implementation follows:
- OWASP Top 10: Protection against XSS and XXE attacks
- CSP Best Practices: img tag rendering prevents script execution
- Industry Standards: Same approach used by WordPress, Drupal, GitHub
- v0.8.0 (2025-12-16): Initial SVG support with sanitization
- enshrined/svg-sanitize v0.20
- Server-side sanitization
- DOCTYPE rejection
- Multi-layer defense approach
For security concerns or bug reports:
- GitHub Issues: https://github.com/shalution/intravox/issues
- Security Email: [email protected]
Last Updated: 2025-12-16 Document Version: 1.0