Prechádzať zdrojové kódy

docs: correct the claim that view filters are a security boundary

The guide described view filters as 'a security enforcement boundary' and
compared them to row-level security. They are not: authentication attaches no
principal to the call, the underlying collection stays readable (views only
reject writes on view names), and a fully-qualified other_project:collection
name is accepted from any client. Views constrain a cooperating client, which
makes them a schema/shape contract, not access control.

Also notes that field-level encryption is encryption at rest, not access
control - sensitive fields are decrypted for every authenticated reader.

This is the kind of sentence someone builds a threat model on.
fszontagh 1 mesiac pred
rodič
commit
49316a7cff
1 zmenil súbory, kde vykonal 31 pridanie a 1 odobranie
  1. 31 1
      docs/integration-guide.md

+ 31 - 1
docs/integration-guide.md

@@ -362,7 +362,37 @@ The `op` field in `where` filters accepts either a string name (`EQ`, `NE`, `GT`
 
 #### Why AND-merge filters?
 
-The view's filters act as a **security enforcement boundary**. A consumer querying `active_admins` literally cannot see inactive users — the filter is applied server-side, after authentication, before projection. This is how SQL views with `WHERE` clauses work, and how row-level security works in most databases. Any consumer filter is ANDed on top, narrowing further.
+A view's filters are applied **server-side**, before projection, and a consumer
+filter is ANDed on top rather than replacing them — so a caller querying
+`active_admins` cannot widen the result set beyond what the view defines. That
+makes a view a reliable **schema and shape contract**: what comes back through
+this name is always a subset of what the view allows.
+
+> **A view is NOT a security boundary.** Earlier revisions of this guide
+> described view filters as "a security enforcement boundary" and compared them
+> to row-level security. That was wrong, and should not be used as the basis of
+> a threat model. Three reasons:
+>
+> 1. **There is no caller identity.** Authentication (2.4+) compares a bearer
+>    token against a flat list of shared keys per listener. It attaches no
+>    principal to the call, so the server cannot know *who* is asking — and
+>    every key holder has identical, complete access.
+> 2. **The underlying collection stays readable.** Views only reject *writes* on
+>    a view name. `find("active_admins")` narrows; `find("users")` does not.
+>    Nothing stops a client bypassing the view entirely.
+> 3. **Projects are not a boundary either.** `Config::project` is chosen by the
+>    client, and a fully-qualified `other_project:collection` name is accepted
+>    from any client, so namespaces isolate naming, not access.
+>
+> Views constrain what a **cooperating** client sees. Treat them as ergonomics
+> and API hygiene. Real row- and column-level security requires a per-principal
+> identity and a policy evaluated at every read handler; that is planned but not
+> present today.
+>
+> Related: field-level encryption (`sensitiveFields`) is encryption **at rest**,
+> not access control. Sensitive fields are decrypted for every authenticated
+> reader; the protection is against someone reading data files, snapshots, or
+> backups, not against a client.
 
 ### Collection Configuration