iOS 사진 내보내기의 GPS 메타데이터 제거 검증

Security ·약 9분 읽기

iOS 사진 내보내기의 GPS 메타데이터 제거 검증

사진 공유 화면에 촬영 위치가 표시되지 않더라도, 내보낸 파일에는 좌표가 남아 있을 수 있습니다. iOS 사진 내보내기 기능을 검증할 때는 앱 안의 미리보기가 아니라 수신자가 최종적으로 받는 파일을 확인해야 합니다. 클라우드 Mac에 GPS 정보가 포함된 테스트 사진을 준비하면 이 개인정보 보호 요구사항을 반복 가능한 파일 검사로 바꿀 수 있습니다.

내보내기 경계부터 확인하기

일반적인 흐름은 사진 선택, 편집 또는 크기 조정, 임시 파일 생성, 공유 절차로의 파일 전달 순서입니다. 각 단계에서 메타데이터 처리 방식이 달라질 수 있습니다. 사진 선택기가 반환한 이미지만 검사해서는 최종 파일에서 정보가 제거됐다고 입증할 수 없고, 공유 전 썸네일만 확인해도 마찬가지입니다.

테스트 픽스처는 팀에서 직접 만들고, 원본 파일에 GPS 딕셔너리가 실제로 들어 있는지 확인해야 합니다. 실제 사용자 사진을 지속적 통합 테스트에 사용하거나 좌표가 포함된 픽스처를 공개 저장소에 넣지 마세요. 접근이 통제된 테스트 자산 디렉터리에 픽스처를 보관하고 예상 형식과 픽셀 크기를 기록할 수 있습니다. 테스트가 끝나면 생성된 파일을 삭제하세요.

이 글의 통과 기준은 ‘최종 파일에 GPS 딕셔너리가 없다’는 것이지, ‘내보낸 파일이 원본과 바이트 단위로 동일하다’는 것이 아닙니다. 다시 인코딩하면 파일 해시는 대개 달라집니다.

ImageIO로 파일 생성 및 재검사하기

다음 Swift 스크립트는 입력 이미지를 읽고 디코딩한 뒤 원본 파일과 같은 형식으로 다시 인코딩합니다. 방향 정보만 명시적으로 전달하고, 입력 파일의 메타데이터 딕셔너리 전체를 출력에 넘기지는 않습니다. 이후 출력 파일을 다시 열어 GPS 딕셔너리, 크기, 방향, 형식을 검사합니다. 먼저 격리된 디렉터리에 redact.swift로 저장하세요.

import Foundation
import ImageIO
import CoreGraphics

guard CommandLine.arguments.count == 3 else {
    fatalError("Usage: redact input output")
}

let input = URL(fileURLWithPath: CommandLine.arguments[1])
let output = URL(fileURLWithPath: CommandLine.arguments[2])
guard input.standardizedFileURL != output.standardizedFileURL else {
    fatalError("Input and output must differ")
}
guard let source = CGImageSourceCreateWithURL(input as CFURL, nil),
      let type = CGImageSourceGetType(source),
      let image = CGImageSourceCreateImageAtIndex(source, 0, nil),
      let original = CGImageSourceCopyPropertiesAtIndex(source, 0, nil)
        as? [CFString: Any],
      let destination = CGImageDestinationCreateWithURL(
        output as CFURL, type, 1, nil
      ) else {
    fatalError("Cannot read input or create output")
}

let orientation = original[kCGImagePropertyOrientation] ?? 1
let options: [CFString: Any] = [
    kCGImagePropertyOrientation: orientation
]
CGImageDestinationAddImage(destination, image, options as CFDictionary)
guard CGImageDestinationFinalize(destination),
      let result = CGImageSourceCreateWithURL(output as CFURL, nil),
      let finalImage = CGImageSourceCreateImageAtIndex(result, 0, nil),
      let metadata = CGImageSourceCopyPropertiesAtIndex(result, 0, nil)
        as? [CFString: Any] else {
    fatalError("Cannot verify output")
}

guard metadata[kCGImagePropertyGPSDictionary] == nil,
      CGImageSourceGetType(result) == type,
      finalImage.width == image.width,
      finalImage.height == image.height,
      String(describing: metadata[kCGImagePropertyOrientation] ?? 1)
        == String(describing: orientation) else {
    fatalError("Export verification failed")
}
print("GPS removed; format, dimensions and orientation checked")

클라우드 Mac의 터미널에서 실행하세요.

swiftc -framework Foundation -framework ImageIO \
  -framework CoreGraphics redact.swift -o redact
./redact fixtures/with-gps.jpg output/without-gps.jpg

먼저 output 디렉터리를 만들고, 예시 입력 파일을 GPS 딕셔너리가 포함됐음을 확인한 JPEG로 바꾸세요. 실행에 성공했다는 것은 이번 출력 파일이 검사를 통과했다는 뜻일 뿐입니다. 실제 앱에 자르기, 압축 또는 공유 확장 경로가 있다면 각 경로에서 생성한 최종 파일을 검사해야 합니다. 이 스크립트를 앱의 실제 내보내기 구현을 대신하는 용도로 사용해서는 안 됩니다.

