Skip to content

LINE webhook signature check trivially bypassed by omitting header #3439

Description

@houko

Severity

P0 — single-header omission grants full inbound webhook injection.

Files

  • crates/librefang-channels/src/line.rs:380-392

Finding

The handler reads x-line-signature with .unwrap_or(""), then guards the verify
call with if !signature.is_empty() && !verify_line_signature(...). An attacker
who simply omits the header passes both clauses and reaches the parse/dispatch
path with full webhook injection. Additionally the verifier is fed
serde_json::to_vec(&body.0) (re-serialized JSON), but LINE's HMAC is over the
raw bytes — implying signature verification is effectively unreachable for
legitimate LINE servers and only blocks forgers who bother to set the header.

Evidence

// crates/librefang-channels/src/line.rs:380-392
let signature = headers.get("x-line-signature").and_then(|v| v.to_str().ok()).unwrap_or("");
let body_bytes = serde_json::to_vec(&body.0).unwrap_or_default();
if !signature.is_empty() && !verify_line_signature(secret.as_bytes(), &body_bytes, signature) {
    warn!("LINE: invalid webhook signature");
    return axum::http::StatusCode::UNAUTHORIZED;
}

Suggested Fix

Switch to a Bytes body extractor (preserves raw bytes), require the header
(return 401 if absent), and verify HMAC-SHA256 over raw bytes — never
to_vec(parsed_json). Constant-time compare with subtle::ConstantTimeEq.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/channelsMessaging channel adaptersbugSomething isn't workinghas-prA pull request has been linked to this issuesecuritySecurity issuesseverity/criticalRemote-exploitable, data loss, or production-blocking

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions