Chapter 15 · Security: Authentication, RBAC, Privileges, TLS, Secrets, and Least Privilege

Threat-Model a Graph Application Including Query Injection, Over-Privileged Procedures, Data Exfiltration, and Admin Paths

Threat-model AtlasMart across Cypher construction, tenant traversal, credentials, TLS, procedures, backups and administrative recovery, then turn defenses into negative tests.

Advanced230–300 minutesEnd-to-end graph security threat-model labNeo4j 2026.07.1 Community baseline · Cypher 25Enterprise/Aura RBAC clearly separatedPython driver 6.3.0Last reviewed: September 2026

Learning outcomes

A secure AtlasMart deployment can still fail through composition: a parameterized endpoint is safe from value injection but lets the caller choose arbitrary labels; an APOC HTTP procedure has unrestricted network access; a backup directory is readable by a web-service account; a Community DB credential bypasses application tenant checks; or an admin recovery step temporarily disables authentication while the server remains remotely reachable. Threat modeling connects these paths before an attacker does.

01

Build an asset/trust-boundary/threat/control matrix for graph application, DBMS, plugins, network and admin paths.

02

Distinguish parameterized value safety from dynamic-label/type/query-shape authorization.

03

Assess procedure/plugin, file/URL, backup and unrestricted/boosted execution as data-exfiltration paths.

04

Run safe negative tests for cross-tenant access, injection-like input, bad TLS, bad auth and forbidden admin behavior.

05

Produce a production security acceptance checklist with explicit edition/tier gaps and incident-response ownership.

Chapter 15 baseline · reviewed 9 September 2026

The continuity lab remains Neo4j Community 2026.07.1, database neo4j, explicit CYPHER 25 where language behavior matters, container atlasmart-neo4j, loopback Bolt bolt://127.0.0.1:7687, synthetic credential neo4j/atlasmart-course-2026, official Python driver neo4j 6.3.0, and no mandatory plugin. Neo4j 5.26.30 is the LTS comparison line. For TLS changes, this chapter deliberately uses a second disposable container atlasmart-neo4j-secure so earlier labs are not disrupted.

Edition boundary that changes the security design

Current Community Edition supports native users but has no roles; every Community user has implied administrator privileges. Fine-grained RBAC, built-in/custom roles, graph/database/procedure privileges, external auth-provider integration, security/query logs and related enterprise authorization controls are Enterprise/Aura-tier features. Mandatory Community exercises therefore prove authentication, parameterization, TLS, credential rotation and application-layer authorization, while RBAC denial examples are explicitly marked licensed/deterministic rather than presented as Community output.

Safety boundary

All credentials, certificates and attacks in this chapter are synthetic and disposable. Never paste production passwords/private keys into source control, do not disable certificate verification to make a production connection work, and do not broaden procedure/plugin privileges merely to get a demo running.

Reproducible AtlasMart setup

PowerShell · continuity environment
$env:NEO4J_URI='bolt://127.0.0.1:7687'$env:NEO4J_USER='neo4j'$env:NEO4J_PASSWORD='atlasmart-course-2026'$env:NEO4J_DATABASE='neo4j'py -3 -m venv .venv.\.venv\Scripts\Activate.ps1python -m pip install --upgrade pippython -m pip install neo4j==6.3.0
Cypher · stable security fixture
CYPHER 25CREATE CONSTRAINT customer_id IF NOT EXISTSFOR (c:Customer) REQUIRE c.customerId IS UNIQUE;CREATE CONSTRAINT product_id IF NOT EXISTSFOR (p:Product) REQUIRE p.productId IS UNIQUE;CREATE CONSTRAINT order_id IF NOT EXISTSFOR (o:Order) REQUIRE o.orderId IS UNIQUE;MERGE (c:Customer {customerId:'C-1001'})SET c.name='Mina Rahimi', c.tier='GOLD', c.tenantId='TENANT-A'MERGE (p:Product {productId:'P-1001'})SET p.name='Trail Camera', p.category='Cameras', p.price=129.90MERGE (o:Order {orderId:'O-5001'})SET o.status='PAID', o.tenantId='TENANT-A', o.orderedAt=datetime('2026-09-08T16:30:00Z')MERGE (c)-[:PLACED]->(o)MERGE (o)-[r:CONTAINS]->(p)SET r.quantity=1, r.unitPrice=129.90;

