Skip to content

Commit 0657677

Browse files
[mirotalkc2c] - fix(oidc): validate Host header against allowlist when baseUrlDynamic is enabled
1 parent 692999f commit 0657677

5 files changed

Lines changed: 58 additions & 7 deletions

File tree

‎.env.template‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# ====================================================
2-
# MiroTalk C2C v.1.3.03 - Environment Configuration
2+
# MiroTalk C2C v.1.3.04 - Environment Configuration
33
# ====================================================
44

55
# App environment
@@ -86,7 +86,8 @@ EMAIL_SEND_TO=test@mirotalk.com
8686
# For those seeking an open-source solution, check out: https://github.com/panva/node-oidc-provider
8787

8888
OIDC_ENABLED=false # true or false
89-
OIDC_BASE_URL_DYNAMIC=false # true or false
89+
OIDC_BASE_URL_DYNAMIC=false # Derive OIDC baseURL from incoming Host header (true|false). Requires OIDC_ALLOWED_DYNAMIC_BASE_URLS
90+
OIDC_ALLOWED_DYNAMIC_BASE_URLS= # Comma-separated allowlist of origins permitted when OIDC_BASE_URL_DYNAMIC=true (e.g. https://c2c.example.com,https://c2c.eu.example.com)
9091
OIDC_ISSUER_BASE_URL='https://server.example.com'
9192
OIDC_BASE_URL='http://localhost:8080' # https://c2c.mirotalk.com
9293
OIDC_CLIENT_ID='ClientID'

‎backend/server.js‎

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* @license For private project or commercial purposes contact us at: license.mirotalk@gmail.com or purchase it directly via Code Canyon:
1010
* @license https://codecanyon.net/item/mirotalk-c2c-webrtc-real-time-cam-2-cam-video-conferences-and-screen-sharing/43383005
1111
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
12-
* @version 1.3.03
12+
* @version 1.3.04
1313
*/
1414

1515
require('dotenv').config();
@@ -130,6 +130,20 @@ const redirectURL = process.env.REDIRECT_URL || false;
130130
const OIDC = {
131131
enabled: process.env.OIDC_ENABLED ? getEnvBoolean(process.env.OIDC_ENABLED) : false,
132132
baseUrlDynamic: process.env.OIDC_BASE_URL_DYNAMIC ? getEnvBoolean(process.env.OIDC_BASE_URL_DYNAMIC) : false,
133+
/*
134+
* When `baseUrlDynamic` is true, the OIDC baseURL (and therefore the redirect_uri
135+
* sent to the IdP) is derived from the incoming `Host` header. To prevent
136+
* Host-header injection from redirecting authorization codes to an attacker,
137+
* list every origin the server is allowed to serve here (full origin, no path).
138+
* The static `config.baseURL` is always trusted and does not need to be repeated.
139+
*
140+
* Example: OIDC_ALLOWED_DYNAMIC_BASE_URLS='https://c2c.example.com,https://c2c.eu.example.com'
141+
*/
142+
allowedDynamicBaseURLs: process.env.OIDC_ALLOWED_DYNAMIC_BASE_URLS
143+
? process.env.OIDC_ALLOWED_DYNAMIC_BASE_URLS.split(',')
144+
.map((u) => u.trim())
145+
.filter(Boolean)
146+
: [],
133147
config: {
134148
issuerBaseURL: process.env.OIDC_ISSUER_BASE_URL,
135149
clientID: process.env.OIDC_CLIENT_ID,
@@ -212,6 +226,27 @@ app.use((err, req, res, next) => {
212226

213227
// OpenID Connect - Dynamically set baseURL based on incoming host and protocol
214228
if (OIDC.enabled) {
229+
// Build an allowlist of origins permitted to be used as the OIDC baseURL.
230+
// This prevents Host-header injection from rewriting the redirect_uri
231+
// (see: https://portswigger.net/web-security/host-header).
232+
// Sources, in order of precedence:
233+
// 1. OIDC.allowedDynamicBaseURLs (string[] of full origins from OIDC_ALLOWED_DYNAMIC_BASE_URLS)
234+
// 2. OIDC.config.baseURL (always trusted)
235+
const configuredAllowlist = Array.isArray(OIDC.allowedDynamicBaseURLs) ? OIDC.allowedDynamicBaseURLs : [];
236+
const allowedOrigins = new Set(
237+
[OIDC.config?.baseURL, ...configuredAllowlist]
238+
.filter(Boolean)
239+
.map((u) => {
240+
try {
241+
return new URL(u).origin;
242+
} catch {
243+
return null;
244+
}
245+
})
246+
.filter(Boolean)
247+
);
248+
249+
// Cache auth middleware per host+protocol to avoid re-creating on every request
215250
const authMiddlewareCache = new Map();
216251

217252
const getAuthMiddleware = (host, protocol) => {
@@ -236,6 +271,21 @@ if (OIDC.enabled) {
236271
app.use((req, res, next) => {
237272
const host = req.headers.host;
238273
const protocol = req.protocol === 'https' ? 'https' : 'http';
274+
const cacheKey = `${protocol}://${host}`;
275+
276+
// Reject Host headers that are not in the configured allowlist when
277+
// baseUrlDynamic is enabled. Without this, an attacker can force the
278+
// OIDC library to issue a redirect_uri pointing to a domain they
279+
// control and steal the authorization code.
280+
if (OIDC.baseUrlDynamic && !allowedOrigins.has(cacheKey)) {
281+
log.warn('OIDC Host header not in allowlist - rejecting request', {
282+
host,
283+
origin: cacheKey,
284+
allowed: [...allowedOrigins],
285+
});
286+
return res.status(400).send('Bad Request: invalid Host header');
287+
}
288+
239289
try {
240290
getAuthMiddleware(host, protocol)(req, res, next);
241291
} catch (err) {

‎frontend/js/client.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* @license For private project or commercial purposes contact us at: license.mirotalk@gmail.com or purchase it directly via Code Canyon:
1010
* @license https://codecanyon.net/item/mirotalk-c2c-webrtc-real-time-cam-2-cam-video-conferences-and-screen-sharing/43383005
1111
* @author Miroslav Pejic - miroslav.pejic.85@gmail.com
12-
* @version 1.3.03
12+
* @version 1.3.04
1313
*/
1414

1515
const roomId = new URLSearchParams(window.location.search).get('room');

‎package-lock.json‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mirotalkc2c",
3-
"version": "1.3.03",
3+
"version": "1.3.04",
44
"description": "A free WebRTC Cam-2-Cam browser-based video calls",
55
"main": "server.js",
66
"scripts": {

0 commit comments

Comments
 (0)