red5-server icon indicating copy to clipboard operation
red5-server copied to clipboard

Clickjacking in Red5 Server and global Web.xml is not found

Open LakshmiPhani7680 opened this issue 1 year ago • 9 comments

Issue

Short description

Brief description of what happened We are trying to add headers in red5 media server to avoid clickjacking, but unfortunately the headers aren't getting reflected, please get us a way to avoid the clickjacking vulnerability and how to add headers in red5 media server.

Environment

[] Operating system and version: [] Java version: jdk8 we are using in red5 [] Red5 version: No idea how to find it.

LakshmiPhani7680 avatar Jul 15 '24 06:07 LakshmiPhani7680

这是来自QQ邮箱的假期自动回复邮件。   您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。

chushiyun2015 avatar Jul 15 '24 06:07 chushiyun2015

@LakshmiPhani7680 could you provide more information on the exploit?

mondain avatar Jul 26 '24 12:07 mondain

Hi @mondain, Thank you for the response, In general if i want to add request or response headers for the red5 server where i need to add? web.xml file in /webapps/vod/ somewhere in it right? or anywhere else? cause the red5 server which we are using has this clickjacking vulnerability because it doesn't have the desired headers to avoid this vulnerability.

LakshmiPhani7680 avatar Jul 26 '24 13:07 LakshmiPhani7680

Would you mind linking to an article or incident report that I can review?

On Fri, Jul 26, 2024, 06:17 LakshmiPhani7680 @.***> wrote:

Hi @mondain https://github.com/mondain, Thank you for the response, In general if i want to add request or response headers for the red5 server where i need to add? web.xml file in /webapps/vod/ somewhere in it right? or anywhere else? cause the red5 server which we are using has this clickjacking vulnerability because it doesn't have the desired headers to avoid this vulnerability.

— Reply to this email directly, view it on GitHub https://github.com/Red5/red5-server/issues/353#issuecomment-2252747736, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAD4UXICH2VOHLDRRNYQHADZOJEAHAVCNFSM6AAAAABK35A4JOVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENJSG42DONZTGY . You are receiving this because you were mentioned.Message ID: @.***>

mondain avatar Jul 26 '24 17:07 mondain

Hi @mondain, Yeah sure, will send on monday. But can you please tell me in general how to add request/response headers like X-Frame-Options for Red5 media server? Thank you

LakshmiPhani7680 avatar Jul 27 '24 07:07 LakshmiPhani7680

The default JEE container used in Red5 is Tomcat; so you'll want to look at that specifically. If I wanted to inject headers from the server side, I'd add a context listener or servlet filter.

mondain avatar Jul 28 '24 13:07 mondain

Hi @mondain , Thank you for the response, So without tomcat red5 won't work? or only the headers related?

LakshmiPhani7680 avatar Jul 28 '24 13:07 LakshmiPhani7680

The global web.xml for Tomcat is not used in Red5; each app has its own web.xml, so if you cannot sort it out there, you'll have to add a context listener or servlet filter.

mondain avatar Jul 28 '24 14:07 mondain

I have added some tags in web.xml but not getting reflected, so placed proxy in front of red5, but just need to know like how to add for Red5 itself without using any other proxy servers.

LakshmiPhani7680 avatar Jul 28 '24 14:07 LakshmiPhani7680

There's no need for a proxy server unless that easiest for you to implement. A content listener or filter servlet can inject headers, but we don't provide such things, they are very standard JEE webapp solutions. I took a couple minutes and Claude provided what looks to be a valid solution example web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- Header Injection Filter Definition -->
    <filter>
        <filter-name>HeaderInjectionFilter</filter-name>
        <filter-class>com.example.filters.HeaderInjectionFilter</filter-class>
        
        <!-- Configure headers using init-param with "header." prefix -->
        <init-param>
            <param-name>header.X-Frame-Options</param-name>
            <param-value>DENY</param-value>
        </init-param>
        
        <init-param>
            <param-name>header.X-Content-Type-Options</param-name>
            <param-value>nosniff</param-value>
        </init-param>
        
        <init-param>
            <param-name>header.X-XSS-Protection</param-name>
            <param-value>1; mode=block</param-value>
        </init-param>
        
        <init-param>
            <param-name>header.Strict-Transport-Security</param-name>
            <param-value>max-age=31536000; includeSubDomains</param-value>
        </init-param>
        
        <init-param>
            <param-name>header.Cache-Control</param-name>
            <param-value>no-cache, no-store, must-revalidate</param-value>
        </init-param>
        
        <init-param>
            <param-name>header.Pragma</param-name>
            <param-value>no-cache</param-value>
        </init-param>
        
        <!-- Custom application headers -->
        <init-param>
            <param-name>header.X-Application-Name</param-name>
            <param-value>MyWebApp</param-value>
        </init-param>
        
        <init-param>
            <param-name>header.X-Version</param-name>
            <param-value>1.0.0</param-value>
        </init-param>
    </filter>

    <!-- Filter Mapping - Apply to all requests -->
    <filter-mapping>
        <filter-name>HeaderInjectionFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!-- Alternative: Apply only to specific patterns -->
    <!--
    <filter-mapping>
        <filter-name>HeaderInjectionFilter</filter-name>
        <url-pattern>/api/*</url-pattern>
    </filter-mapping>
    
    <filter-mapping>
        <filter-name>HeaderInjectionFilter</filter-name>
        <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
    -->

</web-app>

Filter servlet:

package com.example.filters;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * Servlet filter that injects HTTP headers based on configuration parameters
 * specified in web.xml. Headers are configured using init-param elements
 * with names starting with "header.".
 */
public class HeaderInjectionFilter implements Filter {
    
    private Map<String, String> headersToInject = new HashMap<>();
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // Read all init parameters that start with "header."
        Enumeration<String> paramNames = filterConfig.getInitParameterNames();
        
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            
            if (paramName.startsWith("header.")) {
                // Extract header name by removing "header." prefix
                String headerName = paramName.substring("header.".length());
                String headerValue = filterConfig.getInitParameter(paramName);
                
                if (headerName != null && !headerName.trim().isEmpty() && 
                    headerValue != null && !headerValue.trim().isEmpty()) {
                    headersToInject.put(headerName, headerValue);
                }
            }
        }
        
        // Log configured headers for debugging
        if (!headersToInject.isEmpty()) {
            System.out.println("HeaderInjectionFilter initialized with headers: " + headersToInject);
        }
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        // Cast to HttpServletResponse to access header methods
        if (response instanceof HttpServletResponse) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            
            // Inject all configured headers
            for (Map.Entry<String, String> header : headersToInject.entrySet()) {
                httpResponse.setHeader(header.getKey(), header.getValue());
            }
        }
        
        // Continue with the filter chain
        chain.doFilter(request, response);
    }
    
    @Override
    public void destroy() {
        headersToInject.clear();
    }
}

mondain avatar May 22 '25 20:05 mondain