Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

실시간 데이터베이스 규칙에서 조건 사용

이 설명서는이 기반으로 핵심 중포 기지 보안 규칙 언어 학습 하여 중포 기지 실시간 데이터베이스 보안 규칙에 조건을 추가하는 방법을 보여 가이드.

실시간 데이터베이스 보안 규칙의 기본 빌딩 블록은 조건이다. 조건은 특정 작업을 허용할지 거부할지 여부를 결정하는 부울 식입니다. 기본 규칙의 사용 truefalse 조건으로 리터럴은 prefectly 잘 작동합니다. 그러나 실시간 데이터베이스 보안 규칙 언어는 다음과 같은 보다 복잡한 조건을 작성할 수 있는 방법을 제공합니다.

  • 사용자 인증 확인
  • 새로 제출된 데이터에 대해 기존 데이터 평가
  • 데이터베이스의 다른 부분에 액세스 및 비교
  • 수신 데이터 검증
  • 보안 논리를 위해 들어오는 쿼리의 구조를 사용합니다.

$ 변수를 사용하여 경로 세그먼트 캡처

당신은 함께 캡처 변수를 선언하여 읽기 또는 쓰기에 대한 경로의 일부를 캡처 할 수 있습니다 $ 접두사. 이것은 와일드 카드 역할을 하며 규칙 조건 내에서 사용하기 위해 해당 키의 값을 저장합니다.

{
  "rules": {
    "rooms": {
      // this rule applies to any child of /rooms/, the key for each room id
      // is stored inside $room_id variable for reference
      "$room_id": {
        "topic": {
          // the room's topic can be changed if the room id has "public" in it
          ".write": "$room_id.contains('public')"
        }
      }
    }
  }
}

동적의 $ 변수는 일정 경로 이름과 병행하여 사용할 수있다. 이 예에서는 사용하고 $other 선언하는 변수를 .validate 규칙을 보장하는 widget 아닌 다른 아이가없는 titlecolor . 추가 자식이 생성되는 쓰기 작업은 실패합니다.

{
  "rules": {
    "widget": {
      // a widget can have a title or color attribute
      "title": { ".validate": true },
      "color": { ".validate": true },

      // but no other child paths are allowed
      // in this case, $other means any key excluding "title" and "color"
      "$other": { ".validate": false }
    }
  }
}

입증

가장 일반적인 보안 규칙 패턴 중 하나는 사용자의 인증 상태를 기반으로 액세스를 제어하는 ​​것입니다. 예를 들어 앱에서 로그인한 사용자만 데이터를 쓸 수 있도록 허용할 수 있습니다.

앱이 중포 기지 인증을 사용하는 경우, request.auth 변수는 데이터를 요청하는 클라이언트에 대한 인증 정보가 포함되어 있습니다. 에 대한 자세한 내용은 request.auth 참조 참조 문서를 .

Firebase 인증은 Firebase 실시간 데이터베이스와 통합되어 조건을 사용하여 사용자별로 데이터 액세스를 제어할 수 있습니다. 사용자를 인증되면 auth 사용자의 실시간 데이터베이스 보안 규칙 규칙 변수는 사용자의 정보로 채워집니다. 이 정보는 고유 식별자 년 ( uid )뿐만 아니라 페이스 북 ID 또는 이메일 주소 및 기타 정보 등의 연결된 계정의 데이터를. 사용자 지정 인증 공급자를 구현하는 경우 사용자의 인증 페이로드에 고유한 필드를 추가할 수 있습니다.

이 섹션에서는 Firebase 실시간 데이터베이스 보안 규칙 언어를 사용자에 대한 인증 정보와 결합하는 방법을 설명합니다. 이 두 개념을 결합하여 사용자 ID를 기반으로 데이터에 대한 액세스를 제어할 수 있습니다.

auth 변수

미리 정의 된 auth 인증이 발생하기 전에 규칙의 변수는 null입니다.

사용자가로 인증되면 중포 기지 인증 은 다음과 같은 속성이 포함됩니다 :

공급자 사용된 인증 방법("비밀번호", "익명", "facebook", "github", "google" 또는 "twitter")입니다.
아이디 모든 공급자에서 고유한 것으로 보장되는 고유한 사용자 ID입니다.
토큰 Firebase 인증 ID 토큰의 콘텐츠입니다. 에 대한 참조 문서를 참조하십시오 auth.token 자세한 내용은.

