AUTOMATIC1111/stable-diffusion-webui
Security
The webui is designed for single-user, local use. Most of its security posture follows from that assumption: the API is unauthenticated by default, the Extensions tab can install arbitrary code, and the launcher pip-installs without sandboxing. Running it in a multi-tenant or internet-exposed setting requires extra care.
Trust boundaries
| Boundary | Default posture | Hardening |
|---|---|---|
| Network | Listens on 127.0.0.1:7860 |
--listen, --port, reverse proxy, TLS |
| HTTP / browser | No auth, very permissive Gradio CORS removed (see below) | --gradio-auth, --api-auth, --cors-allow-origins |
| Extensions tab | Can install any URL the user pastes; runs install.py afterwards |
--enable-insecure-extension-access=false (default outside of localhost) |
| Custom code script | Disabled | Don't enable --allow-code on shared deployments |
Pickle deserialisation of .ckpt |
Restricted unpickler | --disable-safe-unpickle removes the restriction |
| Image URLs in API extras | Optional | api_enable_requests / api_forbid_local_requests |
CORS
By default Gradio enables an extremely permissive CORS policy. The webui explicitly strips the middleware in webui.py:
# gradio uses a very open CORS policy via app.user_middleware, which makes it possible for
# an attacker to trick the user into opening a malicious HTML page, which makes a request to the
# running web ui and do whatever the attacker wants, including installing an extension and
# running its code. We disable this here. Suggested by RyotaK.
app.user_middleware = [x for x in app.user_middleware if x.cls.__name__ != 'CORSMiddleware']If you need cross-origin access (e.g., a frontend on a different host calling the API), use --cors-allow-origins=https://my.frontend.example or --cors-allow-origins-regex=^https://(.*)\\.mycompany\\.com$. These are wired into modules/initialize_util.py setup_middleware().
Authentication
- Gradio UI auth —
--gradio-auth user:password,user2:password2enables HTTPBasic on the UI.--gradio-auth-path file.txtreads from a file (one entry per line). - API auth —
--api-auth user:password,...and--api-auth-filecover/sdapi/v1/*. Implementation inApi.auth()(modules/api/api.py) usessecrets.compare_digestto avoid timing leaks. - The two flags are independent; you can have a public UI and a locked-down API or vice versa, but for any public deployment both should be set.
There is no role-based access control. Any authenticated user can change settings, install extensions (if allowed), and trigger arbitrary generations.
Extensions are arbitrary code
The Extensions tab installs git repositories from URLs the user pastes. After cloning, it runs the extension's install.py (if present) and imports its scripts/*.py at boot. There is no sandboxing.
For shared deployments:
- Set
--enable-insecure-extension-access=false(the default when the webui isn't on127.0.0.1). - Optionally combine with
--disable-all-extensions all(lock to a known-good set). - Audit extensions in
extensions/periodically; treat them as full process privileges.
Pickle safety
Stable Diffusion checkpoints distributed in .ckpt format are pickle archives — they execute Python on load if not contained. modules/safe.py provides a restricted unpickler that only allows a known list of types (torch.nn.Parameter, basic Python builtins, numpy arrays, …). This is enabled by default.
--disable-safe-unpickle turns the restriction off — only do this when you trust the checkpoint absolutely. Prefer .safetensors files, which use a safe binary format and bypass pickle entirely.
Image URL fetching
/sdapi/v1/extra-single-image accepts an image as base64 or as a URL. Two settings gate the URL path:
api_enable_requests(default true) — allows URL inputs at all.api_forbid_local_requests(default true) — blocks URLs that resolve to private IP space, preventing trivial SSRF.
verify_url() in modules/api/api.py does the IP-resolution check using ipaddress.ip_address.is_global. It is a best-effort defence and assumes the resolver isn't lying; in hostile networks set api_enable_requests=false and require base64 inputs.
Custom code
scripts/custom_code.py runs user-supplied Python with the full webui in scope. It is disabled by default and requires --allow-code on the command line. The Settings page warns when the flag is set.
For any non-localhost deployment, do not enable this flag.
Localhost vs LAN vs Internet
| Topology | Settings | Comments |
|---|---|---|
| Localhost only | Defaults are fine | The address bar shows 127.0.0.1. |
| LAN sharing | --listen --port 7860 --gradio-auth ... --api-auth ... --enable-insecure-extension-access=false |
Add HTTPS via a reverse proxy. |
| Internet-exposed | All of the above + --cors-allow-origins, lock down extensions, consider --disable-all-extensions extra |
The extensions registry can be poisoned in principle; review before enabling new ones. |
--share (Gradio tunnel) |
Adds a public *.gradio.live URL |
Useful for one-off demos; auth is required because the tunnel is open. |
Reporting vulnerabilities
The README does not list a security contact. Issues affecting the project's security can be filed in private to the maintainer (@AUTOMATIC1111). Embargoed disclosure is informal — high-impact issues have historically been patched within days through a regular PR (the credit string for "Security advice — RyotaK" stems from this exact loop in late 2022).
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.
Previous
Deployment
Next
Reference