Authentication and extension bypass in Faye
Description
Faye (NPM, RubyGem) versions greater than 0.5.0 and before 1.0.4, 1.1.3 and 1.2.5, has the potential for authentication bypass in the extension system. The vulnerability allows any client to bypass checks put in place by server-side extensions, by appending extra segments to the message channel. It is patched in versions 1.0.4, 1.1.3 and 1.2.5.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Faye (NPM, RubyGem) before 1.0.4, 1.1.3 and 1.2.5 allows authentication bypass by appending extra segments to meta channels.
Vulnerability
CVE-2020-11020 is an authentication bypass in Faye, a simple pub/sub messaging library for web applications. The vulnerability affects NPM and RubyGem versions greater than 0.5.0 and before 1.0.4, 1.1.3 and 1.2.5 [1]. It resides in Faye's extension system, which allows server-side extensions to check and authorize messages. A flaw in how the server recognizes special /meta/* channels means that any client can bypass these checks by appending extra segments to the message channel [2].
Exploitation
To exploit the vulnerability, a client sends a message to a channel like /meta/subscribe/x instead of the exact /meta/subscribe. The server treats any channel that is a prefix-match for one of the special channels (/meta/handshake, /meta/connect, /meta/subscribe, /meta/unsubscribe, /meta/disconnect) as though it were an exact match [2]. As a result, the extension's channel check (which often verifies only the exact channel name) is skipped, but the message is still processed as a valid subscription or connection event [1][2]. No authentication is required to perform this attack; any client can submit such a channel.
Impact
An unauthenticated attacker can subscribe to arbitrary channels without providing the necessary credentials required by server-side extensions [1][2]. This could allow unauthorized access to sensitive data streams or event notifications that the extension intended to protect, leading to information disclosure or privilege escalation within the pub/sub system.
Mitigation
The vulnerability has been patched in Faye versions 1.0.4, 1.1.3, and 1.2.5 [1][2]. Users of the affected versions should upgrade immediately. There are no known workarounds, as the fix addresses the core channel-matching logic. The vulnerability was reported on April 20, 2020, and existed since version 0.5.0 when extensions were introduced [2].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
fayeRubyGems | >= 0.5.0, < 1.0.4 | 1.0.4 |
fayeRubyGems | >= 1.1.0, < 1.1.3 | 1.1.3 |
fayeRubyGems | >= 1.2.0, < 1.2.5 | 1.2.5 |
Affected products
2- faye/Fayev5Range: >= 0.5.0, < 1.0.4
Patches
165d297d341b6Strict meta channel recognition in server
4 files changed · +98 −8
lib/faye/protocol/server.rb+12 −4 modified@@ -6,8 +6,6 @@ class Server include Logging include Extensible - META_METHODS = %w[handshake connect disconnect subscribe unsubscribe] - attr_reader :engine def initialize(options = {}) @@ -107,9 +105,9 @@ def handle(message, local = false, &callback) end def handle_meta(message, local, &callback) - method = Channel.parse(message['channel'])[1] + method = method_for(message) - unless META_METHODS.include?(method) + unless method response = make_response(message) response['error'] = Faye::Error.channel_forbidden(message['channel']) response['successful'] = false @@ -123,6 +121,16 @@ def handle_meta(message, local, &callback) end end + def method_for(message) + case message['channel'] + when Channel::HANDSHAKE then :handshake + when Channel::CONNECT then :connect + when Channel::SUBSCRIBE then :subscribe + when Channel::UNSUBSCRIBE then :unsubscribe + when Channel::DISCONNECT then :disconnect + end + end + def advize(response, connection_type) return unless [Channel::HANDSHAKE, Channel::CONNECT].include?(response['channel'])
spec/javascript/server/extensions_spec.js+36 −0 modified@@ -36,6 +36,42 @@ jstest.describe("Server extensions", function() { with(this) { }}) }}) + describe("with subscription auth installed", function() { with(this) { + before(function() { with(this) { + var extension = { + incoming: function(message, callback) { + if (message.channel === "/meta/subscribe" && !message.auth) { + message.error = "Invalid auth" + } + callback(message) + } + } + server.addExtension(extension) + }}) + + it("does not subscribe using the intended channel", function() { with(this) { + var message = { + channel: "/meta/subscribe", + clientId: "fakeclientid", + subscription: "/foo" + } + stub(engine, "clientExists").yields([true]) + expect(engine, "subscribe").exactly(0) + server.process(message, false, function() {}) + }}) + + it("does not subscribe using an extended channel", function() { with(this) { + var message = { + channel: "/meta/subscribe/x", + clientId: "fakeclientid", + subscription: "/foo" + } + stub(engine, "clientExists").yields([true]) + expect(engine, "subscribe").exactly(0) + server.process(message, false, function() {}) + }}) + }}) + describe("with an outgoing extension installed", function() { with(this) { before(function() { with(this) { var extension = {
spec/ruby/server/extensions_spec.rb+36 −0 modified@@ -40,6 +40,42 @@ def incoming(message, callback) end end + describe "with subscription auth installed" do + before do + extension = Class.new do + def incoming(message, callback) + if message["channel"] == "/meta/subscribe" and !message["auth"] + message["error"] = "Invalid auth" + end + callback.call(message) + end + end + server.add_extension(extension.new) + end + + it "does not subscribe using the intended channel" do + message = { + "channel" => "/meta/subscribe", + "clientId" => "fakeclientid", + "subscription" => "/foo" + } + engine.stub(:client_exists).and_yield(true) + engine.should_not_receive(:subscribe) + server.process(message, false) {} + end + + it "does not subscribe using an extended channel" do + message = { + "channel" => "/meta/subscribe/x", + "clientId" => "fakeclientid", + "subscription" => "/foo" + } + engine.stub(:client_exists).and_yield(true) + engine.should_not_receive(:subscribe) + server.process(message, false) {} + end + end + describe "with an outgoing extension installed" do before do extension = Class.new do
src/protocol/server.js+14 −4 modified@@ -13,8 +13,6 @@ var Class = require('../util/class'), Socket = require('./socket'); var Server = Class({ className: 'Server', - META_METHODS: ['handshake', 'connect', 'disconnect', 'subscribe', 'unsubscribe'], - initialize: function(options) { this._options = options || {}; var engineOpts = this._options.engine || {}; @@ -120,10 +118,10 @@ var Server = Class({ className: 'Server', }, _handleMeta: function(message, local, callback, context) { - var method = Channel.parse(message.channel)[1], + var method = this._methodFor(message), response; - if (array.indexOf(this.META_METHODS, method) < 0) { + if (method === null) { response = this._makeResponse(message); response.error = Error.channelForbidden(message.channel); response.successful = false; @@ -137,6 +135,18 @@ var Server = Class({ className: 'Server', }, this); }, + _methodFor: function(message) { + var channel = message.channel; + + if (channel === Channel.HANDSHAKE) return 'handshake'; + if (channel === Channel.CONNECT) return 'connect'; + if (channel === Channel.SUBSCRIBE) return 'subscribe'; + if (channel === Channel.UNSUBSCRIBE) return 'unsubscribe'; + if (channel === Channel.DISCONNECT) return 'disconnect'; + + return null; + }, + _advize: function(response, connectionType) { if (array.indexOf([Channel.HANDSHAKE, Channel.CONNECT], response.channel) < 0) return;
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-qpg4-4w7w-2mq5ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2020-11020ghsaADVISORY
- github.com/faye/faye/commit/65d297d341b607f3cb0b5fa6021a625a991cc30eghsax_refsource_MISCWEB
- github.com/faye/faye/security/advisories/GHSA-qpg4-4w7w-2mq5ghsax_refsource_CONFIRMWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/faye/CVE-2020-11020.ymlghsaWEB
News mentions
0No linked articles in our index yet.