음성 테스트 사례 추가하기

‘정보를 제거한 뒤 통과한다’는 결과만으로는 검증 조건이 제대로 작동하는지 알 수 없습니다. 동일한 읽기 로직으로 입력 픽스처를 검사했을 때 GPS 딕셔너리가 검출되어야 하고, 출력을 검사했을 때는 없어야 합니다. 입력에 애초에 좌표가 없다면 양쪽이 모두 통과해도 의미 있는 검증이 아닙니다. 로그를 통한 정보 유출을 막기 위해 좌표 값이나 전체 메타데이터를 출력하지 마세요.

정보를 제거하지 않고 파일 바이트를 그대로 복사한 파일도 음성 테스트 사례로 보관해 검사기가 이를 거부하는지 확인하세요. 복사할 때는 메타데이터를 다시 쓸 수 있는 이미지 도구를 거치지 마세요. 이렇게 하면 제거 단계가 작동하는지뿐 아니라, 검증기가 ‘읽기 실패’를 ‘GPS 없음’으로 잘못 판단하지 않는지도 확인할 수 있습니다.

다시 인코딩한 뒤 확인할 사항

위치 정보를 제거했다고 해서 이미지 기능이 모두 정상이라는 뜻은 아닙니다. 디코딩 후 다시 인코딩하는 과정에서 손실 압축 품질, 색상 표현 또는 위치 정보 이외의 메타데이터가 달라질 수 있습니다. 크기가 같아도 픽셀 단위로 동일한 이미지는 아닐 수 있습니다. 출처가 다른 사진으로 회귀 테스트 세트를 구성하고, 최소한 세로 사진과 가로 사진, 서로 다른 방향 표시를 포함하세요. 최종 파일을 직접 열어 회전 오류, 색상 틀어짐 또는 눈에 띄는 화질 저하가 없는지 확인해야 합니다.

검사 항목 통과 조건
위치 정보 최종 파일에 GPS 딕셔너리가 없음
이미지 크기 해당 내보내기 경로에서 예상한 너비와 높이와 일치
표시 방향 출력 파일의 방향 표시에 따라 올바르게 표시
파일 형식 수신자가 예상한 형식의 파일을 받음
읽기 실패 테스트 실패로 처리하고 ‘GPS 없음’으로 간주하지 않음

예시 스크립트는 출력 크기가 입력과 같아야 하므로 크기를 조정하지 않는 내보내기 경로에 적합합니다. 제품에서 이미지를 자르거나 압축한다면 크기 검증 조건을 해당 경로의 명확한 예상값으로 바꿔야 합니다. 색상 프로파일이나 특정 저작권 필드를 유지해야 하는 경우에도 각 필드의 테스트를 별도로 작성하세요. 이를 보존하려고 원본 메타데이터 딕셔너리를 통째로 복사해서는 안 됩니다.

클라우드 Mac 검증 절차에 통합하기

픽스처, 앱이 실제 생성한 파일, 검사 스크립트는 분리해서 보관하세요. 파이프라인에서는 먼저 내보내기를 실행한 다음 최종 전달 경로의 파일을 읽어야 합니다. 파일이 없거나 디코딩할 수 없거나 메타데이터를 읽지 못하면 즉시 실패로 처리하세요. 병렬 작업에는 각각 별도의 출력 디렉터리를 사용해 이전 실행에서 남은 파일 때문에 잘못 통과하는 일이 없도록 합니다.

마지막으로 수동 검증도 수행하세요. 앱에서 실제 공유 또는 저장 작업을 완료한 뒤, 중간 캐시가 아니라 수신자가 받은 파일을 다시 검사합니다. 이를 통해 ‘편집 미리보기에서는 정보가 제거됐지만 공유할 때 원본 파일을 다시 참조하는’ 경로 오류를 발견할 수 있습니다. 검증을 통과하면 임시 내보내기 파일을 삭제하세요. 테스트 보고서에는 GPS 딕셔너리의 존재 여부, 크기, 방향 및 실패 단계만 기록하고 좌표는 기록하지 않습니다. 이렇게 해야 검증 결과를 재현할 수 있으면서도, 문제 조사 로그에 제거 대상인 정보가 다시 노출되지 않습니다.

자주 묻는 질문

앱 화면에 촬영 위치가 보이지 않으면 내보낸 파일도 안전한가요?

아닙니다. 화면 표시와 파일 내보내기는 별개이므로 최종 파일에서 GPS 메타데이터가 없는지 직접 확인해야 합니다.

재인코딩 전후 파일 해시가 같아야 하나요?

아닙니다. 재인코딩은 파일 바이트를 바꿀 수 있습니다. 대신 GPS 정보, 이미지 크기, 방향과 표시 결과를 각각 확인하세요.

필요한 기간만큼 대여

독립형 Mac mini로 다음 단계를 검증하세요

VMCommit은 M4 독립형 Mac mini를 일간, 주간, 월간 또는 분기 단위로 대여합니다. 모델과 위치를 선택한 뒤 콘솔에서 실시간 이용 가능 여부를 확인하세요.

Mac mini 지금 대여하기