Firebase Cloud Storage 보안 규칙의 조건 사용

이 가이드는 Firebase Security Rules 언어의 핵심 문법 알아보기 가이드를 기반으로 합니다. 이 가이드에서 Cloud StorageFirebase Security Rules에 조건을 추가하는 방법을 알아보세요.

Cloud Storage Security Rules의 기본 구성요소는 조건입니다. 조건이란 특정 작업을 허용 또는 거부할지 여부를 결정하는 부울 표현식입니다. 기본 규칙의 경우 truefalse 리터럴을 조건으로 사용하면 원활하게 작동합니다. 하지만 Cloud StorageFirebase Security Rules 언어를 사용하면 다음과 같은 작업이 가능한 좀 더 복잡한 조건을 작성할 수 있습니다.

  • 사용자 인증 확인
  • 수신 데이터의 유효성 검사

인증

Cloud StorageFirebase Security RulesFirebase Authentication과 통합되어 Cloud Storage에 대한 강력한 사용자 기반 인증을 제공합니다. 이렇게 하면 Firebase Authentication 토큰의 클레임에 따라 세분된 액세스 제어가 가능합니다.

인증된 사용자가 Cloud Storage에 대한 요청을 수행하면 request.auth 변수가 사용자의 uid(request.auth.uid) 및 Firebase Authentication JWT의 클레임(request.auth.token)으로 채워집니다.

또한 커스텀 인증을 사용하는 경우 추가 클레임이 request.auth.token 필드에 표시됩니다.

인증되지 않은 사용자가 요청을 수행하면 request.auth 변수는 null입니다.

이 데이터를 사용하여 인증을 통해 파일의 보안을 설정하는 일반적인 방법은 다음과 같습니다.

  • 공개: request.auth 무시
  • 인증 비공개: request.authnull이 아닌지 확인
  • 사용자 비공개: request.auth.uiduid 경로와 같은지 확인
  • 그룹 비공개: 커스텀 토큰의 클레임이 특정 클레임과 일치하는지 확인하거나 파일 메타데이터를 읽어 메타데이터 필드가 있는지 확인

공개

request.auth 컨텍스트를 고려하지 않는 모든 규칙은 사용자의 인증 컨텍스트를 고려하지 않으므로 public 규칙으로 간주될 수 있습니다. 이러한 규칙은 게임 자산, 사운드 파일 또는 기타 정적 콘텐츠와 같은 공개 데이터를 노출하는 데 유용합니다.

// Anyone to read a public image if the file is less than 100kB
// Anyone can upload a public file ending in '.txt'
match /public/{imageId} {
  allow read: if resource.size < 100 * 1024;
  allow write: if imageId.matches(".*\\.txt");
}

인증 비공개

애플리케이션의 특정 데이터를 인증된 사용자에게만 공개하고 비인증 사용자에게는 숨겨야 할 수 있습니다. 인증되지 않은 모든 사용자는 request.auth 변수가 null이므로 인증을 요구하려면 request.auth 변수가 있는지 확인하면 됩니다.

// Require authentication on all internal image reads
match /internal/{imageId} {
  allow read: if request.auth != null;
}

사용자 비공개

request.auth의 가장 일반적인 사용 사례는 프로필 사진 업로드, 개인 문서 조회와 같이 파일에 개별 사용자 단위로 권한을 부여하는 것입니다.

Cloud Storage의 파일에는 파일의 전체 '경로'가 있기 때문에 사용자가 제어하는 파일을 만들려면 규칙이 판정될 때 확인할 수 있는 고유한 사용자 식별 정보(예: 사용자의 uid)가 파일 이름 프리픽스에 있기만 하면 됩니다.

// Only a user can upload their profile picture, but anyone can view it
match /users/{userId}/profilePicture.png {
  allow read;
  allow write: if request.auth.uid == userId;
}

그룹 비공개

또 다른 일반적인 사용법은 여러 팀원이 공유 문서를 공동으로 작업하는 경우와 같이 객체에 대한 그룹 권한을 허용하는 것입니다. 여기에는 몇 가지 방법이 있습니다.

  • 그룹 구성원에 대한 추가 정보(예: 그룹 ID)를 포함하는 Firebase Authentication 커스텀 토큰 발급
  • 파일 메타데이터에 그룹 정보(예: 그룹 ID 또는 승인된 uid 목록) 포함

