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
1515require ( 'dotenv' ) . config ( ) ;
@@ -130,6 +130,20 @@ const redirectURL = process.env.REDIRECT_URL || false;
130130const 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
214228if ( 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 ) {
0 commit comments