These are course-only credentials and synthetic records. The fixture deliberately includes tenantId because authorization mistakes often appear when a graph traversal crosses a tenant boundary even though authentication succeeded.

1. Define assets and trust boundaries

Asset/boundary Threat Control/evidence
Customer/order graph cross-tenant traversal/PII exfiltration DB RBAC where available + mandatory tenant predicate/subject authorization + negative tests
Cypher text injection or arbitrary query-shape control parameters for values; allowlisted identifiers/query templates
Bolt/HTTPS credential/data interception TLS + full trust verification + network segmentation
DB credential credential theft scoped identity, secret manager, rotation, no source/log exposure
APOC/custom plugin file/network/boosted privilege abuse install minimum extensions; allowlist; no broad unrestricted/boosted access
backup/data directory offline full-database theft filesystem/object-store ACLs, encryption, separate ops identity
admin/recovery path auth disabled or admin token abused local/network isolation, break-glass logging, two-person/runbook controls

2. Query injection: parameters solve values, not arbitrary syntax

Python · wrong: user input becomes Cypher syntax
def search(label, text, driver):    # WRONG: label and text both become query syntax.    query = f"MATCH (n:{label}) WHERE n.name CONTAINS '{text}' RETURN n"    return driver.execute_query(query, database_="neo4j")
Python · safer template + allowlisted identifier + parameterized value
ALLOWED_LABELS = {"Product", "Category"}def search(label, text, driver):    if label not in ALLOWED_LABELS:        raise ValueError("unsupported search domain")    query = f"""    CYPHER 25    MATCH (n:{label})    WHERE n.name CONTAINS $text    RETURN n.name AS name    ORDER BY name    LIMIT 50    """    return driver.execute_query(query, text=text, database_="neo4j")

Cypher parameters are the primary defense for values. When identifiers or query structure cannot be parameterized, do not “escape whatever the user sent”; choose from server-owned query templates/allowlists.

3. Tenant boundary must be before or inside graph expansion

Cypher · vulnerable business query
CYPHER 25MATCH (o:Order {orderId:$orderId})-[:CONTAINS]->(p:Product)RETURN o.orderId, o.status, collect(p.productId) AS products;
Cypher · tenant-qualified contract
CYPHER 25MATCH (o:Order {orderId:$orderId, tenantId:$tenantId})-[:CONTAINS]->(p:Product)RETURN o.orderId, o.status, collect(p.productId) AS products;

Application authorization must establish the caller’s tenant/subject from trusted identity context, not accept a tenant ID and blindly trust it. In Enterprise, graph/property-based privileges may add another DB-enforced layer; in Community the app predicate is critical because the DB user remains an implied admin.

4. Procedures/plugins can become exfiltration or privilege bridges

Extension path Risk Guardrail
APOC file import/export read/write host/container files disabled unless required; constrained import/export directory; least filesystem ACL
APOC HTTP/JSON server-side request to attacker/internal endpoint explicit URL/network policy; do not enable casually
custom Java procedure arbitrary code/resource access depending implementation code review, dependency provenance, compatibility testing
dbms.security.procedures.unrestricted procedure may bypass normal sandbox restrictions minimal glob list; never broad * without threat model
EXECUTE BOOSTED Enterprise privilege escalation inside procedure grant to narrowly reviewed procedures/roles only
shell · inspect extension settings, read-only
docker exec atlasmart-neo4j sh -lc '  grep -E "^[[:space:]]*dbms.security.procedures.(allowlist|unrestricted)"     "$NEO4J_HOME/conf/neo4j.conf" || true  ls -la /plugins 2>/dev/null || true' 

5. Safe negative-test matrix

Test Expected signal Reset/blast radius
wrong password authentication denied none
plain Bolt to TLS-required lab TLS/connection failure none
wrong hostname with trusted certificate certificate verification failure none
cross-tenant order ID zero rows/application 404/403 by policy none
injection string in $text treated as data, not syntax none
forbidden Enterprise delete authorization denied none; licensed RBAC test
unapproved procedure execute/allowlist denied no config broadening to make test pass
break-glass recovery drill network remains localhost-only while auth disabled isolated disposable DBMS only

