Cloud Firestore(베타) 공개: Firebase와 Google Cloud Platform의 새롭고 유연하며 확장 가능한 데이터베이스를 사용해 보세요. Cloud Firestore 자세히 알아보기

사용자 기반 보안

이 문서에서는 데이터 보안의 개념을 복습하면서 사전 정의된 auth 변수를 통합하여 완벽한 데이터 보안 솔루션을 만들어 봅니다.

인증 통합

Firebase 인증은 Firebase 실시간 데이터베이스와 통합되어 사용자별 데이터 액세스를 제어하는 기능을 제공합니다.

사용자가 인증되면 Firebase 데이터베이스 규칙의 auth 변수에 사용자의 정보가 입력됩니다. 이 정보에는 고유 식별자(uid), 연결된 계정 데이터(Facebook ID 또는 이메일 주소) 및 기타 정보가 포함됩니다. 맞춤 인증 제공업체를 구현하는 경우 사용자의 인증 페이로드에 필드를 직접 추가할 수 있습니다.

이 가이드에서는 Firebase 실시간 데이터베이스 규칙 언어와 사용자의 인증 정보를 결합하는 방법을 설명합니다. 이러한 두 개념을 결합하여 사용자 ID에 따라 데이터 액세스를 제어할 수 있습니다.

auth 변수

규칙에서 사전 정의된 auth 변수는 인증이 완료되기 전에는 null입니다. Firebase 인증을 통해 사용자가 인증되면 이 변수에 다음 속성이 포함됩니다.

provider 사용된 인증 방법('password', 'anonymous', 'facebook", 'github', 'google', 또는 'twitter')
uid 고유 사용자 ID(제공업체에 관계없이 항상 고유함)
token 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"
      }
    }
  }
}

맞춤 인증 토큰으로 작업

더욱 자세한 제어가 필요한 개발자를 위해 Firebase 인증은 서버에 맞춤 인증 토큰을 만드는 기능을 제공합니다.

개발자는 맞춤 인증 토큰을 만들 때 추가적인 클레임을 토큰에 추가할 수 있습니다. 규칙의 auth.token 변수에 이러한 추가 클레임이 배치됩니다. 다음은 hasEmergencyTowel 맞춤 클레임을 활용하는 규칙의 예입니다.

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

또한 Admin SDK를 사용하면 액세스 제어를 위한 사용자 역할의 정의에 도움이 되는 맞춤 토큰 클레임을 설정할 수 있습니다. 맞춤 클레임 및 보안 규칙을 통한 액세스 제어를 참조하세요.

채팅 예제 복습

데이터 보안의 채팅 예제를 기반으로 사용자 인증을 추가하여 이러한 개념을 모두 결합한 실제 앱을 만들어 보겠습니다.

{
  "rules": {
    "room_names": {
      // any logged in user can get a list of room names
      ".read": "auth !== null",

      "$room_id": {
        // this is just for documenting the structure of rooms, since
        // they are read-only and no write rule allows this to be set
        ".validate": "newData.isString()"
      }
    },

    "members": {
       // I can join or leave any room (otherwise it would be a boring demo)
       // I can have a different name in each room just for fun
       "$room_id": {
          // any member can read the list of member names
          ".read": "data.child(auth.uid).exists()",

          // room must already exist to add a member
          ".validate": "root.child('room_names/'+$room_id).exists()",

          "$user_id": {
             ".write": "auth.uid === $user_id",
             ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length < 20"
          }
       }
    },

    "messages": {
      "$room_id": {
        // the list of messages for a room can be read by any member
        ".read": "root.child('members/'+$room_id+'/'+auth.uid).exists()",

        // room we want to write a message to must be valid
        ".validate": "root.child('room_names/'+$room_id).exists()",

        "$message_id": {
          // a new message can be created if it does not exist, but it
          // cannot be modified or deleted
          // any member of a room can write a new message
          ".write": "root.child('members/'+$room_id+'/'+auth.uid).exists() && !data.exists() && newData.exists()",

          // the room attribute must be a valid key in room_names/ (the room must exist)
          // the object to write must have a name, message, and timestamp
          ".validate": "newData.hasChildren(['user', 'message', 'timestamp'])",

          // the message must be written by logged in user
          "user": {
             ".validate": "newData.val() === auth.uid"
          },

          // the message must be longer than 0 chars and less than 50
          "message": { ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length < 50" },

          // messages cannot be added in the past or the future
          // clients should use firebase.database.ServerValue.TIMESTAMP
          // to ensure accurate timestamps
          "timestamp": { ".validate": "newData.val() <= now" },

          // no other fields can be included in a message
          "$other": { ".validate": false }
        }
      }
    }
  }
}

다음 단계

다음에 대한 의견 보내기...

Firebase 실시간 데이터베이스
도움이 필요하시나요? 지원 페이지를 방문하세요.