programing

보안 규칙으로 하위/필드 액세스 제한

iphone6s 2023. 6. 16. 21:33
반응형

보안 규칙으로 하위/필드 액세스 제한

저는 사용자가 다른 사용자에게 표시되기 전에 조정된 추천을 제출할 수 있는 앱을 작성하고 있습니다.이를 위해서는 보안 규칙을 사용하여 지금까지 구현하지 못했던 여러 가지 제한 사항이 필요합니다.

  1. 아직 승인되지 않은 모든 지명 숨기기
  2. 제출에서 개인 필드 숨기기(전화, 승인 상태, 작성 날짜 등)

나의 현재 규칙은 다음과 같습니다.

{
    "rules": {
        "nominations": {
            ".read": true,

            "$nominationId": {
                ".read": "data.child('state').val() == 'approved' || auth != null", // Only read approved nominations if not authenticated
                ".write": "!data.exists()", // Only allow new nominations to be created

                "phone": {
                    ".read": "auth != null" // Only allow authenticated users to read phone number
                },

                "state": {
                    ".read": "auth != null", // Only allow authenticated users to read approval state
                    ".write": "auth != null" // Only allow authenticated users to change state
                }
            }
        }
    }
}

규칙 하위규예칙):$nomination전체 자식이 부모로부터 읽히는 것을 방지하지 않습니다.내가 듣는다면,child_addedhttps://my.firebaseio.com/nominations 에서 그것은 위의 보안 규칙이 있는 상태에서도 모든 어린이와 그들의 모든 데이터를 행복하게 반환합니다.

의 현재 는 이름의 별도의 노드를 입니다.approved그리고 누군가 지명을 승인하거나 거부할 때마다 목록 간에 데이터를 이동하기만 하면 되지만, 그것은 끔찍하게 깨진 접근법처럼 보입니다.

갱신하다

Michael Lehenbauer의 훌륭한 의견에 따라 저는 최소한의 노력으로 초기 아이디어를 다시 구현했습니다.

새 데이터 구조는 다음과 같습니다.

my-firebase
    |
    `- nominations
        |
        `- entries
        |   |
        |   `- private
        |   `- public
        |
        `- status
            |
            `- pending
            `- approved
            `- rejected

은 각지은다같저다니장됩이음과명에 됩니다.entries전화번호, 이메일 등과 같은 사적인 자료를 가지고 있습니다.아래private공개적으로 볼 수 있는 아래의 데이터public.

업데이트된 규칙은 다음과 같습니다.

{
    "rules": {
        "nominations": {
            "entries": {
                "$id": {
                    ".write": "!data.exists()",

                    "public": {
                        ".read": true,
                    },

                    "private": {
                        ".read": "auth != null"
                    }
                }
            },

            "status": {
                "pending": {
                    ".read": "auth != null",

                    "$id": {
                        ".write": "root.child('nominations/entries').child($id).exists() && (auth != null || newData.val() == true)"
                    }
                },

                "approved": {
                    ".read": true,

                    "$id": {
                        ".write": "root.child('nominations/entries').child($id).exists() && auth != null"
                    }
                },


                "rejected": {
                    ".read": "auth != null",

                    "$id": {
                        ".write": "root.child('nominations/entries').child($id).exists() && auth != null"
                    }
                }
            }
        }
    }
}

그리고 JavaScript 구현:

var db = new Firebase('https://my.firebaseio.com')
var nominations = db.child('nominations')

var entries = nominations.child('entries')

var status = nominations.child('status')
var pending = status.child('pending')
var approved = status.child('approved')
var rejected = status.child('rejected')

// Create nomination via form input (not shown)
var createNomination = function() {
    var data = {
        public: {
            name: 'Foo',
            age: 20
        },

        private: {
            createdAt: new Date().getTime(),
            phone: 123456
        }
    }

    var nomination = entries.push()
    nomination.setWithPriority(data, data.private.createdAt)

    pending.child(nomination.name()).set(true)    
}

// Retrieve current nomination status
var getStatus = function(id, callback) {
    approved.child(id).once('value', function(snapshot) {
        if (snapshot.val()) {
            callback(id, 'approved')
        } else {
            rejected.child(id).once('value', function(snapshot) {
                callback(id, snapshot.val() ? 'rejected' : 'pending')
            })
        }
    })
}

// Change status of nomination
var changeStatus = function(id, from, to) {
    status.child(from).child(id).remove()
    status.child(to).child(id).set(true)
}

구현 과정에서 유일하게 어려움을 겪고 있는 부분은 상태 변화를 처리하는 것입니다. 현재의 접근 방식은 다음과 같이 확실히 개선될 수 있습니다.

_.each([pending, approved, rejected], function(status) {
    status.on('child_added', function(snapshot) {
        $('#' + snapshot.name()).removeClass('pending approved rejected').addClass(status.name())
    })
})

사용할 예정이었습니다.child_changednominations/status하지만 안정적으로 작동하지 못하고 있습니다.

카토의 말이 맞습니다.보안 규칙은 데이터를 필터링하지 않는다는 점을 이해하는 것이 중요합니다.모든 위치에서 모든 데이터(하위 데이터 포함)를 읽을 수 있거나 데이터를 읽을 수 없습니다.따라서 규칙의 경우, "지명"에서 ".read": true를 갖는 것은 다른 모든 규칙을 무효화합니다.

여기서 제가 추천하는 접근법은 3개의 목록을 갖는 것입니다.하나는 지명 데이터를 포함하고, 하나는 승인된 지명 목록을 포함하고, 다른 하나는 보류 중인 지명 목록을 포함합니다.

규칙은 다음과 같습니다.

{
  "rules": {
    // The actual nominations.  Each will be stored with a unique ID.
    "nominations": {
      "$id": {
        ".write": "!data.exists()", // anybody can create new nominations, but not overwrite existing ones.
        "public_data": {
          ".read": true // everybody can read the public data.
        },
        "phone": {
          ".read": "auth != null", // only authenticated users can read the phone number.
        }
      }
    },
    "approved_list": {
      ".read": true, // everybody can read the approved nominations list.
      "$id": {
        // Authenticated users can add the id of a nomination to the approved list 
        // by creating a child with the nomination id as the name and true as the value.
        ".write": "auth != null && root.child('nominations').child($id).exists() && newData.val() == true"
      }
    },
    "pending_list": {
      ".read": "auth != null", // Only authenticated users can read the pending list.
      "$id": {
        // Any user can add a nomination to the pending list, to be moderated by
        // an authenticated user (who can then delete it from this list).
        ".write": "root.child('nominations').child($id).exists() && (newData.val() == true || auth != null)"
      }
    }
  }
}

인증되지 않은 사용자는 다음을 사용하여 새 지명을 추가할 수 있습니다.

var id = ref.child('nominations').push({ public_data: "whatever", phone: "555-1234" });
ref.child('pending_list').child(id).set(true);

인증된 사용자는 다음과 같은 메시지를 승인할 수 있습니다.

ref.child('pending_list').child(id).remove();
ref.child('approved_list').child(id).set(true);

승인된 목록과 보류 중인 목록을 렌더링하려면 다음과 같은 코드를 사용합니다.

ref.child('approved_list').on('child_added', function(childSnapshot) {
  var nominationId = childSnapshot.name();
  ref.child('nominations').child(nominationId).child('public_data').on('value', function(nominationDataSnap) {
    console.log(nominationDataSnap.val());
  });
});

이러한 방식으로 인증되지 않은 사용자와 인증된 사용자가 각각 열거할 수 있는 lightweight 목록으로 approved_list와 pending_list를 사용하고 실제 지명 데이터를 모두 지명 목록에 저장합니다(누구도 직접 열거할 수 없음).

보안 규칙이 작동하는 방식을 완전히 탐색한 경우(나 자신이 직접 학습한 경우), 한 규칙에서 액세스를 허용할 때 액세스가 허용됩니다.따라서 다음과 같이 읽힙니다.

  • nomations.read": true, ACCESS AIRITED
  • 기타 규칙: 읽지 않음

게다가, 만약 그 규칙이 제거된다면,$nominationId.read"는 레코드가 승인된 경우 액세스 권한을 부여합니다. 따라서,.readphone그리고.state승인될 때마다 불필요해집니다.

이것을 다음과 같이 분류하는 것이 아마도 가장 간단할 것입니다.public/그리고.private/아이들, 이렇게:

nominations/unapproved/          # only visible to logged in users
nominations/approved/            # visible to anyone (move record here after approval)
nominations/approved/public/     # things everyone can see
nominations/approved/restricted/ # things like phone number, which are restricted

갱신하다

더 생각해보면, 당신은 여전히 만드는 문제에 직면할 것이라고 생각합니다.approved/기록을 나열할 수 있게 해주는 공공, 그리고.approved/restricted/사적인.이 사용 사례에서는 제한된 데이터에도 자체 경로가 필요할 수 있습니다.

이 스레드는 약간 구식이고 규칙을 통한 해결책이 있을 수 있지만, 비디오에서 말했듯이, 그것은 깔끔한 속임수입니다: https://youtu.be/5hYMDfDoHpI?t=8m50s .

소방본부 문서에 규칙은 필터가 아니라고 나와 있기 때문에 이 방법은 좋은 방법이 아닐 수 있습니다. https://firebase.google.com/docs/database/security/securing-data

저는 보안 전문가는 아니지만, 저는 그 기술을 시험해 보았는데 잘 작동했습니다.:)

따라서 저는 이 구현과 관련된 보안 문제를 더 잘 이해하기를 바랍니다.

언급URL : https://stackoverflow.com/questions/14296625/restricting-child-field-access-with-security-rules

반응형