What a Dify security review covers and when to commission one
A security review of a self-hosted Dify should examine every path by which a request, a model key or a stored secret can reach something it should not. Commission one before go-live, after any major version change and before any app is exposed to people outside your organization. Dify is an LLM application platform for building AI workflows, RAG pipelines and agents through a visual interface, according to the langgenius/dify repository, and a deployment of it holds model provider keys, internal knowledge bases and the tools those agents call. That combination makes the attack surface wider than a typical web app.
The surface spans several services, not one web console. The web console and public APIs accept user and service traffic. The api and worker services run workflows and ingestion. The code sandbox executes Code and Template nodes, the SSRF proxy is the only intended exit for outbound HTTP from nodes, tools and external knowledge calls, and the plugin daemon installs and runs third-party code. Each boundary needs its own test cases: exposed endpoints, SSRF and sandbox escape attempts, plugin supply chain trust, prompt injection through tools and retrieved documents, and secrets held in the .env file. Ask your provider to confirm, for your release, how each boundary is configured and which network paths are actually blocked.
Dify ships minor releases roughly monthly, and the releases page records security changes such as sandbox network isolation and token-based authentication between services. A review should therefore be pinned to a specific version and repeated after any jump that changes the architecture. DeployOpen runs security testing, including application, API, infrastructure and AI agent testing, but it does not issue audit reports, attestations or certifications. If you need evidence for an external assessor, agree in writing which findings and artifacts a test will produce and which independent body will rely on them.
Which parts of a self-hosted Dify belong in scope
Every route a browser, client application or external system can reach is in scope, and the tester should map them before any active testing starts. In a Docker Compose deployment, the Nginx container is the usual front door, forwarding traffic to the web frontend, the console API, the service API, and any published MCP server URLs. The scope should also cover the web app endpoints that serve published apps and the file upload and download paths. Authentication, authorization between workspaces, and rate limiting are the main checks here.
- Console and web app access: Test login, invitation, password reset and session handling for the console, and whether published web apps can be reached without the access level you intended.
- Service API: Test that service API keys are required on every endpoint, that keys are scoped to the right app, and that one tenant cannot read another's data by changing identifiers.
- MCP endpoints: Check whether published MCP server URLs embed credentials and whether a leaked URL grants the same access as an API key.
- Nginx and proxy configuration: Read the Nginx configuration for exposed admin paths, debug routes, and the ports published by Compose, and confirm which services bind to host interfaces.
- Public URL and CORS settings: Read the configured origins and console, API and file URLs, then test whether a foreign origin can make credentialed requests.
Confirm the exact route list against the version you deploy, because routes change between releases.
Any feature that makes the server call a URL is a server-side request forgery candidate. In Dify this includes HTTP request nodes, tools that call external APIs, external knowledge base connectors, and model provider endpoints that you configure with custom base URLs. The Docker Compose deployment includes an SSRF proxy, and the environment reference describes proxy settings that route outbound requests through a proxy that blocks internal and private IP ranges. The scope should test whether that protection holds for every outbound path, not only the default one.
- HTTP and tool nodes: Submit internal addresses, loopback names, link-local metadata addresses and DNS names that resolve to private ranges, and record whether the request leaves the platform.
- External knowledge calls: Test connector URLs and credentials, and check whether redirects can move a request from an allowed host to an internal one.
- Proxy configuration: Read the SSRF_PROXY settings and confirm which containers actually use the proxy, since a service that bypasses it defeats the control.
- Allowlist changes: Review any exceptions added for internal hosts, and test whether those exceptions are wider than the business need.
- Model endpoints: Check whether a custom model base URL can point at internal services.
Ask the vendor or your own build notes which outbound paths skip the proxy in your version.
Code and Template nodes run user-authored logic, so the sandbox that executes it is a privileged boundary. The scope should cover who can author these nodes, what the sandbox can reach over the network, and whether it can escape into the host or other containers. The 1.16.1 release notes describe agent sandbox network isolation through a Squid proxy and sandboxed Jinja2 template rendering. Testing should confirm those controls work in your deployed version rather than assuming the release notes describe your configuration.
- Authoring rights: Identify which roles can create or edit Code and Template nodes, and test whether a lower-privileged user can add one through an imported DSL file.
- Sandbox API key: Read the sandbox configuration and confirm the key is not the default value shipped in the example environment file.
- Egress: Attempt outbound connections from inside the sandbox to the internal network, metadata services and external hosts.
- Resource limits: Test timeouts and output size limits with long-running and memory-heavy code, and check whether a failed run affects other workflows.
- Template escape: Attempt Jinja2 template injection and check what objects are reachable from the template context.
Record the sandbox image version, because the boundary depends on it.
Plugins extend Dify with model providers, tools and agent strategies, and the plugin daemon installs and runs them with access to the platform. The supply chain scope covers where plugins come from, who approves them, how signatures are checked, and what the daemon can read once a plugin runs. Plugin code is third-party code running inside your environment, so each install source is a trust decision that belongs in the review.
- Install sources: Read the configuration that controls which marketplaces and URLs plugins can be installed from, and test whether a console user can install from an unapproved source.
- Signature enforcement: Confirm that signature verification is on and that the daemon trusts only the public keys you intend, then test an unsigned or altered package.
- Daemon privileges: Review the daemon's access to the database, environment variables and network, and check whether a plugin can read values it does not need.
- Private plugin process: Review how internal plugins are built, signed and distributed, and who holds the signing keys.
- Update path: Test whether a plugin update can change its declared permissions without a new review.
Ask for the plugin inventory of the target instance, since each installed plugin is part of the attack surface.
Prompt injection is in scope wherever untrusted text reaches a model that can act, including tool responses, knowledge base documents and retrieved chunks. Secrets are in scope wherever credentials live, most visibly in the environment file that Compose reads at startup. The review should test whether injected instructions can cause a workflow to call a tool, reveal a prompt, or send data to an unintended destination, and whether the secrets that protect the platform are stored and rotated safely.
- Tool outputs: Feed crafted instructions through a tool response and check whether the agent calls other tools or exposes data it should not.
- Knowledge content: Upload documents containing instructions and test whether retrieved chunks change the agent's behavior or citations.
- Environment file: Read the
.envfile for values such asSECRET_KEY, database and Redis passwords, and sandbox credentials, and check file permissions and whether the file is committed to version control. - Secret impact: Confirm what
SECRET_KEYsigns and encrypts, since rotating it logs out all users, and check how workspace encryption keys are stored. - Exports: Confirm that DSL exports omit third-party API keys, and test whether logs or traces capture secrets.
Ask the team which secrets are stored outside the environment file and who can read the file on the host.
How to scope the engagement step by step
The sequence turns a vague request to test Dify into a written scope that names every component in play, the accounts the testers will use, the systems they must not touch, and the windows and reporting path that govern the work.
Inventory the deployed version and compose file
Record the exact Dify version in use, the Docker Compose file and the .env values that differ from the defaults, including the image tags for each service. Confirm whether the instance is Community or Enterprise and whether any plugins are installed. Compare the running version against the Dify releases page so the team knows which security changes already apply, such as the agent sandbox network isolation and token-based service authentication described in the 1.16.1 release notes. The step is done when the version, compose file and environment file are stored with the engagement record and the owner of each has confirmed them.
List every compose service as in scope or excluded
The Docker Compose deployment starts the api, worker, web, plugin daemon, PostgreSQL, Redis, Weaviate, Nginx, a code sandbox and an SSRF proxy. Write each one on a single list and mark it in scope or excluded, with a reason for every exclusion. Include any optional services the team added, such as a separate vector store or external object storage. Ask the client to confirm that nothing else on the host or network exposes Dify functionality. The step is done when the list has one row per service and the client has signed off on each status.
Map trust boundaries and data stores
Draw where requests cross from the internet to Nginx, from the web and API tiers into the database, cache and vector store, from the sandbox and SSRF proxy to external hosts, and from the plugin daemon to plugin sources. Identify the metadata database (PostgreSQL, MySQL or OceanBase), the file storage provider, the vector store and the Redis instance, and note where each holds credentials or user content. Mark which boundaries the test is meant to probe, such as outbound requests from HTTP nodes and tools. The step is done when the diagram and data store inventory have been reviewed by the platform owner and any unclear flows are written down as questions.
Agree test accounts and roles
Decide which accounts the testers will use: an unauthenticated visitor, a standard workspace member, a builder who can author apps and code nodes, a workspace admin, and, where SSO is enabled, an account from each identity provider scope in use. Agree how accounts are created, who holds the credentials, how they are removed afterward, and whether the tester may invite users or change sign-up settings. Record which features each role should reach so the team can tell a finding from expected behavior. The step is done when the account matrix is signed off and each credential has a named owner and a removal date.
Define rules of engagement and out-of-scope systems
Write the rules that limit the test: permitted techniques, whether the tester may send payloads that trigger outbound requests to internal addresses, whether denial-of-service or load tests are allowed, and what to do if production data is exposed. Name the systems the tester must not touch, including model provider accounts such as OpenAI or Azure endpoints, third-party knowledge sources, vector databases hosted by others and the client's identity provider. Confirm which API keys the tester may use and that the client will rotate them after the test. The step is done when both parties have signed the rules and the excluded systems list matches the boundary map.
Set test windows and a stop path
Agree the dates and hours for testing, the environment to be tested (staging, a copy of production or production itself), and the contacts who can halt the test. Agree whether backups are taken and verified before testing starts, since some tests change app data or fill storage. Set a rule for when the tester must stop and call the contact, such as a crash of the api service, a suspected breach of real user data or a sandbox that consumes host resources. The step is done when the window, environment and stop contacts are recorded and the client has confirmed that monitoring is watching the instance during the window.
Confirm the reporting path and retest terms
Decide who receives findings during the test and who receives the final report, how critical issues are escalated before the report is written, and the format each finding will use, including severity, reproduction steps, affected service and suggested fix. Agree how the team will verify fixes, including whether a retest is included in the fixed scope and how a fix is confirmed against the same version or after an upgrade. Note that the release cadence is roughly monthly, so the retest should name the version it applies to. The step is done when the reporting contacts, escalation rule and retest terms appear in the written scope, ready for the testing team to start.
Test areas that matter most for SSRF and sandbox escape
A Dify penetration test should spend most of its time where untrusted input reaches a server-side component. The six areas below cover outbound requests, code execution, internal service trust, tenant boundaries, model-driven misuse and credential exposure, and each one names the evidence a tester should bring back.
SSRF through HTTP nodes and tools
HTTP request nodes, tools and external knowledge calls make outbound requests from the api and worker services. The SSRF proxy is meant to block internal and private IP ranges, so the tester should attempt requests to loopback, link-local, container network and metadata addresses, and to hostnames that resolve to private addresses. Then review every allowlist exception your team added, because each exception is a path the test must confirm stays limited to the named host. Evidence to produce: a request log showing each blocked and allowed destination, and the proxy configuration in use.
Code sandbox escape and egress
Code and Template nodes run in a separate sandbox, and the 1.16.1 release notes describe Squid-based network isolation for the agent sandbox and sandboxed Jinja2 rendering. Testers should try to read files outside the sandbox, reach the api, database or Redis containers, and open connections that bypass the proxy. They should also check timeouts and output size limits, since those limits cap what a malicious script can exfiltrate. Evidence to produce: the sandbox version, the exact egress rules tested, and a record of each escape attempt with its result.
Token-based service authentication
The 1.16.1 notes also describe token-based API authentication between Dify services. A tester should confirm that the internal endpoints of the api, worker and plugin daemon reject requests that lack a valid token, and that a request from the public web tier cannot call internal routes directly. Check whether the internal token is set to a default value in the deployed environment file. Evidence to produce: a list of internal routes, the response each returned to an unauthenticated and to a forged request, and the token source each service reads.
Console and API authorization across workspaces
Broken object-level authorization is the most common finding in multi-user AI platforms. Testers should use two accounts in separate workspaces and try to read, edit or run apps, datasets and API keys belonging to the other workspace by changing identifiers in requests. The same check applies to roles, so a basic member should not reach administrator functions. Evidence to produce: a matrix of account, role, object and action, with the status code returned for each cross-workspace attempt.
Prompt injection through tools and knowledge
Retrieved documents, web pages and tool outputs enter the model context, and any of them can carry instructions that try to change agent behavior. The tester should plant instructions in an uploaded document and in a page returned by an HTTP tool, then check whether the agent calls tools it should not, reveals system prompts, or sends data to an external URL. Confirm which tools are enabled for each app, since the blast radius depends on them. Evidence to produce: the planted payloads, the tool calls the agent made, and the outbound destinations observed.
Secrets in env files and logs
The environment file holds the database credentials, SECRET_KEY, the sandbox API key and model provider settings, and the team must decide who can read it and whether it leaks into container logs or backups. Testers should check file permissions on the host, whether the .env file is served by the web tier, whether container logs print configuration values, and whether exports include credentials. Note that DSL exports are documented as excluding third-party API keys, so verify that claim against your own export. Evidence to produce: file permission listings, log samples searched for secret patterns, and a list of values found in plain text with their locations.
Reviewing the plugin supply chain and install sources
The first step is establishing what is running. Ask for a complete inventory of every plugin installed in each workspace of the self-hosted Dify, including name, version, publisher and the source it was installed from, whether that is the official marketplace, a private package file or another location. Record whether signature enforcement is switched on, and identify every account that can install, update or publish plugins. Those permissions sit alongside workspace administration in the console, so the review should confirm which roles can reach the plugin screens. An inventory taken at the start of the engagement gives the tester a baseline to compare against after remediation.
The plugin daemon is the component that makes plugins a security concern. It runs as a core service in the Docker Compose deployment and executes third-party code on behalf of models, tools and data sources. Treat it as code execution inside your perimeter. The tester should establish which network segments the daemon can reach, whether it can read the environment variables and database credentials used by the other services, and whether outbound traffic from plugins passes through the SSRF proxy or leaves directly. Each answer should come from a test with a deliberately harmless plugin and an inspection of the running container, so the findings reflect observed behavior rather than assumptions.
Do not assume the defaults for signature enforcement or install sources. Confirm the current values against the Dify environment variable reference and against the .env file used by the running stack, since the file on a build server and the file in production often differ. Export the effective values from the running containers as evidence. Repeat the check after every upgrade, because the Compose documentation advises comparing .env with .env.example when a new version arrives. Finally, record who may change these settings and how changes are approved, since a plugin signed by a trusted key still gains capabilities once it is installed.
Assessment types and when each fits
This table compares five assessment types that apply to a self-hosted Dify, each answering a different question and needing different access from your team. Read it by starting with the question you need answered, then checking what the assessment needs from you. The main takeaway is that a configuration review is a prerequisite for the others, and AI-specific testing does not replace testing the surrounding web application and infrastructure.
| Assessment type | What it answers | What it needs from the client | When it fits |
|---|---|---|---|
| Configuration review | Are SECRET_KEY, sandbox keys, SSRF proxy rules, sign-up settings and CORS values set as intended? | Read access to the .env file, Compose or Helm values, reverse proxy config and sign-up settings, with secrets handled out of band | Before any other test, and again after upgrades that add new variables |
| Authenticated application and API testing | Can a user reach workspaces, apps, datasets or API keys they should not, and can HTTP nodes or tools reach internal hosts? | Test accounts for each role, a staging instance that mirrors production, and a list of internal hosts the SSRF proxy is expected to block | When multiple roles, workspaces or published apps exist and access must be proven |
| Infrastructure and container testing | Are exposed ports, the code sandbox, the plugin daemon, Redis, PostgreSQL and the vector store reachable from places they should not be? | Network diagrams, container host access for scanning, agreed scan windows and an owner for the data stores | When Dify runs on Docker Compose or Kubernetes with shared networks or external data stores |
| AI-specific testing (prompt injection and agent behavior) | Can untrusted text in user input, retrieved documents or tool output change what an agent or workflow does? | Representative workflows and agents, the tools and knowledge bases they use, and the list of actions that must never run without review | When apps read external documents, call tools with side effects or act on behalf of users |
| Red teaming | Can a motivated attacker chain weaknesses across the app, the model path and the data to reach a business impact? | Defined objectives, rules of engagement, a named contact for stopping the test and agreement on which systems are in scope | When the outcome of a compromise matters more than a finding list and the team wants a realistic adversary view |
| Plugin and supply chain review | Were installed plugins signed, from trusted sources and reviewed before use? | A list of installed plugins, their sources, signature settings and the process for approving private plugins | Before enabling marketplace plugins or distributing internal plugins to other workspaces |
Questions teams ask before commissioning a Dify pen test
What drives the cost and effort of a Dify security review?
The main drivers are the size of the written scope: how many workspaces and apps are in play, which workflows and agents expose tools or knowledge bases, how many plugins and external integrations are enabled, and how many environments (for example staging and production) must be tested. Each of these adds test surface and review time. Cost also depends on whether the work is a configuration review, a grey-box or black-box test, or both, and on whether retesting is included. Fees are set from the agreed scope in writing, so ask for each component to be priced separately: scoping, testing, reporting, a fix-validation pass and any retainer for later retesting. Dify's own pricing page does not cover third-party assessment costs, so budget for this work as a separate line.
How long does a Dify pen test take from start to report?
The timeline depends on the written scope, so there is no standard duration to quote here. The main variables are the number of apps and integrations, how quickly test accounts and architecture information arrive, how many environments are covered, and how much time is needed for remediation discussions before a final report. Ask for milestones in the statement of work: kickoff, test window, draft findings, client review and final report. If a change freeze or a maintenance window limits when testing can run, state it at scoping so it is written into the schedule.
What does the client need to provide for a Dify security review?
Teams usually need to provide the following before testing starts:
- Test accounts for each role that matters, such as workspace admin, editor, end user and any web app user, so access control can be checked from each position.
- Architecture diagrams showing how the api, worker, web, plugin daemon, sandbox, SSRF proxy, databases, vector store and object storage connect, plus the public URLs in use.
- Read access to the deployed configuration, including a review of the
.envfile (with secrets handled under agreed rules) and the container or Kubernetes settings that apply. - Change windows and an escalation contact, so any load or intrusive test happens when operations can respond.
- A list of enabled plugins, external knowledge sources and outbound destinations, so the team can confirm which paths are in scope.
Confirm the handling rules for credentials and personal data in writing before access is granted.
Can our team assess a self-hosted Dify without an outside firm?
Yes, a capable internal team can do a useful first pass using the configuration documentation. The official environment variable reference describes settings such as SECRET_KEY, the SSRF_PROXY configuration and the metadata, file storage and vector store options, and the deployment docs explain the Docker Compose layout. Checking these against your own deployment is a reasonable starting point. An internal review tends to miss things that need adversarial testing: whether an HTTP node or tool can reach an internal address, whether prompt injection through a tool or knowledge document changes agent behavior, and whether the plugin install path accepts untrusted packages. An outside test adds an independent view and written evidence, but it does not replace ongoing internal ownership of configuration and patching.
Does a penetration test replace a compliance audit for Dify?
No. A penetration test gives findings about the security of a specific system at the time it is tested, and it can supply evidence that an auditor may review. It does not produce an audit report, an attestation or a certification. DeployOpen prepares systems, controls and evidence for compliance assessments, but independent auditors and certification bodies issue audit reports and certifications. Before commissioning testing, ask your auditor which evidence they expect, such as test scope, methodology, dates, findings and remediation records, and confirm that those items are captured in the statement of work.
How do we retest Dify after fixes and version upgrades?
Retesting should be written into the statement of work so it is planned rather than improvised. Start by mapping each finding to a fix, then decide what must be retested: the specific findings, the surfaces the change touched, and any area affected by a version change. Dify ships minor releases roughly monthly, according to the release history on GitHub, so a retest after an upgrade should check the release notes for security changes, such as sandbox network isolation or token-based service authentication, and confirm those controls still hold in your environment. Ask whether retesting is fixed-scope or covered by a retainer, and how the team will confirm each fix was applied and verified in the running system.
Security considerations that shape the test plan
A Dify assessment touches live workflows, stored credentials and model keys, so the way testing is run matters as much as what it finds. The cards below cover the operating rules to agree before the first request is sent, since each one changes what the test team can safely do.
Test against non-production data
A Dify instance usually holds prompts, knowledge base content, connected model keys and conversation logs that reflect real users. Ask for a staging copy built from the same version and configuration, and confirm with the client whether production testing is allowed at all. If production is in scope, agree on which apps, workspaces and knowledge bases the team may query and which must stay untouched, and record every request the testers send.
Keep model keys out of findings
Testers often see provider API keys during review of workflow nodes, model provider settings or environment files. Agree in writing that any key observed is recorded only by its last characters or location, never pasted into a report, ticket or chat. Confirm where evidence is stored and who can read it, because a finding that quotes a live key becomes a second exposure.
Rotate credentials exposed during testing
Any credential the team uses or uncovers, including service API keys, MCP server URLs with embedded credentials and provider keys, should be treated as compromised once it appears in test logs or notes. Plan the rotation with the owner of each key and confirm who performs it and how quickly it must happen. Rotation of a key is a different action from rotating the SECRET_KEY, and the test plan should keep the two separate.
Do not rotate SECRET_KEY casually
The SECRET_KEY signs session cookies and JWTs and encrypts OAuth credentials, and changing it after deployment logs out all users. A test that needs to prove a rotation weakness should run against a disposable copy, not the client's running instance. Ask the client to schedule any real rotation in a planned window, because a rotation that breaks OAuth plugin credentials can stop workflows that depend on them.
Back up before any remediation
Fixes to environment files, plugin settings, sandbox configuration or database schema can break a running Dify, and some changes are hard to reverse. The Dify Docker Compose documentation recommends a backup before every upgrade, and the same discipline applies to remediation. Confirm that the backup covers the metadata database, file storage and vector store, and test that a restore works before the first change is made.
How the written scope protects both sides
Engagements are fixed-scope. Before any testing starts, the targets, accounts, rules of engagement, reporting format and retest terms are agreed in writing, along with deliverables and timelines. For a self-hosted Dify, the target list is the part that most often goes wrong, so it should name the exact hostnames and ports: the console and web app domains, the public API base, any published MCP server URLs, file upload paths, and whether the internal services such as the sandbox, the SSRF proxy and the plugin daemon are in or out. Ask the provider to confirm which of these are in the test and which are only reviewed through configuration. Ongoing testing after a Dify minor release, which arrives roughly monthly according to the Dify releases page, can be covered by a retainer instead of repeated one-off scopes.
The accounts section is where a Dify test succeeds or stalls. Specify which roles the testers receive, for example a Community workspace admin, a normal member, an end user of a web app, and an unauthenticated visitor, and whether the tester may create workspaces, install plugins or import DSL files. Rules of engagement should state that the tester may send SSRF payloads to the HTTP, tool and external knowledge paths, may attempt sandbox escapes from Code and Template nodes in a non-production environment, and may not reach third-party model provider accounts or live customer knowledge base content. Agree in advance how the tester will handle any secret found in an environment file, and who is told within the same working day if a critical finding appears during the test.
Reporting format and retest terms decide whether the findings become fixes. Ask for a report that ties each finding to the component, the configuration key and the evidence, such as the .env setting, the proxy rule or the sign-up policy involved, with severity and a clear remediation step. Retest terms should say whether one retest round is included, how long after the fix it may occur, and whether the retest is covered by the fixed fee or quoted separately. Because the cost of a scope change is usually set by the number of added targets or roles, ask the provider to list in the written scope what triggers a change order. The reader holds the quote against this written scope, so every term that affects effort should appear there.
How DeployOpen scopes and delivers a Dify security review
DeployOpen runs security testing as a fixed-scope engagement inside your own infrastructure, so the review covers your actual Dify version, deployment layout and integrations. The work is agreed in writing before testing starts, and the findings come back as documented, prioritized items your team can act on.
How we scope
- Written scope lists the Dify version, deployment method and each exposed endpoint, console, API and web app included in testing.
- Scope names the SSRF, sandbox, plugin, prompt injection and env file areas to test, and which are excluded.
- Engagements are fixed-scope, so deliverables and timelines are agreed in writing before any testing begins.
How we build
- Testing runs against an agreed environment, with test accounts, roles and tool connections prepared with your team in advance.
- Testers attempt outbound requests to internal ranges, sandbox escapes and plugin installs, recording each path and its evidence.
- Prompt injection tests use the tools and knowledge bases your apps really use, with sensitive data kept out of the test set.
How we hand over
- Findings report the affected component, reproduction steps, severity and the configuration change or code fix that addresses each one.
- A retest step confirms which findings are closed after your team applies fixes, with the results recorded in the same report.
- Evidence and configuration notes are delivered so your team can repeat the checks after upgrades and new releases.
How we support
- Ongoing support is available as a retainer, covering upgrades, repeat testing and review of changes to Dify configuration.
- Retainers can align repeat tests with the monthly minor release cycle, so each new Dify version is checked before rollout.
- Ask for the retainer terms, response expectations and included retests in your written quote before work begins.

