Firebase Summit에서 발표된 모든 내용을 살펴보고 Firebase로 앱을 빠르게 개발하고 안심하고 앱을 실행하는 방법을 알아보세요. 자세히 알아보기

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

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

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 변수를 사용하여 widgettitlecolor 이외의 자식이 없도록 하는 .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
        }
     }
  }
}

이 보안 구조를 통해 /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
참고: 이 Firebase 제품은 App Clip 대상에서 사용할 수 없습니다.
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
}];
빠른
참고: 이 Firebase 제품은 App Clip 대상에서 사용할 수 없습니다.
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
참고: 이 Firebase 제품은 App Clip 대상에서 사용할 수 없습니다.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
빠른
참고: 이 Firebase 제품은 App Clip 대상에서 사용할 수 없습니다.
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 실시간 데이터베이스 보안 규칙에 대한 이해를 심화할 수 있습니다.

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

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