Как загрузить SVG в WordPress: безопасно и просто

# Как загрузить SVG в WordPress

По умолчанию в WordPress нельзя загружать файлы формата SVG из соображений безопасности. Но что делать, если вам это действительно нужно? Есть решение!

![Ошибка загрузки](assets/uploads/2023/12/not-permitted.webp) Когда вы пытаетесь загрузить SVG, появляется сообщение: "Извините, этот тип файла не разрешен по соображениям безопасности".

Если вам интересно, почему загрузка SVG запрещена по умолчанию в WordPress и с чем это связано, вы можете ознакомиться с тикетом #24251 "Пересмотреть включение SVG в список допустимых типов файлов".

Если вам проще, вы можете использовать плагин **Safe SVG**. Он позволяет загружать SVG и при этом очищает код загружаемого файла от вредоносных элементов.

## Шаг 1 - Включение SVG в список разрешенных файлов

Для этого мы используем хук upload_mimes, который позволяет изменить список файлов, доступных для загрузки, по их MIME-типам.

```php
add_filter( 'upload_mimes', 'svg_upload_allow' );

// Добавляет SVG в список разрешенных типов файлов.
function svg_upload_allow( $mimes ) {
    $mimes['svg'] = 'image/svg+xml';
    return $mimes;
}

Иногда этого кода достаточно, но не для всех SVG-файлов. Давайте разберемся почему.

Настоящий MIME-тип SVG файла, который определяется в WordPress с помощью функции php finfo_file(), может быть двух вариантов:

  • image/svg+xml — когда SVG-код имеет заголовок XML.
  • image/svg — когда SVG-код содержит только тег <svg> без заголовков.

Оба этих варианта нужно добавить, но так как можно добавить только один, одного кода недостаточно, поэтому нужно выполнить второй шаг.

Шаг 2 - Замена MIME-типа SVG

Как упоминалось ранее, предыдущего кода недостаточно. Причина в том, что в результате проверки и несоответствия указанного MIME-типа реальному (который определяется WordPress), MIME-тип просто будет сброшен в ноль. Для этого нам поможет хук wp_check_filetype_and_ext.

add_filter( 'wp_check_filetype_and_ext', 'fix_svg_mime_type', 10, 5 );

// Исправляем MIME-тип для SVG-файлов.
function fix_svg_mime_type( $data, $file, $filename, $mimes, $real_mime = '' ) {
    // WordPress 5.1 и выше
    if( version_compare( $GLOBALS['wp_version'], '5.1.0', '>=' ) ) {
        $dosvg = in_array( $real_mime, [ 'image/svg', 'image/svg+xml' ] );
    } else {
        $dosvg = ( '.svg' === strtolower( substr( $filename, -4 ) ) );
    }

    // Если MIME-тип был сброшен, исправим его и проверим права пользователя
    if( $dosvg ) {
        // Разрешить
        if( current_user_can('manage_options') ) {
            $data['ext']  = 'svg';
            $data['type'] = 'image/svg+xml';
        } 
        // Запре��ить
        else {
            $data['ext']  = false;
            $data['type'] = false;
        }
    }

    return $data;
}

Если вы уберете проверку прав администратора из приведенного выше кода, он станет небезопасным и может открыть уязвимость на вашем сайте.

В приведенном коде есть проверка версии WordPress. Начиная с версии 5.1.0, благодаря тикету разработчика, появился параметр $real_mime, который позволяет более надежно проверять файл по его коду, а не по расширению.

Я также рекомендую ограничить размер загружаемого SVG файла. Например, не более 50 КБ. SVG-файлы обычно маленькие, и большой размер может указывать на наличие плохого кода в файле.

Кода выше достаточно, чтобы WordPress разрешил загрузку SVG в медиа-библиотеку. Следующий шаг — это визуализация.

Отображение SVG в медиа-библиотеке

После выполнения описанных выше шагов SVG-файлы будут отображаться как документы, а не как изображения:

Список SVG файлов

Код, отвечающий за отображение этих блоков изображений, создается функцией wp_print_media_templates() из файла wp-includes/media-template.php:

<# if ( data.uploading ) { #>
<# } else if ( 'image' === data.type && data.sizes ) { #>
<# } else { #>
<# if ( data.image && data.image.src && data.image.src !== data.icon ) { #> <# } else if ( data.sizes && data.sizes.medium ) { #> <# } else { #> <# } #>
{{ data.filename }}
<# } #>

Смысл кода заключается в следующем: если изображение имеет свойство src (ссылка на изображение) и оно не равно ссылке на иконку документа (поскольку любой файл по сути является документом), или если изображение имеет размер medium (SVG-файлы не обрезаются).

Так как у SVG-файлов нет ни того, ни другого, нам нужно смоделировать один из этих вариантов, а для этого поможет фильтр wp_prepare_attachment_for_js.

Вариант 1 - С отображением имени файла

add_filter( 'wp_prepare_attachment_for_js', 'show_svg_in_media_library' );

// Формирует данные для отображения SVG как изображения в медиа-библиотеке.
function show_svg_in_media_library( $response ) {
    if ( $response['mime'] === 'image/svg+xml' ) {
        // С отображе��ием имени файла
        $response['image'] = [
            'src' => $response['url'],
        ];
    }
    return $response;
}

Вариант 2 - Без отображения имени файла

add_filter( 'wp_prepare_attachment_for_js', 'show_svg_in_media_library' );

// Формирует данные для отображения SVG как изображения в медиа-библиотеке.
function show_svg_in_media_library( $response ) {
    if ( $response['mime'] === 'image/svg+xml' ) {
        // Без отображения имени файла
        $response['sizes'] = [
            'medium' => [
                'url' => $response['url'],
            ],
            // при редактировании изображения
            'full' => [
                'url' => $response['url'],
            ],
        ];
    }
    return $response;
}

Полный класс

Вот пример класса, который вы можете использовать:

=' ) ) {
            $dosvg = in_array( $real_mime, [ 'image/svg', 'image/svg+xml' ] );
        } else {
            $dosvg = ( '.svg' === strtolower( substr( $filename, -4 ) ) );
        }

        // Если MIME-тип был сброшен, исправим его
        if( $dosvg ) {
            if( current_user_can( 'upload_files' ) ) {
                $data['ext']  = 'svg';
                $data['type'] = 'image/svg+xml';
            } else {
                // Запретить
                $data['ext'] = $type_and_ext['type'] = false;
            }
        }

        return $data;
    }

    /**
     * Генерирует данные для отображения SVG как изображений в медиа-библиотеке.
     */
    public static function show_svg_in_media_library( $response ) {
        if ( $response['mime'] === 'image/svg+xml' ) {
            $response['sizes'] = [
                'medium' => [
                    'url' => $response['url'],
                ],
                // при редактировании изображения
                'full' => [
                    'url' => $response['url'],
                ],
            ];
        }

        return $response;
    }

    /**
     * Ограничить размер загружаемых файлов по типу
     */
    public static function check_upload_file_size( $file ) {
        if( ! $file ) {
            return $file;
        }

        // для SVG
        if( str_contains( ( $file['type'] ?? '' ), 'image/svg+xml' ) ) {
            $size_limit = 50 * 1024; // допустимый размер в КБ

            if( (int) $file['size'] > $size_limit ) {
                $file['error'] = sprintf(
                    __( 'ОШИБКА: Размер этого типа файлов не может превышать %s', 'km' ),
                    size_format( $size_limit )
                );
            }
        }

        return $file;
    }
}

Теперь вы можете загружать SVG-файлы в WordPress, используя описанные выше методы.

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *