이 페이지는 Cloud Translation API를 통해 번역되었습니다.
Switch to English

실시간 데이터베이스 규칙 언어의 핵심 구문 알아보기

Firebase 실시간 데이터베이스 보안 규칙을 사용하면 데이터베이스에 저장된 데이터에 대한 액세스를 제어 할 수 있습니다. 유연한 규칙 구문을 사용하면 데이터베이스에 대한 모든 쓰기에서 개별 노드의 작업에 이르기까지 모든 항목과 일치하는 규칙을 만들 수 있습니다.

실시간 데이터베이스 보안 규칙은 데이터베이스에 대한 선언적 구성입니다. 이는 규칙이 제품 로직과 별도로 정의됨을 의미합니다. 여기에는 여러 가지 장점이 있습니다. 클라이언트는 보안을 시행 할 책임이없고 버그가있는 구현은 데이터를 손상시키지 않으며 아마도 가장 중요한 것은 서버와 같은 중간 심판이 세상으로부터 데이터를 보호 할 필요가 없다는 것입니다.

이 항목에서는 전체 규칙 집합을 만드는 데 사용되는 기본 구문 및 구조 실시간 데이터베이스 보안 규칙에 대해 설명합니다.

보안 규칙 구조화

실시간 데이터베이스 보안 규칙은 JSON 문서에 포함 된 JavaScript와 유사한 표현식으로 구성됩니다. 규칙의 구조는 데이터베이스에 저장 한 데이터의 구조를 따라야합니다.

기본 규칙 보호 할 노드 집합 , 관련된 액세스 방법 (예 : 읽기, 쓰기) 및 액세스가 허용되거나 거부되는 조건 을 식별합니다. 다음 예에서 조건 은 단순한 truefalse 진술이지만 다음 주제에서는 조건을 표현하는보다 동적 인 방법을 다룰 것입니다.

예를 들어 child_node 아래에서 parent_node 를 보호하려는 경우 따라야 할 일반 구문은 다음과 같습니다.

{
  "rules": {
    "parent_node": {
      "child_node": {
        ".read": <condition>,
        ".write": <condition>,
        ".validate": <condition>,
      }
    }
  }
}

이 패턴을 적용 해 봅시다. 예를 들어 메시지 목록을 추적하고 있고 다음과 같은 데이터가 있다고 가정 해 보겠습니다.

{
  "messages": {
    "message0": {
      "content": "Hello",
      "timestamp": 1405704370369
    },
    "message1": {
      "content": "Goodbye",
      "timestamp": 1405704395231
    },
    ...
  }
}

규칙은 유사한 방식으로 구성되어야합니다. 다음은이 데이터 구조에 적합한 읽기 전용 보안 규칙 집합입니다. 이 예는 규칙이 적용되는 데이터베이스 노드와 해당 노드에서 규칙을 평가하기위한 조건을 지정하는 방법을 보여줍니다.

{
  "rules": {
    // For requests to access the 'messages' node...
    "messages": {
      // ...and the individual wildcarded 'message' nodes beneath
      // (we'll cover wildcarding variables more a bit later)....
      "$message": {

        // For each message, allow a read operation if <condition>. In this
        // case, we specify our condition as "true", so read access is always granted.
        ".read": "true",

        // For read-only behavior, we specify that for write operations, our
        // condition is false.
        ".write": "false"
      }
    }
  }
}

기본 규칙 작업

데이터에 대해 수행되는 작업 유형에 따라 보안을 적용하는 규칙에는 .write , .read.validate 세 가지 유형이 있습니다. 다음은 그 목적에 대한 간략한 요약입니다.

규칙 유형
.읽다 사용자가 데이터를 읽을 수 있는지 여부와시기를 설명합니다.
.쓰다 데이터 쓰기 허용 여부와시기를 설명합니다.
.확인 올바른 형식의 값, 하위 속성이 있는지 여부 및 데이터 유형을 정의합니다.

와일드 카드 캡처 변수

모든 규칙 문은 노드를 가리 킵니다. 문은 특정 노드를 가리 키거나 $ 와일드 카드 캡처 변수 를 사용하여 계층 수준의 노드 집합을 가리킬 수 있습니다. 이러한 캡처 변수를 사용하여 후속 규칙 문 내에서 사용할 노드 키 값을 저장합니다. 이 기술을 사용하면 더 복잡한 규칙 조건 을 작성할 수 있습니다. 다음 항목에서 자세히 다룰 것입니다.

{
  "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 변수를 사용하여 widget titlecolor 이외의 자식이 없도록하는 .validate 규칙을 선언합니다. 추가 자식이 생성되는 모든 쓰기는 실패합니다.

{
  "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 }
    }
  }
}

읽기 및 쓰기 규칙 캐스케이드

.read.write 규칙은 하향식에서 작동하며 더 얕은 규칙은 더 깊은 규칙을 재정의합니다. 규칙이 특정 경로에서 읽기 또는 쓰기 권한을 부여하면 그 아래의 모든 하위 노드에 대한 액세스 권한도 부여 됩니다. 다음 구조를 고려하십시오.

