데이터 검증

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

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

새 데이터에 대한 제한사항

특정 필드가 포함된 문서가 만들어지지 않도록 하려면 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')
    }
  }

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

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

Firebase Security Rules에서 기존 데이터 사용

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

  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;
      }
    }
  }

Realtime Database에서 .validate 규칙을 사용하여 데이터 구조를 적용하고 데이터의 형식과 콘텐츠를 확인합니다. .write 규칙의 액세스 허용을 확인한 후 Rules.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);
참고: 이 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];
참고: 이 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);
# 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

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

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

  service firebase.storage {
    match /b/{bucket}/o {
      match /images {
        match /{fileName} {
          // 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 {
        // 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) Filename (stored in imageId wildcard variable) is less than 32 characters
        match /{imageId} {
          allow read;
          allow write: if request.resource.size < 5 * 1024 * 1024
                       && request.resource.contentType.matches('image/.*')
                       && request.resource.contentType == resource.contentType
                       && imageId.size() < 32
        }
      }
    }
  }

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