[Spring Boot] 画像のバリデーション

IT記事, Spring Boot

サーバー側で画像ファイルを受け取る際、正しいデータかチェックします。

こちらの記事(ホワイトハッカーへの道 二歩目)を参考にすると以下をする必要があります。

  • ファイル名にパスが含まれていないかチェックする。
  • ファイルにスクリプトタグが含まれていないかのチェックする。
  • MIMEタイプに対応する拡張子をチェックする。
  • アップロードサイズをチェックする。
  • アップロード時にEXIFは削除する。
  • アップロードされたファイル名をランダムなファイル名にする。
  • アップロードディレクトリ、ファイルは実行権限を付与しない。
  • アップロード先をドキュメントルート外フォルダかクラウドにする。

ここでは3つ目のMIMEタイプのチェックを行う独自バリデーションのコードを書きます。

package aaaaaa.validator;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Documented
@Constraint(validatedBy = MediaTypeImageValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MediaTypeImage {
    String message() default "{MediaTypeImage.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
package aaaaaa.validator;

import java.util.Arrays;
import java.util.List;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.apache.commons.io.FilenameUtils;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;

public class MediaTypeImageValidator implements ConstraintValidator<MediaTypeImage, MultipartFile> {

    @Override
    public boolean isValid(MultipartFile value, ConstraintValidatorContext context) {

        // Emptyなら通す。他のバリデーターで検証する
        if (value.isEmpty()) {
            return true;
        }

        MediaType mediaType = MediaType.parseMediaType(value.getContentType());

        String ext = FilenameUtils.getExtension(value.getOriginalFilename());

        List<MediaType> mediaTypeList = Arrays.asList(MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG, MediaType.IMAGE_GIF);
        List<String> extList = Arrays.asList("jpg", "jpeg", "png", "gif");

        return mediaTypeList.stream().anyMatch((mType) -> mediaType.includes(mType))
                && extList.stream().anyMatch((v) -> ext.toLowerCase().equals(v));
    }

}

以下のように使う。

package aaaaaa.dto.form;

import org.springframework.web.multipart.MultipartFile;

import aaaaaa.validator.ContentTypeZip;
import aaaaaa.validator.MediaTypeImage;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class UploadForm {
    @MediaTypeImage
    private MultipartFile thumbnail;
}

他はちょっと待ってー。コードを掘り返してるんだけど、記憶が曖昧で……。とりあえずここまで。