Back to Blog
critical SEVERITY6 min read

How Missing Authentication on DELETE Endpoints Happens in Python aiohttp and How to Fix It

A critical missing authentication vulnerability in `pz_minimax.py` allowed any network-connected user to delete stored MiniMax prompts via the `DELETE /pz_easyuse/minimax-prompts/{index}` endpoint without any access control. An attacker could enumerate sequential indices to wipe all user-created prompts from the shared JSON file. The fix restricts the DELETE endpoint to localhost-only requests by checking `request.remote` against loopback addresses.

O
By Orbis AppSec
Published September 6, 2026Reviewed September 6, 2026

Answer Summary

This is a missing authentication / broken access control vulnerability (CWE-306) in a Python aiohttp web route within `pz_minimax.py`, a ComfyUI plugin file. The `delete_minimax_prompt` function at line 79 accepted DELETE requests from any network client without verifying the caller's identity or origin. The fix adds a `_is_local_request()` guard that returns HTTP 403 Forbidden for any request not originating from `127.0.0.1` or `::1`, restricting destructive operations to the local machine.

Vulnerability at a Glance

cweCWE-306
fixAdded a localhost-only guard (`_is_local_request`) that rejects non-loopback requests with HTTP 403
riskAny network user can delete all stored prompts without authentication
languagePython (aiohttp)
root causeThe DELETE route handler had no access control check before performing destructive file operations
vulnerabilityMissing Authentication on Destructive Endpoint

Introduction

The pz_minimax.py file handles MiniMax prompt management for a ComfyUI plugin, exposing HTTP endpoints that let users create, update, and delete prompts stored in a shared JSON file. But a flaw in the delete_minimax_prompt function at line 79 created a serious security risk: the DELETE /pz_easyuse/minimax-prompts/{index} route had zero authentication or access control, meaning any device on the same network could silently delete every stored prompt.

This isn't a theoretical concern. ComfyUI instances are frequently exposed on local networks — and sometimes the open internet — for collaborative AI workflows. A single unauthenticated DELETE endpoint turns a productivity tool into a target for data destruction.

The Vulnerability Explained

What the code did (before the fix)

Here's the vulnerable endpoint as it existed at line 79 of pz_minimax.py:

@PromptServer.instance.routes.delete("/pz_easyuse/minimax-prompts/{index}")
async def delete_minimax_prompt(request):
    try:
        index = int(request.match_info["index"])
    except ValueError:
        ...

The function immediately parses the {index} path parameter and proceeds to delete the prompt at that position from a shared JSON file. There is no check on:

  • Who is making the request (no authentication)
  • Where the request originates (no IP filtering)
  • Whether the caller owns the prompt (no ownership verification)

How an attacker exploits this

The attack is trivially simple and requires no special tools — just curl or a basic script:

# Enumerate and delete all prompts by sequential index
for i in $(seq 0 100); do
  curl -X DELETE http://<comfyui-host>:8188/pz_easyuse/minimax-prompts/$i
done

Because prompts are stored in a shared JSON file and indices are sequential integers starting from 0, an attacker can:

  1. Enumerate all prompts by sending DELETE requests with incrementing indices
  2. Destroy every stored prompt in seconds
  3. Repeat the attack silently — there's no logging or rate limiting

This is a classic 2-step exploitation chain: network access → data destruction. The scanner flagged it as "Likely exploitable" because the endpoint is directly reachable over HTTP with no intervening middleware.

Real-world impact

For ComfyUI users, MiniMax prompts represent carefully crafted AI generation configurations. Losing them means:

  • Lost work: Hours of prompt engineering wiped out
  • No recovery: If no external backup exists, the prompts are gone permanently
  • Silent attack: Users may not realize prompts were deleted until they try to use them
  • Lateral risk: If this endpoint lacks auth, other endpoints in the same plugin may also be unprotected

The Fix

The PR introduces a focused, minimal change: a localhost-only guard function that blocks all non-local requests to the DELETE endpoint.

New helper function

A new _is_local_request function was added at line 79:

def _is_local_request(request):
    return request.remote in ("127.0.0.1", "::1")