{
  "rules": {
     "foo": {
        // allows read to /foo/*
        ".read": "data.child('baz').val() === true",
        "bar": {
          /* ignored, since read was allowed already */
          ".read": false
        }
     }
  }
}

이 보안 구조를 통해 /bar/ /foo/true 값을 가진 자식 baz 가 포함될 때마다 /bar/ 를 읽을 수 있습니다. /foo/bar/ 아래의 ".read": false 규칙은 하위 경로에 의해 액세스를 취소 할 수 없기 때문에 여기에서 효과가 없습니다.

즉시 직관적으로 보이지는 않지만 규칙 언어의 강력한 부분이며 최소한의 노력으로 매우 복잡한 액세스 권한을 구현할 수 있습니다. 이는이 가이드의 뒷부분에서 사용자 기반 보안에 대해 설명 할 때 설명됩니다.

.validate 규칙은 계단식으로 적용되지 않습니다. 쓰기가 허용 되려면 모든 유효성 검사 규칙이 계층의 모든 수준에서 충족되어야합니다.

규칙은 필터가 아닙니다

규칙은 원자 적 방식으로 적용됩니다. 즉, 해당 위치 또는 액세스를 허용하는 상위 위치에 규칙이없는 경우 읽기 또는 쓰기 작업이 즉시 실패합니다. 영향을받는 모든 자식 경로에 액세스 할 수 있어도 부모 위치에서 읽기가 완전히 실패합니다. 다음 구조를 고려하십시오.

{
  "rules": {
    "records": {
      "rec1": {
        ".read": true
      },
      "rec2": {
        ".read": false
      }
    }
  }
}

규칙이 원자 적으로 평가된다는 것을 이해하지 못하면 /records/ 경로를 가져 오면 rec1 되지만 rec2 는 반환되지 않는 것처럼 보일 수 있습니다. 그러나 실제 결과는 오류입니다.

자바 스크립트
var db = firebase.database();
db.ref("records").once("value", function(snap) {
  // success method is not called
}, function(err) {
  // error callback triggered with PERMISSION_DENIED
});
목표 -C
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
  // success block is not called
} withCancelBlock:^(NSError * _Nonnull error) {
  // cancel block triggered with PERMISSION_DENIED
}];
빠른
var ref = FIRDatabase.database().reference()
ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // success block is not called
}, withCancelBlock: { error in
    // cancel block triggered with PERMISSION_DENIED
})
자바
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // success method is not called
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback triggered with PERMISSION_DENIED
  });
});
쉬다
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

/records/ 에서의 읽기 작업은 원자 적이며 /records/ 아래의 모든 데이터에 대한 액세스 권한을 부여하는 읽기 규칙이 없기 때문에 PERMISSION_DENIED 오류가 발생합니다. Firebase 콘솔 의 보안 시뮬레이터에서이 규칙을 평가하면 /records/ 경로에 대한 액세스를 허용하는 읽기 규칙이 없기 때문에 읽기 작업이 거부되었음을 알 수 있습니다. 그러나 rec1 에 대한 규칙은 우리가 요청한 경로에 없기 때문에 평가되지 않았습니다. rec1 을 가져 오려면 직접 액세스해야합니다.

자바 스크립트
var db = firebase.database();
db.ref("records/rec1").once("value", function(snap) {
  // SUCCESS!
}, function(err) {
  // error callback is not called
});
목표 -C
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
빠른
var ref = FIRDatabase.database().reference()
ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // SUCCESS!
})
자바
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("records/rec1");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    // SUCCESS!
  }

  @Override
  public void onCancelled(FirebaseError firebaseError) {
    // error callback is not called
  }
});
쉬다
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

겹치는 문

노드에 둘 이상의 규칙을 적용 할 수 있습니다. 여러 규칙 표현식이 노드를 식별하는 경우 조건 하나 라도 false 이면 액세스 방법이 거부됩니다.

{
  "rules": {
    "messages": {
      // A rule expression that applies to all nodes in the 'messages' node
      "$message": {
        ".read": "true",
        ".write": "true"
      },
      // A second rule expression applying specifically to the 'message1` node
      "message1": {
        ".read": "false",
        ".write": "false"
      }
    }
  }
}

위의 예에서 첫 번째 규칙이 항상 true 인 경우에도 두 번째 규칙이 항상 false 이므로 message1 노드에 대한 읽기가 거부됩니다.

다음 단계

Firebase 실시간 데이터베이스 보안 규칙에 대해 더 깊이 이해할 수 있습니다.

  • 규칙이 사용자 인증을 확인하고, 기존 데이터와 수신 데이터를 비교하고, 수신 데이터의 유효성을 검사하고, 클라이언트에서 오는 쿼리의 구조를 확인할 수있는 규칙 언어의 다음 주요 개념 인 동적 조건 에 대해 알아 봅니다.

  • 일반적인 보안 사용 사례와이를 해결하는 Firebase 보안 규칙 정의를 검토 합니다 .