여기서 사용하는 예시적인 규칙 인 auth 각 사용자는 단지 사용자가 특정 경로에 쓸 수 있도록 변수 :

{
  "rules": {
    "users": {
      "$user_id": {
        // grants write access to the owner of this user account
        // whose uid must exactly match the key ($user_id)
        ".write": "$user_id === auth.uid"
      }
    }
  }
}

인증 조건을 지원하도록 데이터베이스 구조화

일반적으로 규칙을 더 쉽게 작성할 수 있는 방식으로 데이터베이스를 구조화하는 것이 도움이 됩니다. 실시간 데이터베이스에서 사용자 데이터를 저장하기위한 하나 개의 일반적인 패턴은 하나에 모든 사용자를 저장하는 users 그 자녀들에게 있습니다 노드 uid 모든 사용자에 대한 값. 로그인한 사용자만 자신의 데이터를 볼 수 있도록 이 데이터에 대한 액세스를 제한하려는 경우 규칙은 다음과 같습니다.

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid"
      }
    }
  }
}

인증 사용자 지정 클레임 작업

서로 다른 사용자에 대한 사용자 액세스 제어를 필요로하는 앱의 경우 중포 기지 인증에 개발자 수 중포 기지 사용자에 대한 일련의 주장을 . 이러한 주장은에 accesible입니다 auth.token 규칙의 변수입니다. 여기에 사용하게 규칙의 예입니다 hasEmergencyTowel 사용자 지정 클레임은 :

{
  "rules": {
    "frood": {
      // A towel is about the most massively useful thing an interstellar
      // hitchhiker can have
      ".read": "auth.token.hasEmergencyTowel === true"
    }
  }
}

만드는 개발자 자신의 사용자 정의 인증 토큰은 선택적으로 이러한 토큰에 대한 주장을 추가 할 수 있습니다. 이러한 주장은에 있음 있습니다 auth.token 규칙의 변수입니다.

기존 데이터와 새로운 데이터

미리 정의 된 data 변수가 일어나는 기록 동작 전에 데이터를 참조하기 위해 사용된다. 반대로, newData 변수는 쓰기 작업이 성공하면 존재합니다 새로운 데이터가 포함되어 있습니다. newData 새로운 데이터가 기록 및 기존 데이터되는 병합 된 결과를 나타냅니다.

설명을 위해 이 규칙을 사용하면 새 레코드를 만들거나 기존 레코드를 삭제할 수 있지만 null이 아닌 기존 데이터를 변경할 수는 없습니다.

// we can write as long as old data or new data does not exist
// in other words, if this is a delete or a create, but not an update
".write": "!data.exists() || !newData.exists()"

다른 경로의 데이터 참조

모든 데이터를 규칙의 기준으로 사용할 수 있습니다. 미리 정의 된 변수의 사용 root , datanewData , 우리는 이전 또는 쓰기 이벤트 이후에 존재하는 것처럼 어떤 경로를 액세스 할 수 있습니다.

긴으로 쓰기 작업을 할 수 있습니다이 예제를 고려의 값으로 /allow_writes/ 노드가 true 하는이없는 부모 노드 readOnly 플래그가 설정 및 명명 된 아이가 foo 새로 기록 된 데이터가 :

".write": "root.child('allow_writes').val() === true &&
          !data.parent().child('readOnly').exists() &&
          newData.child('foo').exists()"

데이터 검증

데이터 구조를 시행하고 형식 및 사용하여 수행해야 데이터의 내용의 유효성을 검사 .validate 후에 만 실행 규칙을 .write 규칙 액세스 권한을 부여 성공을. 아래는 샘플입니다 .validate 만 정규 표현식을 사용하여 선택되어 년 1,900에서 2,099 사이 사이의 형식 YYYY-MM-DD에 날짜를 허용 규칙 정의는.

".validate": "newData.isString() &&
              newData.val().matches(/^(19|20)[0-9][0-9][-\\/. ](0[1-9]|1[012])[-\\/. ](0[1-9]|[12][0-9]|3[01])$/)"

.validate 규칙은 캐스케이드을하지 않는 보안 규칙의 유일한 유형입니다. 하위 레코드에서 유효성 검사 규칙이 실패하면 전체 쓰기 작업이 거부됩니다. 데이터가 삭제 될 때 또한, 유효성 검사 정의 (즉, 새로운 값이 기록되는 경우입니다 무시됩니다 null ).

이는 사소해 보이지만 사실 강력한 Firebase 실시간 데이터베이스 보안 규칙을 작성하기 위한 중요한 기능입니다. 다음 규칙을 고려하십시오.

{
  "rules": {
    // write is allowed for all paths
    ".write": true,
    "widget": {
      // a valid widget must have attributes "color" and "size"
      // allows deleting widgets (since .validate is not applied to delete rules)
      ".validate": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99
        ".validate": "newData.isNumber() &&
                      newData.val() >= 0 &&
                      newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical
        // /valid_colors/ index
        ".validate": "root.child('valid_colors/' + newData.val()).exists()"
      }
    }
  }
}

이 변형을 염두에 두고 다음 쓰기 작업의 결과를 살펴보세요.

자바스크립트
var ref = db.ref("/widget");

// PERMISSION_DENIED: does not have children color and size
ref.set('foo');

// PERMISSION DENIED: does not have child color
ref.set({size: 22});

// PERMISSION_DENIED: size is not a number
ref.set({ size: 'foo', color: 'red' });

// SUCCESS (assuming 'blue' appears in our colors list)
ref.set({ size: 21, color: 'blue'});

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child('size').set(99);
오브젝티브-C
FIRDatabaseReference *ref = [[[FIRDatabase database] reference] child: @"widget"];

// PERMISSION_DENIED: does not have children color and size
[ref setValue: @"foo"];

// PERMISSION DENIED: does not have child color
[ref setValue: @{ @"size": @"foo" }];

// PERMISSION_DENIED: size is not a number
[ref setValue: @{ @"size": @"foo", @"color": @"red" }];

// SUCCESS (assuming 'blue' appears in our colors list)
[ref setValue: @{ @"size": @21, @"color": @"blue" }];

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
[[ref child:@"size"] setValue: @99];
빠른
var ref = FIRDatabase.database().reference().child("widget")

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo")

// PERMISSION DENIED: does not have child color
ref.setValue(["size": "foo"])

// PERMISSION_DENIED: size is not a number
ref.setValue(["size": "foo", "color": "red"])

// SUCCESS (assuming 'blue' appears in our colors list)
ref.setValue(["size": 21, "color": "blue"])

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
자바
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("widget");

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo");

// PERMISSION DENIED: does not have child color
ref.child("size").setValue(22);

// PERMISSION_DENIED: size is not a number
Map<String,Object> map = new HashMap<String, Object>();
map.put("size","foo");
map.put("color","red");
ref.setValue(map);

// SUCCESS (assuming 'blue' appears in our colors list)
map = new HashMap<String, Object>();
map.put("size", 21);
map.put("color","blue");
ref.setValue(map);

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
쉬다
# PERMISSION_DENIED: does not have children color and size
curl -X PUT -d 'foo' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION DENIED: does not have child color
curl -X PUT -d '{"size": 22}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION_DENIED: size is not a number
curl -X PUT -d '{"size": "foo", "color": "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# SUCCESS (assuming 'blue' appears in our colors list)
curl -X PUT -d '{"size": 21, "color": "blue"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# If the record already exists and has a color, this will
# succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
# will fail to validate
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

이제 동일한 구조에서의 모습을 보자,하지만 사용 .write 대신 규칙 .validate :

{
  "rules": {
    // this variant will NOT allow deleting records (since .write would be disallowed)
    "widget": {
      // a widget must have 'color' and 'size' in order to be written to this path
      ".write": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99, ONLY IF WE WRITE DIRECTLY TO SIZE
        ".write": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical valid_colors/ index
        // BUT ONLY IF WE WRITE DIRECTLY TO COLOR
        ".write": "root.child('valid_colors/'+newData.val()).exists()"
      }
    }
  }
}

이 변형에서는 다음 작업 중 하나가 성공합니다.

