CVE-2026-105963
Secret key base exposed in stack trace arguments on wrong-typed Plug.Crypto arguments
Externally-Generated Error Message Containing Sensitive Information vulnerability in plug_crypto in elixir-plug plug_crypto allows an unauthenticated remote attacker to expose the secret key base through application logs via a wrong-typed argument. Every public function in Plug.Crypto validates argument types in a function-head guard. A failed guard makes the Erlang VM raise FunctionClauseError with the actual argument values in the top stack frame, so the secret key base, a derived key, or the value an API key is compared against lands in that frame. The library's rescue blocks and Plug.Crypto.prune_args_from_stacktrace/1 only reach exceptions raised inside a function body, so they never see a guard failure. Plug.Cowboy and Bandit write the frame to the error log and attach it as crash_reason Logger metadata, which error trackers forward.
Two entry points take request data. Plug.Crypto.verify/4 exposes the secret key base when the token is neither a binary nor nil, which Phoenix.Token.verify/4 reaches because it forwards the token unguarded. Plug.Crypto.secure_compare/2 exposes the other operand when one side is not a binary, as happens when a missing request header yields nil. Reading the leaked value needs separate access to the logs or the error tracker, so the request alone does not disclose the secret to the attacker.
This issue affects plug: from 1.6.3 before 1.7.0; plug_crypto: from 1.0.0 before 2.2.1.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- High
- User interaction
- None
What an attacker can affect
- Confidentiality
- Low
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
5.1
Secret key base exposed in stack trace arguments on wrong-typed Plug.Crypto arguments
Externally-Generated Error Message Containing Sensitive Information vulnerability in plug_crypto in elixir-plug plug_crypto allows an unauthenticated remote attacker to expose the secret key base through application logs via a wrong-typed argument. Every public function in Plug.Crypto validates argument types in a function-head guard. A failed guard makes the Erlang VM raise FunctionClauseError with the actual argument values in the top stack frame, so the secret key base, a derived key, or the value an API key is compared against lands in that frame. The library's rescue blocks and Plug.Crypto.prune_args_from_stacktrace/1 only reach exceptions raised inside a function body, so they never see a guard failure. Plug.Cowboy and Bandit write the frame to the error log and attach it as crash_reason Logger metadata, which error trackers forward.
Two entry points take request data. Plug.Crypto.verify/4 exposes the secret key base when the token is neither a binary nor nil, which Phoenix.Token.verify/4 reaches because it forwards the token unguarded. Plug.Crypto.secure_compare/2 exposes the other operand when one side is not a binary, as happens when a missing request header yields nil. Reading the leaked value needs separate access to the logs or the error tracker, so the request alone does not disclose the secret to the attacker.
This issue affects plug: from 1.6.3 before 1.7.0; plug_crypto: from 1.0.0 before 2.2.1.
- Record
CVE-2026-105963- CVSS 4.0
- 5.1 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-532
- Updated
- 2026-11-02
EUVDEUVD-2026-108666
5.1
Externally-Generated Error Message Containing Sensitive Information vulnerability in plug_crypto in elixir-plug plug_crypto allows an unauthenticated remote attacker to expose the secret key base through application logs via a wrong-typed argument. Every public function in Plug.Crypto validates argument types in a function-head guard. A failed guard makes the Erlang VM raise FunctionClauseError with the actual argument values in the top stack frame, so the secret key base, a derived key, or the value an API key is compared against lands in that frame. The library's rescue blocks and Plug.Crypto.prune_args_from_stacktrace/1 only reach exceptions raised inside a function body, so they never see a guard failure. Plug.Cowboy and Bandit write the frame to the error log and attach it as crash_reason Logger metadata, which error trackers forward.
Two entry points take request data. Plug.Crypto.verify/4 exposes the secret key base when the token is neither a binary nor nil, which Phoenix.Token.verify/4 reaches because it forwards the token unguarded. Plug.Crypto.secure_compare/2 exposes the other operand when one side is not a binary, as happens when a missing request header yields nil. Reading the leaked value needs separate access to the logs or the error tracker, so the request alone does not disclose the secret to the attacker.
This issue affects plug: from 1.6.3 before 1.7.0; plug_crypto: from 1.0.0 before 2.2.1.
- Record
EUVD-2026-108666- CVSS 4.0
- 5.1 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
NVD
5.1
Externally-Generated Error Message Containing Sensitive Information vulnerability in plug_crypto in elixir-plug plug_crypto allows an unauthenticated remote attacker to expose the secret key base through application logs via a wrong-typed argument. Every public function in Plug.Crypto validates argument types in a function-head guard. A failed guard makes the Erlang VM raise FunctionClauseError with the actual argument values in the top stack frame, so the secret key base, a derived key, or the value an API key is compared against lands in that frame. The library's rescue blocks and Plug.Crypto.prune_args_from_stacktrace/1 only reach exceptions raised inside a function body, so they never see a guard failure. Plug.Cowboy and Bandit write the frame to the error log and attach it as crash_reason Logger metadata, which error trackers forward.
Two entry points take request data. Plug.Crypto.verify/4 exposes the secret key base when the token is neither a binary nor nil, which Phoenix.Token.verify/4 reaches because it forwards the token unguarded. Plug.Crypto.secure_compare/2 exposes the other operand when one side is not a binary, as happens when a missing request header yields nil. Reading the leaked value needs separate access to the logs or the error tracker, so the request alone does not disclose the secret to the attacker.
This issue affects plug: from 1.6.3 before 1.7.0; plug_crypto: from 1.0.0 before 2.2.1.
- Record
CVE-2026-105963- CVSS 4.0
- 5.1 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-532
- Updated
- 2026-11-02
OSV
5.1
Secret key base exposed in stack trace arguments on wrong-typed Plug.Crypto arguments
Externally-Generated Error Message Containing Sensitive Information vulnerability in plug_crypto in elixir-plug plug_crypto allows an unauthenticated remote attacker to expose the secret key base through application logs via a wrong-typed argument. Every public function in Plug.Crypto validates argument types in a function-head guard. A failed guard makes the Erlang VM raise FunctionClauseError with the actual argument values in the top stack frame, so the secret key base, a derived key, or the value an API key is compared against lands in that frame. The library's rescue blocks and Plug.Crypto.prune_args_from_stacktrace/1 only reach exceptions raised inside a function body, so they never see a guard failure. Plug.Cowboy and Bandit write the frame to the error log and attach it as crash_reason Logger metadata, which error trackers forward.
Two entry points take request data. Plug.Crypto.verify/4 exposes the secret key base when the token is neither a binary nor nil, which Phoenix.Token.verify/4 reaches because it forwards the token unguarded. Plug.Crypto.secure_compare/2 exposes the other operand when one side is not a binary, as happens when a missing request header yields nil. Reading the leaked value needs separate access to the logs or the error tracker, so the request alone does not disclose the secret to the attacker.
This issue affects plug: from 1.6.3 before 1.7.0; plug_crypto: from 1.0.0 before 2.2.1.
- Record
CVE-2026-105963- CVSS 4.0
- 5.1 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-532
- Updated
- 2026-11-02
OSVEEF-CVE-2026-105963
5.1
Secret key base exposed in stack trace arguments on wrong-typed Plug.Crypto arguments
Summary
Externally-Generated Error Message Containing Sensitive Information vulnerability in plug_crypto in elixir-plug plug_crypto allows an unauthenticated remote attacker to expose the secret key base through application logs via a wrong-typed argument. Every public function in Plug.Crypto validates argument types in a function-head guard. A failed guard makes the Erlang VM raise FunctionClauseError with the actual argument values in the top stack frame, so the secret key base, a derived key, or the value an API key is compared against lands in that frame. The library's rescue blocks and Plug.Crypto.prune_args_from_stacktrace/1 only reach exceptions raised inside a function body, so they never see a guard failure. Plug.Cowboy and Bandit write the frame to the error log and attach it as crash_reason Logger metadata, which error trackers forward.
Two entry points take request data. Plug.Crypto.verify/4 exposes the secret key base when the token is neither a binary nor nil, which Phoenix.Token.verify/4 reaches because it forwards the token unguarded. Plug.Crypto.secure_compare/2 exposes the other operand when one side is not a binary, as happens when a missing request header yields nil. Reading the leaked value needs separate access to the logs or the error tracker, so the request alone does not disclose the secret to the attacker.
This issue affects plug: from 1.6.3 before 1.7.0; plug_crypto: from 1.0.0 before 2.2.1.
Details
1. Type checks in function-head guards. Every public function in Plug.Crypto validates its arguments in a head guard, for example secure_compare/2 with when is_binary(left) and is_binary(right) and verify/4 with when is_binary(key_base) and is_binary(salt) and is_binary(token).
2. The VM builds the error, not the library. When no clause matches, the Erlang VM raises FunctionClauseError and the top stack frame holds the actual argument values. The library's rescue ... reraise blocks and Plug.Crypto.prune_args_from_stacktrace/1 only apply to exceptions raised inside a function body, so a guard failure never reaches them. In plug_crypto the same commit that added prune_args_from_stacktrace/1 also added these guards.
3. Which value leaks. verify/4 and sign/4 put the secret key base in the frame. decrypt/4 has no is_binary(token) guard on its main clause, so the key is derived first and the failing guard belongs to MessageEncryptor.decrypt/4, placing the derived 32-byte key in the frame. secure_compare/2 places the other operand in the frame, which is the server-side secret when a request value is nil.
4. The frame reaches logs and error trackers. Plug.Cowboy and Bandit write the formatted frame, arguments included, to the error log and attach it as crash_reason Logger metadata. Error trackers forward it; Sentry's handler sends crash reports by default and serializes frame arguments as vars.
Proof of concept
- Run an application that depends on
plug_cryptoand exposes a route callingPhoenix.Token.verify(secret_key_base, "user auth", conn.params["token"]), plus a route callingPlug.Crypto.secure_compare(List.first(get_req_header(conn, "x-api-key")), api_key). - Send
GET /verify?token[]=x.Plug.Parsersyields a list for the bracket syntax, so the token argument is a list. - Observe the 500 response and read the error log. The top frame prints
Plug.Crypto.verify("SECRET_KEY_BASE_...", "user auth", ["x"], [])with the secret key base in full, and the same frame is attached ascrash_reasonLogger metadata. - Send
GET /apiwith nox-api-keyheader. The header lookup yields nil, and the frame printsPlug.Crypto.secure_compare(nil, "<configured API key>"). - A JSON body of
{"token": 123}to the first route gives the same result with an integer argument.
Impact
An unauthenticated attacker sends one request with a wrong-typed parameter, or omits a header, and the secret key base or the compared…
- Record
EEF-CVE-2026-105963- CVSS 4.0
- 5.1 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-532
- Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-105963
5.1
Externally-Generated Error Message Containing Sensitive Information vulnerability in plug_crypto in elixir-plug plug_crypto allows an unauthenticated remote attacker to expose the secret key base through application logs via a wrong-typed argument. Every public function in Plug.Crypto validates argument types in a function-head guard. A failed guard makes the Erlang VM raise FunctionClauseError with the actual argument values in the top stack frame, so the secret key base, a derived key, or the value an API key is compared against lands in that frame. The library's rescue blocks and Plug.Crypto.prune_args_from_stacktrace/1 only reach exceptions raised inside a function body, so they never see a guard failure. Plug.Cowboy and Bandit write the frame to the error log and attach it as crash_reason Logger metadata, which error trackers forward. Two entry points take request data. Plug.Crypto.verify/4 exposes the secret key base when the token is neither a binary nor nil, which Phoenix.Token.verify/4 reaches because it forwards the token unguarded. Plug.Crypto.secure_compare/2 exposes the other operand when one side is not a binary, as happens when a missing request header yields nil. Reading the leaked value needs separate access to the logs or the error tracker, so the request alone does not disclose the secret to the attacker. This issue affects plug: from 1.6.3 before 1.7.0; plug_crypto: from 1.0.0 before 2.2.1.
- Record
UBUNTU-CVE-2026-105963- CVSS 4.0
- 5.1 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-05
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
plugHex |
1.6.3 up to 1.7.0, plus listed versions 1.6.4 |
1.7.0 |
plug_cryptoHex |
1.0.0 up to 2.2.1, plus listed versions 1.1.01.1.11.1.21.2.01.2.1 Show 8 moreShow less1.2.21.2.31.2.41.2.52.0.02.1.02.1.12.2.0 |
2.2.1 |
sonic-piUbuntu:16.04:LTS |
All versions, plus listed versions 2.9.0~repack-6 |
None known |
sonic-piUbuntu:18.04:LTS |
All versions, plus listed versions 2.10.0~repack-22.10.0~repack-2.1 |
None known |
sonic-piUbuntu:20.04:LTS |
All versions, plus listed versions 2.10.0~repack-2.12.10.0~repack-2.1build12.10.0~repack-2.1build2 |
None known |
sonic-piUbuntu:22.04:LTS |
All versions, plus listed versions 3.2.2~repack-7ubuntu1 |
None known |
sonic-piUbuntu:24.04:LTS |
All versions, plus listed versions 3.2.2~repack-83.2.2~repack-8ubuntu13.2.2~repack-8ubuntu23.2.2~repack-8ubuntu3 |
None known |
sonic-piUbuntu:26.10 |
All versions, plus listed versions 3.2.2~repack-12.1 |
None known |
sonic-piUbuntu:26.04:LTS |
All versions, plus listed versions 3.2.2~repack-123.2.2~repack-12.13.2.2~repack-12.1ubuntu0.26.04.1 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, aliases, facts, references | OSV | |
| first indexed | CVE List, EUVD, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-105963 is a medium vulnerability (CVSS 5.1) in Elixir-plug Plug, published October 23, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.51%, the 43rd percentile. Fixed in plug 1.7.0.
References
github.com/elixir-plug/plug/security/advisories/GHSA-ffw9-w85g-f2vh
cna.erlef.org/cves/CVE-2026-105963.html
osv.dev/vulnerability/EEF-CVE-2026-105963
github.com/elixir-plug/plug/commit/56000c33d8a0a2fbac491f8be06b9c5112bc2276
github.com/elixir-plug/plug/commit/aeaa90e76b73ea90c3ecca9aae16fce71bd2a8d4
github.com/elixir-plug/plug_crypto/commit/2d078b7b8264c565f0ff71235c515f4f46882dcf
Show 11 moreShow less
github.com/elixir-plug/plug_crypto/commit/981887493dada0fce7003c61861c00dfbb0fc8c5
github.com
repo.hex.pm
github.com/CVEProject/cvelistV5/tree/main/cves/2026/105xxx/CVE-2026-105963.json
nvd.nist.gov/vuln/detail/CVE-2026-105963
github.com/elixir-plug/plug
github.com/elixir-plug/plug_crypto
hex.pm/packages/plug
hex.pm/packages/plug_crypto
ubuntu.com/security/CVE-2026-105963
cve.org/CVERecord?id=CVE-2026-105963
Left out here: the rest of 1 source description. The JSON record has them.