데이터 검증

Firebase 보안 규칙을 사용하면 데이터베이스 또는 스토리지 버킷의 기존 데이터를 기반으로 새 데이터를 조건부로 작성할 수 있습니다. 기록되는 새 데이터를 기반으로 쓰기를 제한하여 데이터 검증을 시행하는 규칙을 작성할 수도 있습니다. 기존 데이터를 사용하여 보안 조건을 생성하는 규칙에 대해 더 자세히 알아보려면 계속해서 읽어보시기 바랍니다.

데이터 검증 규칙에 대해 더 자세히 알아보려면 각 섹션에서 제품을 선택하세요.

새 데이터에 대한 제한사항

Cloud Firestore

특정 필드가 포함된 문서가 만들어지지 않도록 하려면 allow 조건에 이 필드를 포함하면 됩니다. 예를 들어 ranking 필드가 포함된 문서의 생성을 거부하려면 create 조건에서 이 필드를 허용하지 않습니다.

  service cloud.firestore {
    match /databases/{database}/documents {
      // Disallow
      match /cities/{city} {
        allow create: if !("ranking" in request.resource.data)
      }
    }
  }

실시간 데이터베이스

특정 값을 포함하는 데이터가 데이터베이스에 추가되지 않도록 하려면 해당 값을 규칙에 포함하고 쓰기를 허용하지 않습니다. 예를 들어 ranking 값이 포함된 쓰기를 거부하려면 ranking 값이 포함된 모든 문서에 쓰기를 허용하지 않습니다.

  {
    "rules": {
      // Write is allowed for all paths
      ".write": true,
      // Allows writes only if new data doesn't include a `ranking` child value
      ".validate": "!newData.hasChild('ranking')
    }
  }

Cloud Storage

특정 메타데이터가 포함된 파일이 만들어지지 않도록 하려면 이 메타데이터를 allow 조건에 포함하면 됩니다. 예를 들어 ranking 메타데이터가 포함된 파일의 생성을 거부하려면 create 조건에서 이 메타데이터를 허용하지 않습니다.

  service firebase.storage {
    match /b/{bucket}/o {
      match /files/{allFiles=**} {
      // Disallow
        allow create: if !("ranking" in request.resource.metadata)
      }
    }
  }

Firebase 보안 규칙에서 기존 데이터 사용

Cloud Firestore

액세스 정보를 데이터베이스의 문서에 필드로 저장하는 앱이 많습니다. Cloud Firestore 보안 규칙은 문서 데이터에 따라 동적으로 액세스를 허용하거나 거부할 수 있습니다.

  service cloud.firestore {
    match /databases/{database}/documents {
      // Allow the user to read data if the document has the 'visibility'
      // field set to 'public'
      match /cities/{city} {
        allow read: if resource.data.visibility == 'public';
      }
    }
  }

resource 변수는 요청된 문서를 가리키고 resource.data는 문서에 저장된 모든 필드와 값의 맵입니다. resource 변수에 관한 자세한 내용은 참조 문서를 확인하세요.

데이터를 기록할 때 수신 데이터를 기존 데이터와 비교해야 할 수 있습니다. 이를 통해 필드가 변경되지 않았는지, 필드가 하나씩만 증가했는지 또는 새 값이 앞으로 최소 일주일은 있는지 확인하는 등의 작업을 수행할 수 있습니다. 이러한 경우 규칙 세트에서 대기 중인 쓰기를 허용하면 request.resource 변수에 문서의 미래 상태가 포함됩니다. 문서 필드의 일부만 수정하는 update 작업의 경우 request.resource 변수는 작업 후 대기 중인 문서 상태를 포함합니다. request.resource의 필드 값을 확인하여 불필요하거나 일관성이 없는 데이터 업데이트를 방지할 수 있습니다.

   service cloud.firestore {
     match /databases/{database}/documents {
      // Make sure all cities have a positive population and
      // the name is not changed
      match /cities/{city} {
        allow update: if request.resource.data.population > 0
                      && request.resource.data.name == resource.data.name;
      }
    }
  }

실시간 데이터베이스

실시간 데이터베이스에서 .validate 규칙을 사용하여 데이터 구조를 적용하고 데이터의 형식과 콘텐츠를 확인합니다. .write 규칙의 액세스 허용을 확인한 후 .validate 규칙을 실행합니다.

.validate 규칙은 하위로 전파되지 않습니다. 규칙의 경로 또는 하위 경로에서 하나라도 검증 규칙이 실패하면 전체 쓰기 작업이 거부됩니다. 또한 검증 정의는 null이 아닌 값만 확인한 후 데이터를 삭제한다는 모든 요청을 무시합니다.

예를 들어 다음 .validate 규칙을 살펴보세요.

  {
    "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);
Objective-C
참고: 이 Firebase 제품은 앱 클립 대상에서는 사용할 수 없습니다.
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];
Swift
참고: 이 Firebase 제품은 앱 클립 대상에서는 사용할 수 없습니다.
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);
REST
# 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

Cloud Storage

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

resource 객체에는 Cloud Storage 객체에 표시된 파일 메타데이터와 함께 키-값 쌍이 포함되어 있습니다. read 또는 write 요청에서 이러한 속성을 검사하여 데이터 무결성을 확인할 수 있습니다. resource 객체는 Cloud Storage 버킷에 있는 기존 파일에서 메타데이터를 확인합니다.

  service firebase.storage {
    match /b/{bucket}/o {
      match /images {
        match /{allImages=**} {
          // Allow reads if a custom 'visibility' field is set to 'public'
          allow read: if resource.metadata.visibility == 'public';
        }
      }
    }
  }

write 요청(업로드, 메타데이터 업데이트, 삭제 등)에 request.resource 객체를 사용할 수도 있습니다. request.resource 객체는 write가 허용되는 경우 작성될 파일에서 메타데이터를 가져옵니다.

이 두 값을 사용하여 원하지 않거나 일치하지 않는 업데이트를 방지하거나 파일 형식, 크기 등의 애플리케이션 제한사항을 적용할 수 있습니다.

  service firebase.storage {
    match /b/{bucket}/o {
      match /images {
        // Cascade read to any image type at any path
        match /{allImages=**} {
          allow read;
        }

        // Allow write files to the path "images/*", subject to the constraints:
        // 1) File is less than 5MB
        // 2) Content type is an image
        // 3) Uploaded content type matches existing content type
        // 4) File name (stored in imageId wildcard variable) is less than 32 characters
        match /{imageId} {
          allow write: if request.resource.size < 5 * 1024 * 1024
                       && request.resource.contentType.matches('image/.*')
                       && request.resource.contentType == resource.contentType
                       && imageId.size() < 32
        }
      }
    }
  }

resource 객체의 전체 속성 목록은 참조 문서에서 제공됩니다.