Android FileProvider

김성환·2024년 3월 26일

안드로이드 앱을 개발하다보면 기기안에있는 리소스에 접근을 해야할때가 있습니다. 예를들어 사진이라든가 문서, 비디오등이 있다고 해보죠. 이경우 해당 위치에 대한주소를 가지고 오더라고 해당 Uri가 file:// 스킴을 사용하는데, Android 7.0 (API 레벨 24)부터는 파일 시스템 경로를 직접 노출할 수 없고, 사용하더라도 FileUriExposedException이 발생합니다. 이는 보안상의 이유로 앱 간 파일 공유를 제한하기 위한 것입니다. 이 경우 우린 FileProvider를 이용할수 있습니다.

FileProvider.getUriForFile()를 사용하면 FileProvider가 제공하는 content:// 스킴을 통해 파일에 대한 안전한 접근이 가능하며, 파일 공유 규칙을 준수할 수 있습니다. 이를 통해 안드로이드 시스템은 앱 간 파일 공유를 보다 안전하게 할 수 있습니다.

공식 문서를 봐봅시다

FileProvider is a special subclass of ContentProvider that facilitates secure sharing of files associated with an app by creating a content:// Uri for a file instead of a file:/// Uri.
A content URI allows you to grant read and write access using temporary access permissions. When you create an Intent containing a content URI, in order to send the content URI to a client app, you can also call Intent.setFlags() to add permissions. These permissions are available to the client app for as long as the stack for a receiving Activity is active. For an Intent going to a Service, the permissions are available as long as the Service is running.
In comparison, to control access to a file:/// Uri you have to modify the file system permissions of the underlying file. The permissions you provide become available to any app, and remain in effect until you change them. This level of access is fundamentally insecure.
The increased level of file access security offered by a content URI makes FileProvider a key part of Android's security infrastructure.

FileProvider는 ContentProvider의 특수한 하위 클래스로, 앱과 관련된 파일을 안전하게 공유할 수 있도록 지원합니다. 이를 통해 file:/// Uri 대신 파일에 대한 content:// Uri를 생성합니다. content URI를 사용하면 임시 액세스 권한을 사용하여 읽기 및 쓰기 액세스를 부여할 수 있습니다. content URI를 포함하는 Intent를 생성할 때, 클라이언트 앱에 content URI를 전송하려면 Intent.setFlags()를 호출하여 권한을 추가할 수도 있습니다. 이러한 권한은 수신하는 Activity의 스택이 활성화된 동안에만 클라이언트 앱에서 사용할 수 있습니다. Service로 이동하는 Intent의 경우, 권한은 Service가 실행 중인 동안에만 사용할 수 있습니다.
반면에, file:/// Uri에 대한 액세스를 제어하려면 기본 파일의 파일 시스템 권한을 수정해야 합니다. 제공하는 권한은 모든 앱에서 사용할 수 있으며, 권한은 변경할 때까지 계속 유효합니다. 이러한 수준의 액세스는 근본적으로 보안이 취약합니다.
content URI를 통해 제공되는 증가된 파일 액세스 보안 수준은 FileProvider를 안드로이드의 보안 인프라의 핵심 요소로 만듭니다.


파일프로바이더는 어떻게 사용할까요? 그전에 앱에서 파일프로바이더를 사용하려면 기초작업이 필요합니다.

먼저 manifest의 파일프로바이더와 패스를 등록해줘야합니다.

<application ... >
    <provider
        android:authorities="com.example.myapplication.fileprovider"
        android:name="androidx.core.content.FileProvider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>

이때 파일프로바이더드 커스텀으로 fileprovider 상속한 클래스를 만들어도 되고 이렇게 com.example.myapplication.fileprovider를 사용해도 됩니다.
패스의 경우 FileProvider는 사전에 지정한 디렉터리에 있는 파일에 대한 콘텐츠 URI만 생성할 수 있습니다. 디렉터리를 지정하려면 해당 < paths>요소의 하위 요소를 사용하여 XML로 해당 저장 영역과 경로를 지정해야 합니다.

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path
        name="internal_path"
        path="." />
    : 내부저장소 / Context.getFilesDir()
    <cache-path
        name="internal_cache_path"
        path="." />
    : 내부저장소 / getCacheDir()
    <external-path
        name="external_path"
        path="." />
    : 외부저장소 / Environment.getExternalStorageDirectory()
    <external-files-path
        name="external_file_path"
        path="." />
    : 외부저장소 / Context.getExternalFilesDir()
    <external-cache-path
        name="external_cache_path"
        path="." />
    : 외부캐시영역 / Context.getExternalCacheDir()
</paths>

이후 코드에서 파일 프로바이더를 이용하여 content:// 스킴을 가지는 Uri를 가져와 사용할수 있습니다.

File imagePath = new File(Context.getFilesDir(), "my_images");
File newFile = new File(imagePath, "default_image.jpg");
Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);

reference
https://developer.android.com/reference/androidx/core/content/FileProvider
http://www.gisdeveloper.co.kr/?p=10595

0개의 댓글