자바스크립트
var ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.set({size: 99999, color: 'red'});

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child('size').set(99);
오브젝티브-C
Firebase *ref = [[Firebase alloc] initWithUrl:URL];

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
[ref setValue: @{ @"size": @9999, @"color": @"red" }];

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
[[ref childByAppendingPath:@"size"] setValue: @99];
빠른
var ref = Firebase(url:URL)

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.setValue(["size": 9999, "color": "red"])

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.childByAppendingPath("size").setValue(99)
자바
Firebase ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
Map<String,Object> map = new HashMap<String, Object>();
map.put("size", 99999);
map.put("color", "red");
ref.setValue(map);

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child("size").setValue(99);
쉬다
# ALLOWED? Even though size is invalid, widget has children color and size,
# so write is allowed and the .write rule under color is ignored
curl -X PUT -d '{size: 99999, color: "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# ALLOWED? Works even if widget does not exist, allowing us to create a widget
# which is invalid and does not have a valid color.
# (allowed by the write rule under "color")
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

이 사이의 차이점 설명 .write.validate 규칙을. 알 수 있듯이,이 규칙은 모든 이용하여 기록되어야 .validate 의 가능한 예외로 newData.hasChildren() 결실을 허용할지 여부에 달려 규칙.

쿼리 기반 규칙

당신이 비록 필터로 규칙을 사용할 수 없습니다 , 당신은 당신의 규칙에 쿼리 매개 변수를 사용하여 데이터의 하위 집합에 대한 액세스를 제한 할 수 있습니다. 사용 query. 쿼리 매개변수를 기반으로 읽기 또는 쓰기 액세스 권한을 부여하는 표현식.

예를 들어, 다음 쿼리 기반 규칙을 사용하여 사용자 기반 보안 규칙 의 데이터에 대한 액세스 제한 및 쿼리 기반 규칙을 baskets 활성 사용자가 소유 한 전용 쇼핑 바구니에 수집 :

"baskets": {
  ".read": "auth.uid != null &&
            query.orderByChild == 'owner' &&
            query.equalTo == auth.uid" // restrict basket access to owner of basket
}

규칙에 쿼리 매개변수를 포함하는 다음 쿼리는 성공합니다.

db.ref("baskets").orderByChild("owner")
                 .equalTo(auth.currentUser.uid)
                 .on("value", cb)                 // Would succeed

그러나, 규칙의 매개 변수를 포함하지 않는 쿼리는 실패 할 PermissionDenied 오류 :

db.ref("baskets").on("value", cb)                 // Would fail with PermissionDenied

쿼리 기반 규칙을 사용하여 클라이언트가 읽기 작업을 통해 다운로드하는 데이터의 양을 제한할 수도 있습니다.

예를 들어 다음 규칙은 우선 순위에 따라 쿼리의 처음 1000개 결과에 대해서만 읽기 액세스를 제한합니다.

messages: {
  ".read": "query.orderByKey &&
            query.limitToFirst <= 1000"
}

// Example queries:

db.ref("messages").on("value", cb)                // Would fail with PermissionDenied

db.ref("messages").limitToFirst(1000)
                  .on("value", cb)                // Would succeed (default order by key)

다음 query. 표현식은 실시간 데이터베이스 보안 규칙에서 사용할 수 있습니다.

쿼리 기반 규칙 표현식
표현 유형 설명
query.orderByKey
query.orderByPriority
query.orderByValue
부울 키, 우선 순위 또는 값으로 정렬된 쿼리의 경우 True입니다. 그렇지 않으면 거짓입니다.
query.orderByChild
없는
문자열을 사용하여 자식 노드에 대한 상대 경로를 나타냅니다. 예를 들어, query.orderByChild == "address/zip" . 쿼리가 자식 노드에 의해 정렬되지 않은 경우 이 값은 null입니다.
query.startAt
쿼리.endAt
query.equalTo

숫자
부울
없는
실행 중인 쿼리의 경계를 검색하거나 경계 집합이 없으면 null을 반환합니다.
query.limitToFirst
query.limitToLast
숫자
없는
실행 쿼리에 대한 제한을 검색하거나 제한이 설정되지 않은 경우 null을 반환합니다.

다음 단계

이러한 조건에 대한 논의를 마친 후에는 규칙에 대해 보다 정교하게 이해하게 되었으며 다음을 수행할 준비가 되었습니다.

핵심 사용 사례를 처리하는 방법과 규칙 개발, 테스트 및 배포를 위한 워크플로를 알아보세요.

Realtime Database와 관련된 학습 규칙 기능: