이 페이지는
보안 규칙 구조화 및
보안 규칙 조건 작성을 통해
Cloud Firestore Security Rules는 쿼리와 상호작용합니다. 작성할 수 있는 쿼리에 보안 규칙이 어떤 영향을 미치는지 자세히 살펴보고 쿼리에서 보안 규칙과 동일한 제약조건을 사용하게 하는 방법을 설명합니다. 이 페이지도
쿼리를 기반으로 쿼리를 허용하거나 거부하는 보안 규칙을 작성하는 방법을 설명합니다.
속성(예: limit
, orderBy
)
규칙과 필터의 차이
쿼리를 작성하여 문서를 가져올 때 보안 규칙은 필터가 아니므로 전부 쿼리하거나 전혀 쿼리하지 않는다는 점에 유의해야 합니다. 시간과 리소스를 절약하기 위해 Cloud Firestore는 잠재적 결과 집합을 기준으로 쿼리를 평가합니다. 필드 값 대신 사용됩니다. 쿼리가 클라이언트에 읽기 권한이 없는 문서를 반환할 수도 있는 경우에 전체 요청이 실패합니다.
쿼리 및 보안 규칙
아래 예시에서 볼 수 있듯이 보안 규칙의 제약조건에 맞게 쿼리를 작성해야 합니다.
auth.uid
를 기반으로 문서 보호 및 쿼리
다음 예시는 쿼리를 작성하여 보안 규칙으로 보호하는 문서를 가져오는 방법을 보여줍니다. story
문서의 컬렉션이 포함된 데이터베이스를 예로 들어보겠습니다.
/stories/{storyid}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
title
및 content
필드 외에도 각 문서가 액세스 제어에 사용할 author
및 published
필드를 저장합니다. 이 예시에서는 앱이 Firebase 인증을 사용하여 author
필드를 문서 생성자 UID로 설정한다고 가정합니다. 또한 Firebase 인증이 보안 규칙의 request.auth
변수를 채웁니다.
다음 보안 규칙은 request.auth
및 resource.data
변수를 사용하여 각 story
의 읽기 및 쓰기 액세스를 해당 작성자로 제한합니다.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Only the authenticated user who authored the document can read or write
allow read, write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
앱에 자신이 작성한 story
문서의 목록을 사용자에게 표시하는 페이지가 있다고 가정해 보겠습니다. 다음 쿼리를 사용하여 이 페이지를 채울 수 있을 것이라 생각할 수도 있지만 이 쿼리에 보안 규칙과 동일한 제약조건이 없으므로 쿼리가 실패합니다.
무효: 쿼리 제약조건이 보안 규칙 제약조건과 일치하지 않습니다.
// This query will fail
db.collection("stories").get()
현재 사용자가 실제로 모든 story
문서의 작성자인 경우에도 쿼리가 실패합니다. 이러한 동작이 발생하는 이유는
Cloud Firestore에서 보안 규칙을 적용하고 쿼리를 평가합니다.
테스트의 실제 속성이 아니라 잠재적 결과 세트에 대해
문서의 첫 번째 레이어가 있습니다 쿼리에 문서가 포함될 수도 있는 경우
쿼리가 실패합니다
반면에 다음 쿼리는 성공하는데, 그 이유는 보안 규칙과 동일한 제약조건을 author
필드에 포함하기 때문입니다.
유효: 쿼리 제약조건이 보안 규칙 제약조건과 일치합니다.
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
필드를 기준으로 문서 보안 적용 및 쿼리
쿼리와 규칙의 상호작용을 더 자세히 보여주기 위해 아래 보안 규칙은 모든 사용자가 published
필드가 true
로 설정된 story
문서를 읽을 수 있도록 stories
컬렉션의 읽기 액세스 권한을 확장합니다.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Anyone can read a published story; only story authors can read unpublished stories
allow read: if resource.data.published == true || (request.auth != null && request.auth.uid == resource.data.author);
// Only story authors can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
게시된 페이지의 쿼리에는 보안 규칙과 동일한 제약조건을 포함해야 합니다.
db.collection("stories").where("published", "==", true).get()
쿼리 제약조건 .where("published", "==", true)
는 resource.data.published
가 모든 결과에 대해 true
라고 보장합니다. 따라서 이 쿼리는 보안 규칙을 충족하며 데이터를 읽을 수 있습니다.
OR
쿼리
논리적 OR
쿼리 (or
, in
또는 array-contains-any
)를 평가할 때
Cloud Firestore는 규칙 세트에 대해 각 비교 값을 평가합니다.
별도로 관리할 수 있습니다 각 비교 값은 보안 규칙 제약조건을 충족해야 합니다. 다음 규칙의 경우를 예로 들어보겠습니다.
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
무효: 쿼리가 모든 잠재적 문서에 대해 x > 5
의 조건을 보장하지 않습니다.
// These queries will fail
query(db.collection("mydocuments"),
or(where("x", "==", 1),
where("x", "==", 6)
)
)
query(db.collection("mydocuments"),
where("x", "in", [1, 3, 6, 42, 99])
)
유효: 쿼리가 모든 잠재적 문서에 대해 x > 5
의 조건을 보장합니다.
query(db.collection("mydocuments"),
or(where("x", "==", 6),
where("x", "==", 42)
)
)
query(db.collection("mydocuments"),
where("x", "in", [6, 42, 99, 105, 200])
)
쿼리의 제약조건 평가
또한 보안 규칙은 제약조건을 기준으로 쿼리를 허용하거나 거부할 수 있습니다.
request.query
변수에는 쿼리의 limit
, offset
, orderBy
속성이 포함됩니다. 예를 들어 보안 규칙은 가져오는 최대 문서 수를 특정 범위로 제한하지 않는 쿼리를 거부할 수 있습니다.
allow list: if request.query.limit <= 10;
다음 규칙 세트는 쿼리에 적용된 제약조건을 평가하는 보안 규칙을 작성하는 방법을 보여줍니다. 이 예시는 다음 변경사항을 포함하여 이전 stories
규칙 세트를 확장합니다.
- 규칙 세트가 읽기 규칙을
get
및list
규칙으로 분리합니다. get
규칙은 문서 검색을 공개 문서나 사용자가 작성한 문서로 제한합니다.list
규칙은get
과 동일한 제한사항을 쿼리에 적용합니다. 또한 쿼리 한도를 확인한 다음 한도가 없거나 10을 초과하는 쿼리를 거부합니다.- 규칙 세트에서 코드 중복을 피하기 위해
authorOrPublished()
함수를 정의합니다.
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{storyid} {
// Returns `true` if the requested story is 'published'
// or the user authored the story
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
// Deny any query not limited to 10 or fewer documents
// Anyone can query published stories
// Authors can query their unpublished stories
allow list: if request.query.limit <= 10 &&
authorOrPublished();
// Anyone can retrieve a published story
// Only a story's author can retrieve an unpublished story
allow get: if authorOrPublished();
// Only a story's author can write to a story
allow write: if request.auth.uid == resource.data.author;
}
}
}
컬렉션 그룹 쿼리 및 보안 규칙
기본적으로 쿼리는 단일 컬렉션으로 범위가 지정되며 해당 컬렉션에서만 결과를 검색합니다. 컬렉션 그룹 쿼리를 사용하면 ID가 동일한 모든 컬렉션으로 구성된 컬렉션 그룹에서 결과를 검색할 수 있습니다. 이 섹션에서는 보안 규칙을 사용하여 컬렉션 그룹 쿼리를 보호하는 방법을 설명합니다.
컬렉션 그룹을 기반으로 문서 보호 및 쿼리
보안 규칙에서 컬렉션 그룹에 대한 규칙을 작성하여 컬렉션 그룹 쿼리를 명시적으로 허용해야 합니다.
- 규칙 세트의 첫 번째 줄에
rules_version = '2';
가 있는지 확인합니다. 컬렉션 그룹 쿼리에는 보안 규칙 버전 2의 새로운 재귀 와일드 카드{name=**}
동작이 필요합니다. match /{path=**}/[COLLECTION_ID]/{doc}
를 사용하여 컬렉션 그룹의 규칙을 작성합니다.
예를 들어 posts
하위 컬렉션을 포함하는 forum
문서로 구성된 포럼을 살펴보겠습니다.
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
}
이 애플리케이션에서는 소유자가 글을 편집하고 인증된 사용자가 글을 읽을 수 있도록 합니다.
service cloud.firestore {
match /databases/{database}/documents {
match /forums/{forumid}/posts/{post} {
// Only authenticated users can read
allow read: if request.auth != null;
// Only the post author can write
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
모든 인증된 사용자는 단일 포럼의 글을 검색할 수 있습니다.
db.collection("forums/technology/posts").get()
그런데 현재 사용자에게 모든 포럼에 있는 자신의 글을 표시하려면 어떻게 해야 할까요?
컬렉션 그룹 쿼리를 사용하여 모든 posts
컬렉션에서 결과를 검색할 수 있습니다.
var user = firebase.auth().currentUser;
db.collectionGroup("posts").where("author", "==", user.uid).get()
보안 규칙에서 posts
컬렉션 그룹에 대한 read 또는 list 규칙을 작성하여 이 쿼리를 허용해야 합니다.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Authenticated users can query the posts collection group
// Applies to collection queries, collection group queries, and
// single document retrievals
match /{path=**}/posts/{post} {
allow read: if request.auth != null;
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth != null && request.auth.uid == resource.data.author;
}
}
}
그러나 이러한 규칙은 계층 구조와 상관없이 ID가 posts
인 모든 컬렉션에 적용됩니다. 예를 들어 이러한 규칙은 다음과 같은 posts
컬렉션 모두에 적용됩니다.
/posts/{postid}
/forums/{forumid}/posts/{postid}
/forums/{forumid}/subforum/{subforumid}/posts/{postid}
필드를 기반으로 컬렉션 그룹 쿼리 보호
단일 컬렉션 쿼리와 마찬가지로 컬렉션 그룹 쿼리도 보안 규칙에 의해 설정된 제약조건을 충족해야 합니다. 예를 들어 위의 stories
예시에서 했던 것처럼 published
필드를 각 포럼 글에 추가할 수 있습니다.
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
published: false
}
그런 다음 published
상태와 글 author
를 기반으로 posts
컬렉션 그룹에 대한 규칙을 작성할 수 있습니다.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Returns `true` if the requested post is 'published'
// or the user authored the post
function authorOrPublished() {
return resource.data.published == true || request.auth.uid == resource.data.author;
}
match /{path=**}/posts/{post} {
// Anyone can query published posts
// Authors can query their unpublished posts
allow list: if authorOrPublished();
// Anyone can retrieve a published post
// Authors can retrieve an unpublished post
allow get: if authorOrPublished();
}
match /forums/{forumid}/posts/{postid} {
// Only a post's author can write to a post
allow write: if request.auth.uid == resource.data.author;
}
}
}
이러한 규칙을 사용하면 웹, Apple, Android 클라이언트에서 다음 쿼리를 수행할 수 있습니다.
누구나 포럼에서 게시된 글을 검색할 수 있습니다.
db.collection("forums/technology/posts").where('published', '==', true).get()
누구나 모든 포럼에서 저자의 게시된 글을 검색할 수 있습니다.
db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
저자는 모든 포럼에서 게시된 글과 게시되지 않은 글을 모두 검색할 수 있습니다.
var user = firebase.auth().currentUser; db.collectionGroup("posts").where("author", "==", user.uid).get()
컬렉션 그룹 및 문서 경로를 기반으로 문서 보호 및 쿼리
경우에 따라 문서 경로를 기반으로 컬렉션 그룹 쿼리를 제한할 수 있습니다. 이러한 제한사항을 만들려면 필드를 기반으로 문서를 보호하고 쿼리할 때 동일한 기법을 사용하면 됩니다.
여러 주식 및 암호화폐 교환 중에 각 사용자의 트랜잭션을 추적하는 애플리케이션을 살펴보겠습니다.
/users/{userid}/exchange/{exchangeid}/transactions/{transaction}
{
amount: 100,
exchange: 'some_exchange_name',
timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
user: "some_auth_id",
}
user
필드를 확인합니다. 문서의 경로를 보면 어떤 사용자가 transaction
문서를 소유하는지 알 수 있지만 다음과 같은 두 가지 작업을 수행할 수 있도록 각 transaction
문서에 이 정보가 복제됩니다.
문서 경로에 특정
/users/{userid}
가 포함된 문서로 제한된 컬렉션 그룹 쿼리를 작성합니다. 예를 들면 다음과 같습니다.var user = firebase.auth().currentUser; // Return current user's last five transactions across all exchanges db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)
한 사용자가 다른 사용자의
transaction
문서를 검색할 수 없도록transactions
컬렉션 그룹의 모든 쿼리에 이 제한사항을 적용합니다.
이러한 제한사항을 보안 규칙에 적용하고 user
필드에 대한 데이터 유효성 검사를 포함합니다.
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{path=**}/transactions/{transaction} {
// Authenticated users can retrieve only their own transactions
allow read: if resource.data.user == request.auth.uid;
}
match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
// Authenticated users can write to their own transactions subcollections
// Writes must populate the user field with the correct auth id
allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
}
}
}
다음 단계
- 역할 기반 액세스 제어의 자세한 예는 사용자 및 그룹에 대한 데이터 액세스 보안을 참조하세요.
- 보안 규칙 참조 읽어보기