This checks the request.remote property (which aiohttp populates with the client's IP address) against the IPv4 loopback (127.0.0.1) and IPv6 loopback (::1) addresses.

Before vs. After

Before (vulnerable):

@PromptServer.instance.routes.delete("/pz_easyuse/minimax-prompts/{index}")
async def delete_minimax_prompt(request):
    try:
        index = int(request.match_info["index"])
    except ValueError:
        ...

After (fixed):

def _is_local_request(request):
    return request.remote in ("127.0.0.1", "::1")


@PromptServer.instance.routes.delete("/pz_easyuse/minimax-prompts/{index}")
async def delete_minimax_prompt(request):
    if not _is_local_request(request):
        return web.json_response({"error": "Forbidden"}, status=403)

    try:
        index = int(request.match_info["index"])
    except ValueError:
        ...

Why this works

  1. Early rejection: The guard runs before any business logic, so unauthorized requests never reach the prompt deletion code
  2. HTTP 403 response: Returns a proper Forbidden status code with a JSON error body, maintaining API consistency
  3. Covers both IP versions: Checks both 127.0.0.1 (IPv4) and ::1 (IPv6) to handle dual-stack configurations
  4. Minimal blast radius: The change is scoped to exactly one file and one code path — the destructive DELETE endpoint — preserving all existing behavior for legitimate local users
  5. Reusable guard: The _is_local_request helper is extracted as a standalone function, making it easy to apply to other sensitive endpoints in the same file

Design consideration

For a local development tool like ComfyUI, localhost restriction is an appropriate security boundary. The tool is designed to run on a user's machine, so restricting destructive operations to local requests prevents network-based attacks while maintaining the expected user experience.

Prevention & Best Practices

1. Apply authentication to all state-changing endpoints

Every endpoint that modifies data (POST, PUT, DELETE, PATCH) should have explicit access control. In aiohttp, you can use middleware to enforce this globally:

@web.middleware
async def auth_middleware(request, handler):
    if request.method in ("DELETE", "PUT", "PATCH"):
        if not _is_local_request(request):
            return web.json_response({"error": "Forbidden"}, status=403)
    return await handler(request)

2. Audit all routes in your application

If one endpoint lacked authentication, others likely do too. Run a systematic audit:

# Find all route registrations in the codebase
grep -rn "@PromptServer.instance.routes" *.py

3. Use defense in depth

Don't rely on a single control. Layer your defenses:

  • Network-level: Bind to 127.0.0.1 instead of 0.0.0.0 if the service is local-only
  • Application-level: IP checks, authentication tokens, CSRF protection
  • Data-level: Backups, soft deletes instead of hard deletes, audit logging

4. Consider CSRF protection for browser-accessible APIs

If the ComfyUI interface is accessed via a browser, a malicious webpage could trigger DELETE requests using the user's browser (which would originate from localhost). Adding CSRF tokens provides an additional layer of protection.

5. Reference standards

  • OWASP Top 10 A01:2021: Broken Access Control
  • CWE-306: Missing Authentication for Critical Function
  • CWE-862: Missing Authorization

Key Takeaways

  • The delete_minimax_prompt function at line 79 of pz_minimax.py had no access control, allowing any network client to delete prompts by guessing sequential integer indices — a trivially exploitable pattern.
  • Sequential integer IDs as the sole resource identifier make enumeration attacks effortless — an attacker doesn't need to know anything about the data to delete all of it.
  • Localhost-only restriction via request.remote checking is an effective baseline for local development tools, but should be combined with additional controls for any network-exposed deployment.
  • Extracting the _is_local_request helper as a reusable function makes it easy to consistently apply the same guard to other sensitive endpoints like update_minimax_prompt.
  • Destructive operations (DELETE) deserve the strictest access controls — they should be the first endpoints audited in any security review.

How Orbis AppSec Detected This

  • Source: HTTP DELETE request from any network-connected client to /pz_easyuse/minimax-prompts/{index}, with the {index} path parameter as attacker-controlled input
  • Sink: The prompt deletion logic inside delete_minimax_prompt() in pz_minimax.py:79, which removes entries from the shared JSON prompt store
  • Missing control: No authentication, no IP-based access restriction, and no ownership verification before executing the destructive delete operation
  • CWE: CWE-306 — Missing Authentication for Critical Function
  • Fix: Added a _is_local_request() guard that checks request.remote against loopback addresses and returns HTTP 403 Forbidden for all non-local requests

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

This vulnerability is a textbook example of how a single missing access control check can turn a useful feature into an attack vector. The delete_minimax_prompt endpoint in pz_minimax.py did exactly what it was supposed to do — delete prompts — but it did it for anyone who asked, not just authorized users.

The fix is small (6 lines of code) but meaningful: it establishes a security boundary that didn't exist before. For developers building similar local-first tools with HTTP APIs, the lesson is clear: every state-changing endpoint needs explicit access control, even if you think the tool is only used locally. Networks are shared, ports get forwarded, and assumptions about who can reach your service are often wrong.

References

Frequently Asked Questions

What is missing authentication on a critical function?

It occurs when a web endpoint that performs sensitive or destructive operations (like deleting data) does not verify the identity or authorization of the requester before executing the action.

How do you prevent missing authentication in Python aiohttp?

Apply authentication middleware or per-route guards that verify the caller's identity (e.g., tokens, session cookies, or IP-based restrictions) before allowing access to sensitive endpoints. For local-only tools, restrict access to loopback addresses.

What CWE is missing authentication on a critical function?

CWE-306: Missing Authentication for Critical Function. It is listed in the OWASP Top 10 under A01:2021 – Broken Access Control.

Is IP-based restriction enough to prevent unauthorized access?

IP-based restriction to localhost is a reasonable baseline for local development tools like ComfyUI plugins, but for production or multi-user deployments, proper authentication tokens or session-based auth should be used alongside network-level controls.

Can static analysis detect missing authentication?

Yes. Static analysis and AI-powered scanners can flag route handlers that perform destructive operations (DELETE, PUT) without any authentication or authorization checks in their call chain.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2

Related Articles

critical

How Information Disclosure Vulnerabilities Happen in Python APIs and How to Fix It

A critical information disclosure vulnerability in the Hermes plugin dashboard API was exposing sensitive filesystem paths, credential file locations, and configuration details without authentication. The fix redacts this sensitive information from API responses, replacing absolute paths with boolean status indicators to prevent attackers from locating and targeting credential files.

critical

How Missing Authentication on DELETE Endpoints Happens in Node.js Express and How to Fix It

A critical authentication bypass vulnerability was discovered in the skill-cabinet server where the DELETE /api/skills/:id endpoint allowed any unauthenticated user to delete arbitrary skills from the filesystem. The fix implements loopback origin validation to ensure only requests from localhost can perform destructive operations, while also consolidating delete functionality into a single, protected endpoint.

critical

How Rate Limiting Vulnerabilities Happen in Next.js API Routes and How to Fix It

A critical rate limiting vulnerability in the `/api/claim` endpoint allowed attackers to exhaust the shared GitHub API quota by sending unlimited rapid requests. While the `/api/records` endpoint had proper throttling, the claim route only checked for GitHub rate limiting responses but implemented no per-user rate limiting, enabling abuse of the shared `REGISTRY_TOKEN` quota.

high

How Cross-Site Request Forgery (CSRF) happens in Express.js and how to fix it

A semgrep audit flagged `devboard/server/index.js` for lacking any CSRF middleware, meaning every state-changing route (`POST`, `PUT`, `DELETE` under `/api/*`) could be triggered by a forged cross-origin request riding on a victim's session cookie. The fix wires in `cookie-parser` and `csurf` right after body parsing, so every mutating request now requires a valid, per-session CSRF token before it reaches route handlers.

critical

How missing authorization and code injection happen in Mindustry JavaScript mods and how to fix it

A `TapEvent` handler in `scripts/CommandBlock.js` exposed a full administrative command palette — including a `run-javascript` command that piped player-supplied text straight into `new Function(text)()` — behind nothing more than a team-membership check. Any player who happened to share a team with the block could execute arbitrary JavaScript (and, through Rhino's Java bridge, arbitrary host code) inside the game runtime. The fix adds an explicit `if (!e.player.admin) return;` guard at the top

critical

How Path Traversal in basic-ftp Leads to File Overwrite Attacks and How to Fix It

CVE-2026-27699 is a critical path traversal vulnerability in basic-ftp versions before 5.3.1 that allows attackers to overwrite arbitrary files on the system by crafting malicious file paths. This vulnerability was fixed by upgrading basic-ftp and enforcing strict version constraints across dependent packages. Understanding this attack and its mitigation is essential for developers using FTP libraries in production environments.