A graph database answers questions about relationships, so one over-broad role or one concatenated Cypher query can expose not just records but the connections between people, accounts and systems. Neo4j gives administrators the controls to prevent that: native, LDAP and single sign-on authentication, role based access control down to labels, relationship types and properties, TLS policies for every connector, procedure allowlists and detailed security and query logs. Growing use of graphs for GraphRAG and AI agent memory adds application identities that query the graph on behalf of users, which makes these controls more important. This guide covers self managed Neo4j and the customer side settings in Neo4j Aura, plus how to write Cypher that resists injection. It is written for database administrators, application developers and security architects. For general database controls, see the Database Security Guide.
This is an independent guide from NHI Mgmt Group, based on Neo4j's public documentation as of November 2026. It is not sponsored or reviewed by Neo4j. Product features change, so confirm settings against Neo4j's current documentation.
Key takeaways
- Set the initial password before first start. Use
neo4j-admin dbms set-initial-passwordso theneo4juser never runs with its default password, and keepdbms.security.auth_enabled=true. - Build custom roles, not admin for everyone. Built-in roles step from reader to admin; create application roles with GRANT and DENY on specific labels, relationship types and properties, remembering that DENY always wins.
- Parameterise every Cypher value. Parameters keep user input out of the query structure; avoid building labels from input and never concatenate into strings passed to APOC procedures that run Cypher.
- Require encrypted Bolt and HTTPS. Enable SSL policies and set
server.bolt.tls_level=REQUIRED, since Bolt starts with TLS disabled until you configure it. - Watch security.log and query.log. The security log records logins, administration commands and authorisation failures; the query log shows who ran what.
How identity and access work in Neo4j
Neo4j stores native users, roles and privileges in the system database. External providers can authenticate users and map their groups to Neo4j roles, and several providers can run together in a defined order.
| Provider | Configuration | Best fit |
|---|---|---|
| Native | Users and roles in the system database; dbms.security.auth_enabled (default true) | Service accounts and small teams |
| LDAP | Built-in LDAP connector to Active Directory or OpenLDAP; use StartTLS | Enterprises managing groups in a directory |
| Single sign-on (OIDC) | dbms.security.authentication_providers=oidc-<name> and dbms.security.oidc.<name>.* | Interactive users of Neo4j Browser and Bloom with an enterprise IdP and MFA |
| Kerberos | Neo4j Kerberos Add-On | Windows domain and Hadoop estates |
| User auth providers | Link externally defined users to Neo4j's internal user model | Mixed estates that need native roles for external users |
Availability: Neo4j's documentation describes these authentication and authorisation features as Enterprise Edition capabilities, with a limited set of user management functions in Community Edition. Check the edition notes for the features you plan to use.
Native authentication settings
- Initial password: the default user is
neo4jwith passwordneo4j, which must be changed at first login unless set beforehand. Runneo4j-admin dbms set-initial-passwordbefore the first start, add--require-password-changewhen issuing a temporary value, and avoid putting the password on the command line where shell history keeps it. - Password length: at least 8 characters by default, adjustable with
dbms.security.auth_minimum_password_length; raise it to match your password policy. - Lockout:
dbms.security.auth_max_failed_attempts(default 3) anddbms.security.auth_lock_time(default 5s) slow guessing; tune both and alert on repeated lockouts.
Single sign-on details
For OIDC, keep auth_flow at its default pkce, which the documentation recommends always using, point well_known_discovery_uri at the IdP, and map groups with claims.groups and authorization.group_to_role_mapping. Neo4j checks the token's issuer and audience against configuration. Turn off dbms.security.logs.oidc.jwt_claims_at_debug_level_enabled in production. See the JWT Security Guide.
Admin roles and privileged access
Built-in roles are hierarchical, each adding to the one below. Every user also holds PUBLIC, which cannot be revoked but whose privileges can be changed.
| Role | Adds | Typical holder |
|---|---|---|
| PUBLIC | Access to the home database, executing procedures and functions with the user's own privileges, loading data; no data read or write | Everyone (review what it can do) |
| reader | Read and traverse all nodes, relationships and properties in all databases | Analysts and read only applications |
| editor | Write to existing data using existing labels, types and property keys | Applications that update data |
| publisher | Create new labels, property keys and relationship types | Data loading pipelines |
| architect | Create and drop indexes and constraints | Schema owners |
| admin | Manage databases, users, roles and privileges; boosted procedures; terminate transactions; change configuration | A small named DBA group |
Fine grained access control
Access to a graph element needs a GRANT and no matching DENY. DENY does not erase a GRANT; both exist, and DENY overrides. Read restrictions make data invisible, so the graph simply looks smaller, while write restrictions raise an error.
- Database access first: without the ACCESS privilege a user is denied the whole database, so grant ACCESS per database rather than relying on reader's all-database scope.
- Labels and relationship types: target
NODES label,RELATIONSHIPS typeorELEMENTS labelto limit what each role traverses and reads. - Property based rules:
FOR patterngrants or denies reads based on property values, useful for tenant or classification fields (not supported for write privileges). - Immutable privileges:
IMMUTABLEprivileges can only be created or removed with authentication disabled, so they protect key rules from even an admin account. - Review:
SHOW ROLE <name> PRIVILEGES AS COMMANDSexports privileges as Cypher, ready for version control and diffing. - Recreated databases: privileges are not restored when a database is dropped and recreated with the same name, so reapply them from source control.
For GraphRAG and agent workloads, give the retrieval service a role that can read only the labels and properties its users are entitled to, so the graph enforces permissions before content reaches a model. See the Permission-Aware RAG Guide.
Integrations, API tokens and service accounts
Applications connect over Bolt with driver credentials, and those application users usually hold more privilege than any person. Treat them as non-human identities with owners, scoped roles and rotation. See the Service Account Security Guide.
- One user per application: separate users for each service make the query log attributable and let you revoke one integration without touching others.
- Least privilege role: a read only application should hold a custom role with read on specific labels, never editor or admin, which limits the damage of any injection.
- Secrets storage: keep driver passwords in a secrets manager and inject them at runtime. See the Secrets Management Guide.
- Procedures and plugins: review
dbms.security.procedures.unrestrictedanddbms.security.procedures.allowlistso only intentionally exposed procedures are available, and restrict theplugins,libandbindirectories to the Neo4j operating system user. - LOAD CSV: limit load privileges to trusted users, keep
server.directories.importpointing at a dedicated directory, setdbms.security.allow_csv_import_from_file_urlsto match your need, and allow only HTTPS sources.
Preventing Cypher injection
Neo4j's knowledge base describes Cypher injection as input that escapes its context and alters the query, the counterpart of SQL injection. The defences are application side:
| Pattern | Safer approach |
|---|---|
| String concatenation of values, including numbers | Pass values as parameters, for example WHERE n.name = $name; the query compiles separately from the values |
| Labels, relationship types or property keys from user input | Model the variable part as a node property (match {name: $cityName}), validate against an allowlist, or use dynamic label parameters available from Neo4j 5.26; if escaping, double backticks and handle Unicode forms |
Query strings passed to apoc.cypher.run, apoc.cypher.doIt, apoc.periodic.iterate or apoc.trigger.add | Keep the inner query static and pass every value through the parameter map |
| Raw database errors returned to users | Return generic errors to prevent error based discovery |
| Stored values later concatenated into queries | Parameterise regardless of where the value came from, to stop second order injection |
| Keyword blocklists and whitespace filtering | Do not rely on them; comments, Unicode and concatenation bypass them |
LLM generated Cypher in text-to-Cypher and agent tools carries the same risk at a larger scale: run it under a read only, label restricted role and validate it before execution. See the SQL Injection Prevention Guide for the shared principles. Parameters also help Neo4j cache execution plans, so the secure pattern is the fast one.
Logging and detection
| Log | Records | Key settings |
|---|---|---|
security.log | Successful and failed logins, all administration commands against system, RBAC authorisation failures, LDAP connection events | On when auth is enabled; dbms.security.log_successful_authentication=false reduces volume for heavy programmatic use |
query.log | Query text, user, client address, driver version, timings and optionally parameters | db.logs.query.enabled (INFO or DEBUG), db.logs.query.threshold, db.logs.query.parameter_logging_enabled |
Passwords appear masked in the security log. Disable parameter logging where parameters carry personal or secret data. Alert on new users and role grants, on any grant of admin, on DENY rules being revoked, on authorisation failures from an application user, and on queries from a service account outside its normal patterns. Forward both logs to the SIEM; see the SIEM Guide.
Metrics that show it is working
- Number of users holding admin, reviewed monthly.
- Share of application users on custom least privilege roles rather than built-in editor or admin.
- Share of connections using encrypted Bolt or HTTPS (target 100%).
- Count of procedures on the unrestricted list, each with an owner.
- Code review findings for concatenated Cypher, trending to zero.
Hardening baseline
- Before first start: set the initial password, confirm
dbms.security.auth_enabled=trueand set password length and lockout values. - TLS: create
certificates/boltandcertificates/httpsdirectories withprivate.key(mode 0400) andpublic.crtfrom a trusted CA, enabledbms.ssl.policy.bolt.enabledanddbms.ssl.policy.https.enabled, setserver.bolt.tls_level=REQUIRED, and keeptls_versionsat TLS 1.2 and 1.3. Leavetrust_allfalse andverify_hostnametrue. - Cluster and backup: enable the
clusterandbackupSSL policies, block the backup listener from outside, and if you do not cluster, bindserver.cluster.listen_addressandserver.cluster.raft.listen_addressto 127.0.0.1 and firewall ports 6000, 7000 and 7688. - Roles: create custom roles per application and team, grant ACCESS per database, trim PUBLIC, and store privilege scripts in version control.
- Extensions: review procedure allowlists, LOAD CSV settings and remote JMX options in
server.jvm.additional. - Files: set data, configuration and log directories to mode 700 or 750 on Linux (owner and service account only on Windows) and encrypt volumes, dumps and backups.
- Single sign-on: configure OIDC with PKCE and group to role mapping for people, keeping native users for services.
- Logging: confirm security and query logs reach the SIEM before go-live.
- Test and roll back: apply changes in staging first and keep the previous
neo4j.confand privilege scripts for rollback. - Patching: run the latest patch release and follow Neo4j security advisories.
Neo4j Aura
In Aura, Neo4j runs the servers and the customer controls access, network exposure and keys. Aura documentation describes TLS 1.2 and 1.3 with a restricted cipher list for client connections, public, private or combined network access with the option to disable public traffic, private endpoints over cloud private link services and dedicated VPC isolation on AuraDB Virtual Dedicated Cloud and AuraDS Enterprise, and lists customer managed keys, IP filtering, single sign-on and multi-factor authentication among its security features. Database roles and Cypher practices above apply unchanged. Check the Aura documentation for the tier each feature requires.
Framework mapping
| Control area | NIST CSF 2.0 | NIST SP 800-53 Rev 5 | ISO/IEC 27001:2022 Annex A |
|---|---|---|---|
| Authentication and SSO | PR.AA-01 | IA-2, IA-5 | A.8.5 |
| Roles, GRANT and DENY | PR.AA-05 | AC-3, AC-6 | A.8.2, A.8.3 |
| Cypher injection prevention | PR.PS-01 | SI-10 | A.8.28 |
| Bolt, HTTPS and cluster TLS | PR.DS-02 | SC-8 | A.8.24 |
| Volume and backup encryption | PR.DS-01 | SC-28 | A.8.24 |
| Security and query logs | PR.PS-04, DE.CM-03 | AU-2, AU-12 | A.8.15 |
Common misconfigurations
- Disabling authentication to recover access and forgetting to re-enable it: the documentation advises disabling auth only with network connections blocked. Re-enable immediately and check
security.log. - Applications running as admin: an application on the admin role turns any injection into full control of users and databases (ATT&CK T1078 Valid Accounts describes misuse of such credentials). Use custom roles.
- Unencrypted Bolt: leaving
server.bolt.tls_levelat its default sends credentials and data in clear text. Set REQUIRED once clients are ready. - Concatenated Cypher in APOC calls: passing user input inside the query string of
apoc.cypher.runstays injectable even when the value is also passed as a parameter. Keep inner queries static. - Open cluster and backup ports: an exposed backup port lets an unauthorised user copy the database. Firewall it and enable the backup SSL policy.
- Sensitive parameters in query.log: parameter logging records personal data and tokens. Disable it where parameters are sensitive.
Practitioner checklist
- The initial password was set before first start and
dbms.security.auth_enabledis true. - Password length and lockout settings match policy, and lockouts raise alerts.
- People sign in through OIDC with PKCE or LDAP over StartTLS, with groups mapped to roles.
- Admin is held by a small named group; applications use custom roles with ACCESS on specific databases and reads on specific labels.
- Privilege scripts are exported with
SHOW PRIVILEGES AS COMMANDSand kept in version control. - All application Cypher uses parameters, including inside APOC procedures, and code review checks for concatenation.
- Bolt and HTTPS SSL policies are enabled with
server.bolt.tls_level=REQUIRED, and cluster and backup traffic is encrypted. - Procedure allowlists, LOAD CSV settings and plugin directories are restricted and reviewed.
security.logandquery.logreach the SIEM, with parameter logging off for sensitive data.
Standards and references
- Neo4j: Authentication and authorization
- Neo4j: Built-in roles
- Neo4j: Role-based access control and privileges
- Neo4j: Security checklist
- Neo4j: SSL framework
- Neo4j: Protecting against Cypher injection
- Neo4j: Logging
- Neo4j: Aura secure connections
Related NHI Mgmt Group resources: Database Security Guide · Permission-Aware RAG Guide · Service Account Security Guide · SQL Injection Prevention Guide · Secrets Management Guide