이러한 데이터를 토큰이나 파일 메타데이터에 저장한 후 규칙 내에서 참조할 수 있습니다.

// Allow reads if the group ID in your token matches the file metadata's `owner` property
// Allow writes if the group ID is in the user's custom token
match /files/{groupId}/{fileName} {
  allow read: if resource.metadata.owner == request.auth.token.groupId;
  allow write: if request.auth.token.groupId == groupId;
}

요청 검증

업로드, 다운로드, 메타데이터 변경 및 삭제는 Cloud Storage로 전송된 request를 사용하여 검증됩니다. 위에서 설명한 request.auth 객체의 사용자 고유 ID 및 Firebase Authentication 페이로드 외에도 request 변수에는 요청이 이루어지는 파일 경로, 요청 수신 시간, 새 resource 값(쓰기 요청인 경우)이 포함됩니다.

또한 request 객체는 request.auth 객체에 사용자의 고유 ID 및 Firebase Authentication 페이로드를 포함합니다. 자세한 내용은 문서의 사용자 기반 보안 섹션을 참고하세요.

다음은 request 객체의 전체 속성 목록입니다.

속성 유형 설명
auth 맵<문자열, 문자열> 사용자가 로그인하면 uid(사용자의 고유 ID) 및 token(Firebase Authentication JWT 클레임의 맵)을 제공합니다. 그렇지 않으면 null입니다.
params 맵<문자열, 문자열> 요청의 쿼리 매개변수를 포함하는 맵입니다.
path 경로 요청이 이루어지는 경로를 나타내는 path입니다.
resource 맵<문자열, 문자열> 새 리소스 값이며 write 요청에만 있습니다.
time 타임스탬프 요청을 검증하는 서버 시간을 나타내는 타임스탬프입니다.

리소스 검증

규칙을 판정할 때 업로드, 다운로드, 수정 또는 삭제되는 파일의 메타데이터를 검증할 수도 있습니다. 이를 통해 특정 콘텐츠 유형의 파일만 업로드를 허용하거나 특정 크기보다 큰 파일만 삭제를 허용하는 등의 복잡하고 강력한 규칙을 만들 수 있습니다.

Cloud StorageFirebase Security Rulesresource 객체에 파일 메타데이터를 제공하며 이 객체는 Cloud Storage 객체에 노출된 메타데이터의 키-값 쌍을 포함합니다. read 또는 write 요청에서 이러한 속성을 검사하여 데이터 무결성을 확보할 수 있습니다.

업로드, 메타데이터 업데이트, 삭제 등의 write 요청에서는 현재 요청 경로에 있는 파일의 메타데이터를 포함하는 resource 객체뿐 아니라 쓰기가 허용된 경우 쓸 파일 메타데이터 중 일부를 포함하는 request.resource 객체도 사용할 수 있습니다. 이러한 두 값을 사용하여 데이터 무결성을 확인하거나 파일 형식, 크기 등의 애플리케이션 제한사항을 적용할 수 있습니다.

다음은 resource 객체의 전체 속성 목록입니다.

속성 유형 설명
name 문자열 객체의 전체 이름입니다.
bucket 문자열 객체가 속한 버킷의 이름입니다.
generation int 이 객체의 Google Cloud Storage 객체 세대입니다.
metageneration int 이 객체의 Google Cloud Storage 객체 메타 세대입니다.
size int 객체 크기(바이트)입니다.
timeCreated 타임스탬프 객체 생성 시간을 나타내는 타임스탬프입니다.
updated 타임스탬프 객체의 마지막 업데이트 시간을 나타내는 타임스탬프입니다.
md5Hash 문자열 객체의 MD5 해시입니다.
crc32c 문자열 객체의 crc32c 해시입니다.
etag 문자열 이 객체에 연결된 etag입니다.
contentDisposition 문자열 이 객체에 연결된 콘텐츠 처리입니다.
contentEncoding 문자열 이 객체에 연결된 콘텐츠 인코딩입니다.
contentLanguage 문자열 이 객체에 연결된 콘텐츠 언어입니다.
contentType 문자열 이 객체에 연결된 콘텐츠 유형입니다.
metadata 맵<문자열, 문자열> 개발자가 추가로 지정한 커스텀 메타데이터의 키-값 쌍입니다.

