Skip to content

Commit 8ae3a4a

Browse files
committed
add a section about avoiding fail-open DENY behavior
1 parent 1ceeb0f commit 8ae3a4a

File tree

1 file changed

+68
-3
lines changed

1 file changed

+68
-3
lines changed

modules/ROOT/pages/authentication-authorization/limitations.adoc

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ CREATE ROLE unrestricted;
1616

1717
Using Neo4j's role-based access control imposes some limitations and implications that users should be aware of, such as:
1818

19-
* Impact on query results regardless of whether the indexes are used.
20-
* Impact results when nodes have multiple labels.
21-
* Potential performance impacts when querying large graphs with complex security rules.
19+
* Impact on query results regardless of whether indexes are used.
20+
* Impact on query results when nodes have multiple labels.
2221
* The need for careful management of user roles and privileges to avoid unintended data exposure.
22+
* Potential performance impacts when querying large graphs with complex security rules.
2323

2424
[[access-control-limitations-indexes]]
2525
== Security and indexes
@@ -186,6 +186,71 @@ Otherwise, it will process as described before.
186186

187187
In this case, the query will return zero results rather than simply returning the results `Andy` and `Sandy`, which might have been expected.
188188

189+
=== Avoiding fail-open `DENY` behavior
190+
191+
A `DENY` rule fails open when its criteria is not met, so Neo4j does not apply the restriction and it grants access by default if a broader `GRANT` exists.
192+
This can lead to unintended data exposure if the `DENY` rule is not carefully crafted.
193+
194+
For example, consider the following scenarios:
195+
196+
.Example of an un-met `DENY` failing open with property-based RBAC
197+
====
198+
You grant a user access to a property, but then try to restrict it with a `DENY` rule that does not match the data.
199+
If the `DENY` rule does not match any data, it does not apply, and the user can still access the property.
200+
201+
[source, cypher]
202+
----
203+
GRANT READ {salary} ON GRAPH * NODES Employee TO myRole
204+
DENY READ {salary} ON GRAPH * FOR (e:Employee) WHERE e.position = 'CEO' TO myRole
205+
----
206+
In this case, if the `e.position` property is null or misspelled, the `DENY` rule will not apply, and `myRole` will see the `salary` property.
207+
208+
A better way is to use a `GRANT` rule that only allows access to the `salary` property for employees whose position is not 'CEO'.
209+
[source, cypher]
210+
----
211+
GRANT READ {salary} ON GRAPH * FOR (e:Employee) WHERE e.position <> 'CEO' TO myRole
212+
----
213+
214+
Or, if for some reason using `DENY` is unavoidable, the problem can be mitigated by adding an additional `DENY` to cover the case where `e.position` is null:
215+
[source, cypher]
216+
----
217+
DENY READ {salary} ON GRAPH * FOR (e:Employee) WHERE e.position IS NULL TO myRole
218+
----
219+
This way, if `e.position` is null, the user will not see the `salary` property, and the `DENY` will not apply.
220+
221+
Alternatively, you can add a constraint to ensure that the `e.position` property cannot be null, so the `DENY` condition is always checkable:
222+
[source, cypher]
223+
----
224+
CREATE CONSTRAINT ON (e:Employee) ASSERT e.position IS NOT NULL;
225+
----
226+
This way, the `DENY` will never apply due to null values, and the user will not see the `salary` property for employees whose position is 'CEO'.
227+
228+
====
229+
230+
.Example of an un-met `DENY` failing open with label-based RBAC
231+
====
232+
233+
In a similar way, a `DENY` rule will not apply when it is too broad and does not match the data.
234+
[source, cypher]
235+
----
236+
GRANT READ {salary} ON GRAPH * NODES * TO myRole;
237+
----
238+
239+
This grants read access to the `salary` property on all nodes, including those that should not be accessible.
240+
241+
Then, you try to restrict it with a `DENY` rule to prevent access to the `salary` property on nodes labeled `Management`:
242+
[source, cypher]
243+
----
244+
DENY READ {salary} ON GRAPH * NODES Management TO myRole;
245+
----
246+
In this case, if the `Management` label is not present on a node that has the `salary` property, the `DENY` rule will not apply, and `myRole` will still see the `salary` property on that node.
247+
A better way is to use a `GRANT` rule to only allow access to the `salary` property for nodes that have a specific label, such as `IndividualContributor`:
248+
[source, cypher]
249+
----
250+
GRANT READ {salary} ON GRAPH * NODES IndividualContributor TO myRole;
251+
----
252+
This way, the user will only see the `salary` property on nodes that have the `IndividualContributor` label, and not on any other nodes.
253+
====
189254

190255
[[access-control-limitations-labels]]
191256
== Security and labels

0 commit comments

Comments
 (0)