6. Threat-model one request end to end

text · request security flow
Internet/client  -> API gateway / TLS / authentication  -> application subject + tenant authorization  -> fixed repository method + parameterized Cypher  -> driver TLS + service credential  -> Neo4j authentication  -> Neo4j RBAC (Enterprise/Aura-capable tier) OR documented Community gap  -> graph traversal/property access  -> procedure/plugin/file/network boundary if invoked  -> bounded projection  -> audit/correlation evidence

Every arrow is a trust boundary with an owner. If a control is absent—such as Community DB RBAC—write the gap down and decide whether application controls plus deployment isolation are sufficient for the risk, or whether the tier must change.

7. Security acceptance checklist

Area Acceptance evidence
Identity no application admin credentials; break-glass owner and rotation documented
RBAC/tier licensed deployment has explicit grants/denies; Community gap acknowledged, not mislabeled
Injection values parameterized; dynamic identifiers/query templates allowlisted; regression tests include adversarial strings
Tenant isolation trusted identity→tenant mapping and cross-tenant negative tests
Transport remote Bolt/HTTPS encrypted with verified CA/hostname; no production ignore flags
Secrets no real secret in source/image/log; tested rotation/revocation
Extensions plugins inventoried/pinned; unrestricted/boosted/file/network capabilities minimal
Admin/backup host, backup, certificate/private-key and recovery paths access-controlled
Evidence security/query/app/audit evidence available to tier is protected and reviewed
Incident response credential/procedure/data-exfiltration tabletop can be executed without improvisation

8. Cleanup of course-only identities

Cypher · remove optional course users/roles where they exist
DROP USER atlasmart_app IF EXISTS;// Enterprise-only cleanup if you created the optional RBAC lab objects:// DROP USER atlasmart_reader_user IF EXISTS;// DROP USER atlasmart_service IF EXISTS;// DROP ROLE atlasmart_reader IF EXISTS;// DROP ROLE atlasmart_order_writer IF EXISTS;// DROP ROLE atlasmart_visualizer IF EXISTS;

Production judgment

Review area Decision evidence
Identity/authentication native or external identity source, MFA/SSO upstream where available, password/token lifecycle, lockout and break-glass process
Authorization least-privilege database/graph/procedure grants; explicit DENY review; Community control gap documented
Transport encrypted remote Bolt/HTTPS, trusted CA and hostname validation; self-signed only for isolated testing
Secrets out of source/logs/images; rotated without code changes; incident revocation path tested
Application layer parameterized Cypher, tenant/subject authorization before graph expansion, bounded result/data-export paths
Extensions/admin procedure allowlist/unrestricted/boosted review, plugin provenance, backup/admin filesystem and host access
Evidence auth/TLS denial tests, security/query logs where licensed, driver/server correlation and configuration review
Recovery credential compromise playbook, backup encryption/access, break-glass scope, rollback/reconciliation and post-incident validation

Check your understanding

  1. Does parameterizing $text make an arbitrary user-provided label safe?
  2. Why are procedures part of the threat model?
  3. What is the strongest Community authorization gap to remember?
  4. Why protect backups like the live database?
  5. What makes a security acceptance test useful?
Review the answers

1. No. Parameters protect values; dynamic identifiers/query shape need fixed templates or strict allowlists.

2. They can access graph, files, network or elevated privileges beyond ordinary query intent depending configuration and implementation.

3. All Community users have implied administrator privileges; the DBMS cannot enforce Enterprise-style least-privilege roles.

4. A readable backup can disclose the entire graph without going through Bolt authentication/RBAC.

5. It contains observable positive and negative behavior, explicit ownership, edition/tier assumptions and a safe reset path.

Summary and next step

Neo4j security is layered: identity, authorization, verified transport, secret lifecycle, safe query construction, extension governance, filesystem/admin boundaries and evidence. Chapter 16 can now introduce clustering and availability without treating replication as a security control or assuming a highly available cluster automatically has a least-privilege architecture.

Authoritative references

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.