[Spring] Pre-signed URL

김상현·2023년 9월 22일
0

Spring

목록 보기
13/13
post-thumbnail

📚 Pre-signed URL이란?

Pre-signed URL을 사용하여 다른 사람이 Amazon S3 버킷에 객체를 업로드하도록 허용할 수 있습니다. Pre-signed URL을 사용하면 상대방에게 AWS 보안 자격 증명이나 권한이 없어도 업로드할 수 있습니다. Pre-signed URL은 이를 생성하는 사용자의 권한에 따라 제한됩니다. 즉, 객체를 업로드하기 위해 Pre-signed URL을 수신하는 경우, URL의 생성자가 해당 객체를 업로드하는 데 필요한 권한을 보유하는 경우에만 객체를 업로드할 수 있습니다.

💡 Pre-signed URL 사용 이유?

현재 진행중인 프로젝트에서는 이미지를 S3 버킷에 저장하는 기능이 필요했습니다. 매번 이미지를 저장할 때마다 이미지 데이터가 클라이언트에서 서버를 거쳐 S3로 이동하는 것은 서버 입장에서 굉장한 리소스 낭비라고 생각했습니다. 하지만 접근 권한 규칙 없이 모든 클라이언트가 직접 S3에 이미지를 업로드할 수 있게 된다면 우리 서비스를 사용하지 않는 다른 서비스에서 우리의 S3 버킷을 public 이미지 저장소로 사용할 수도 있다는 문제점을 발견하고 인증 받은 사용자만 이미지를 저장할 수 있도록 클라이언트에서 서버를 거쳐 S3로 이미지를 저장하는 로직을 구현하기 위해 계획을 세웠습니다.

개발 도중에 서버를 거치지 않고 접근 권한이 있는 클라이언트에서만 S3로 이미지를 직접 저장할 수 있는 방법이 있다는 것을 알게 되었습니다. 서버가 Cloud Front에게 Pre-signed URL을 요청한 후 발급받아서 해당 Pre-signed URL을 클라이언트에게 다시 제공합니다. S3 버킷에 저장할 이미지를 body에 담아 Pre-signed URL로 요청을 보내게 되면 서버를 거치지 않고 S3 버킷에 직접 이미지를 저장할 수 있게 됩니다.

Pre-signed URL 코드

S3Config

@Configuration
public class S3Config {
    @Value("${cloud.aws.credentials.accessKey}")
    private String accessKey;

    @Value("${cloud.aws.credentials.secretKey}")
    private String secretKey;

    @Value("${cloud.aws.region.static}")
    private String region;

    @Bean
    public AmazonS3 amazonS3Client() {

        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

        return AmazonS3ClientBuilder
                .standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withRegion(region)
                .build();
    }

}

S3Controller

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/s3")
public class S3Controller {

    private final S3Service s3Service;

    @GetMapping("/pre-signed-url")
    public ResponseEntity generatePreSignedUrl(@RequestParam("fileName") String fileName) {
        String preSignedUrl = s3Service.generatePreSignedUrl(fileName);
        return new ResponseEntity<>(new ResponseDto<>(1, "S3 pre-signed url 발급에 성공하였습니다.", preSignedUrl), CREATED);
    }

}

S3Service

@Service
@RequiredArgsConstructor
public class S3Service {

    private final AmazonS3Client amazonS3Client;

    @Value("${AWS_S3_BUCKET}")
    private String BUCKET_NAME;

    public String generatePreSignedUrl(String file) {
        GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(BUCKET_NAME, generateObjectKey(file))
                .withMethod(HttpMethod.PUT)
                .withExpiration(new Date(new Date().getTime() + (60 * 1000)));
        return amazonS3Client.generatePresignedUrl(req).toString();
    }

    public String generateObjectKey(String file) {
        String[] fileStructure = file.split("\\.");
        return fileStructure[0] + "-" + LocalDateTime.now() + "." + fileStructure[1];
    }

}

🔥 새로운 문제점

Pre-signed URL을 이용한 이미지 업로드 구현이 완료된 이후 새로운 이슈가 발생하였습니다.

보안 위협

악의적인 사용자가 .sh 파일을 업로드하고 실행하는 경우, 시스템에 대한 악의적인 명령을 실행할 수 있으며, 이로 인해 시스템의 보안이 위협될 수 있습니다. 예를 들어, 파일 시스템을 손상시키거나 중요한 데이터를 삭제할 수 있습니다.

권한 문제

.sh 파일이 실행될 때 해당 작업이 실행된 사용자의 권한으로 실행됩니다. 따라서 파일이 S3 버킷에 대한 쓰기 액세스 권한을 갖고 있는 사용자로 실행된다면, 악의적인 사용자가 S3 버킷에 .sh 파일을 업로드 한 이후 정상적인 사용자가 해당 파일에 접근하게 될 경우 정상적인 사용자의 클라이언트에 문제가 발생할 수 있습니다.

위 이슈들로 인해서 다시 처음으로 돌아가서 클라이언트에서 서버로 이미지를 전송한 후 해당 파일이 이미지 파일의 형식이 맞는지, 이미지의 크기가 과도하게 크진 않은지 확인한 이후 서버에서 S3 버킷으로 이미지를 저장하는 기능을 다시 구현하였습니다.

profile
목적 있는 글쓰기

0개의 댓글