request.resourcegeneration, metageneration, etag, timeCreated, updated만 제외하고 이러한 속성을 모두 포함합니다.

Cloud Firestore로 개선

Cloud Firestore 문서에 액세스하여 다른 승인 기준을 평가할 수 있습니다.

보안 규칙에서 firestore.get()firestore.exists() 함수를 사용하여 수신된 요청을 Cloud Firestore 문서와 비교할 수 있습니다. firestore.get()firestore.exists() 함수 모두 문서 경로 전체를 지정해야 합니다. 변수를 사용하여 firestore.get()firestore.exists()의 경로를 작성하는 경우 $(variable) 문법을 사용하여 변수를 명시적으로 이스케이프해야 합니다.

아래 예에는 파일에 대한 읽기 액세스를 특정 클럽의 멤버로 제한하는 규칙이 있습니다.

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{club}/files/{fileId} {
      allow read: if club in
        firestore.get(/databases/(default)/documents/users/$(request.auth.id)).memberships
    }
  }
}
다음 예에서는 사용자의 친구만 사진을 볼 수 있습니다.
service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/photos/{fileId} {
      allow read: if
        firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.id))
    }
  }
}

Cloud Firestore 함수를 사용하는 첫 번째 Cloud Storage Security Rules를 만들고 저장하면 Firebase 콘솔 또는 Firebase CLI에 두 제품을 연결할 수 있는 권한을 사용 설정하라는 메시지가 표시됩니다.

Firebase Security Rules 관리 및 배포에 설명된 대로 IAM 역할을 삭제하여 이 기능을 사용 중지할 수 있습니다.

데이터 검증

Cloud StorageFirebase Security Rules는 파일 이름 및 경로 검증뿐만 아니라 contentTypesize와 같은 파일 메타데이터 속성 검증을 비롯한 데이터 유효성 검사에도 사용할 수 있습니다.

service firebase.storage {
  match /b/{bucket}/o {
    match /images/{imageId} {
      // Only allow uploads of any image file that's less than 5MB
      allow write: if request.resource.size < 5 * 1024 * 1024
                   && request.resource.contentType.matches('image/.*');
    }
  }
}

커스텀 함수

Firebase Security Rules가 복잡해지면 조건 세트를 함수로 묶어 규칙 세트 전체에서 재사용할 수 있습니다. 보안 규칙에서는 커스텀 함수를 지원합니다. 커스텀 함수의 문법은 JavaScript와 비슷하지만, Firebase Security Rules 함수는 도메인 언어로 작성되며 다음과 같은 중요한 제한사항이 있습니다.

  • 함수는 return 문 하나만 포함할 수 있습니다. 다른 로직은 포함할 수 없습니다. 예를 들어 루프를 실행하거나 외부 서비스를 호출할 수 없습니다.
  • 함수는 정의된 범위에 속하는 함수와 변수에 자동으로 액세스할 수 있습니다. 예를 들어 service firebase.storage 범위 안에 정의된 함수는 resource 변수 및 Cloud Firestore 전용 get(), exists() 등의 기본 제공 함수에 액세스할 수 있습니다.
  • 함수는 다른 함수를 호출할 수 있지만 재귀 호출은 금지됩니다. 호출 스택 깊이는 최대 10으로 제한됩니다.
  • rules2 버전에서 함수는 let 키워드를 사용하여 변수를 정의할 수 있습니다. 함수는 여러 개의 let 바인딩을 가질 수 있지만 반환 구문으로 끝나야 합니다.

함수는 function 키워드로 정의되며 0개 이상의 인수를 취합니다. 예를 들어 위 예시에 사용한 두 가지 유형의 조건을 하나의 함수로 결합할 수 있습니다.

service firebase.storage {
  match /b/{bucket}/o {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }
    match /images/{imageId} {
      allow read, write: if signedInOrPublic();
    }
    match /mp3s/{mp3Ids} {
      allow read: if signedInOrPublic();
    }
  }
}

Firebase Security Rules에서 함수를 사용하면 복잡성이 커짐에 따라 유지관리가 더 쉬워집니다.

다음 단계

지금까지 조건에 대해 학습하며 규칙에 대해 자세히 알아봤습니다. 이제 다음과 같은 단계를 진행할 수 있습니다.

핵심 사용 사례를 처리하는 방법을 알아보고 규칙 개발, 테스트, 배포를 위한 워크플로를 살펴봅니다.