fix: critical SVG XSS vulnerability in file uploads
- Add comprehensive file type validation to prevent SVG uploads - Update upload widgets to restrict file types at input level - Enhanced HTML sanitization to block SVG tags and data URLs - Create centralized file validation utilities with shared constants - Add client-side and server-side protection against malicious uploads Security Impact: - Prevents stored XSS attacks through malicious SVG profile uploads - Blocks execution of JavaScript embedded in SVG files - Enforces whitelist of safe image formats (JPEG, PNG, GIF, WebP) - Provides defense-in-depth protection across all upload components Fixes reported vulnerability in profile image upload functionality.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import ImageCopy from 'carbon-icons-svelte/lib/ImageCopy.svelte';
|
||||
import TrashCan from 'carbon-icons-svelte/lib/TrashCan.svelte';
|
||||
import { ALLOWED_IMAGE_TYPES } from '$lib/utils/functions/fileValidation';
|
||||
|
||||
export let imageURL = '';
|
||||
export let label = '';
|
||||
@@ -8,13 +9,24 @@
|
||||
export let labelClassName = '';
|
||||
let isUploading = false;
|
||||
let fileInput: HTMLInputElement;
|
||||
|
||||
|
||||
const onFileSelected = () => {
|
||||
const file = fileInput?.files?.[0];
|
||||
const sizeInkb = file?.size! / 1024;
|
||||
if (sizeInkb > 500) {
|
||||
if (!file) return;
|
||||
|
||||
// Validate file type to prevent SVG XSS attacks
|
||||
if (!ALLOWED_IMAGE_TYPES.includes(file.type)) {
|
||||
alert('Invalid file type. Only JPEG, PNG, GIF, and WebP files are allowed.');
|
||||
fileInput.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const sizeInMB = file?.size! / 1024 / 1024;
|
||||
if (sizeInMB > 2) {
|
||||
fileInput.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
let reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
@@ -56,6 +68,7 @@
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
accept=".jpg,.jpeg,.png,.gif,.webp,image/jpeg,image/png,image/gif,image/webp"
|
||||
style="display: none;"
|
||||
bind:this={fileInput}
|
||||
on:change={onFileSelected}
|
||||
@@ -68,7 +81,7 @@
|
||||
<div class="space-y-4">
|
||||
<ImageCopy size={24} class="mx-auto" />
|
||||
<p class="text-center text-sm text-gray-500 my-2">
|
||||
Max file size: 10MB, accepted: jpeg, jpg, png, gif
|
||||
Max file size: 2MB, accepted: JPEG, PNG, GIF, WebP only
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* File type validation utilities for preventing malicious uploads
|
||||
* Specifically designed to prevent SVG XSS attacks and other security issues
|
||||
*/
|
||||
|
||||
export const ALLOWED_IMAGE_TYPES = [
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/webp'
|
||||
] as const;
|
||||
|
||||
export const ALLOWED_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp'] as const;
|
||||
|
||||
/**
|
||||
* Validate file type by MIME type
|
||||
* @param file - File object to validate
|
||||
* @returns boolean indicating if file type is allowed
|
||||
*/
|
||||
export function validateImageType(file: File): boolean {
|
||||
return ALLOWED_IMAGE_TYPES.includes(file.type as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate file extension
|
||||
* @param filename - Filename to validate
|
||||
* @returns boolean indicating if extension is allowed
|
||||
*/
|
||||
export function validateImageExtension(filename: string): boolean {
|
||||
const extension = filename.toLowerCase().substring(filename.lastIndexOf('.'));
|
||||
return ALLOWED_EXTENSIONS.includes(extension as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprehensive file validation
|
||||
* @param file - File object to validate
|
||||
* @returns object with validation result and error message
|
||||
*/
|
||||
export function validateImageUpload(file: File): { isValid: boolean; error?: string } {
|
||||
// Check file type
|
||||
if (!validateImageType(file)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: 'Invalid file type. Only JPEG, PNG, GIF, and WebP files are allowed.'
|
||||
};
|
||||
}
|
||||
|
||||
// Check file extension (double check for security)
|
||||
if (!validateImageExtension(file.name)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: 'Invalid file extension. Only .jpg, .jpeg, .png, .gif, and .webp files are allowed.'
|
||||
};
|
||||
}
|
||||
|
||||
// Additional security checks
|
||||
if (file.name.toLowerCase().includes('.svg')) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: 'SVG files are not allowed for security reasons.'
|
||||
};
|
||||
}
|
||||
|
||||
return { isValid: true };
|
||||
}
|
||||
Reference in New Issue
Block a user