View | Details | Raw Unified | Return to bug 49478
Collapse All | Expand All

(-)a/java/org/apache/catalina/filters/AddDefaultCharsetFilter.java (-54 / +97 lines)
Lines 17-24 Link Here
17
17
18
package org.apache.catalina.filters;
18
package org.apache.catalina.filters;
19
19
20
21
import java.io.IOException;
20
import java.io.IOException;
21
import java.nio.charset.Charset;
22
22
23
import javax.servlet.Filter;
23
import javax.servlet.Filter;
24
import javax.servlet.FilterChain;
24
import javax.servlet.FilterChain;
Lines 29-94 import javax.servlet.ServletResponse; Link Here
29
import javax.servlet.http.HttpServletResponse;
29
import javax.servlet.http.HttpServletResponse;
30
import javax.servlet.http.HttpServletResponseWrapper;
30
import javax.servlet.http.HttpServletResponseWrapper;
31
31
32
33
/**
32
/**
34
 * Filter that explicitly sets the default character set for media subtypes of
33
 * Filter that explicitly sets the default character set for media subtypes of
35
 * the "text" type to ISO-8859-1. RFC2616 explicitly states that browsers must
34
 * the "text" type to ISO-8859-1, or another user defined character set. RFC2616
36
 * use ISO-8859-1 in these circumstances. However, browsers may attempt to
35
 * explicitly states that browsers must use that character set in these
37
 * auto-detect the character set. This may be exploited by an attacker to
36
 * circumstances. However, browsers may attempt to auto-detect the character
38
 * perform an XSS attack. Internet Explorer has this behaviour by default. Other
37
 * set. This may be exploited by an attacker to perform an XSS attack. Internet
39
 * browsers have an option to enable it.
38
 * Explorer has this behaviour by default. Other browsers have an option to
39
 * enable it.<br>
40
 * 
40
 * 
41
 * This filter prevents the attack by explicitly setting a character set. Unless
41
 * This filter prevents the attack by explicitly setting a character set. Unless
42
 * the provided character set is explicitly overridden by the user - in which
42
 * the provided character set is explicitly overridden by the user - in which
43
 * case they deserve everything they get - the browser will adhere to an
43
 * case they deserve everything they get - the browser will adhere to an
44
 * explicitly set character set, thus preventing the XSS attack.
44
 * explicitly set character set, thus preventing the XSS attack.<br>
45
 * 
46
 * This filter thas one parameter named "encoding", which can hold the character
47
 * set to be used. The parameter can also be set to one of two special values.
48
 * 
49
 * <dl>
50
 * <dt>default</dt>
51
 * <dd>If the value of this parameter is <code>null</code>, empty or "default",
52
 * the used character set will be "ISO-8559-1".</dd>
53
 * <dt>system</dt>
54
 * <dd>A value of "system" will set the jvm wide default character set, which is
55
 * usually set by the system Locale.</dd>
56
 * </dl>
45
 */
57
 */
46
public class AddDefaultCharsetFilter implements Filter {
58
public class AddDefaultCharsetFilter implements Filter {
47
59
48
    public void destroy() {
60
	private transient String encoding;
49
        // NOOP
61
50
    }
62
	@Override
51
63
	public void destroy() {
52
    public void doFilter(ServletRequest request, ServletResponse response,
64
		// NOOP
53
            FilterChain chain) throws IOException, ServletException {
65
	}
54
        
66
55
        // Wrap the response
67
	@Override
56
        if (response instanceof HttpServletResponse) {
68
	public void doFilter(ServletRequest request, ServletResponse response,
57
            ResponseWrapper wrapped =
69
			FilterChain chain) throws IOException, ServletException {
58
                new ResponseWrapper((HttpServletResponse)response);
70
59
            chain.doFilter(request, wrapped);
71
		// Wrap the response
60
        } else {
72
		if (response instanceof HttpServletResponse) {
61
            chain.doFilter(request, response);
73
			ResponseWrapper wrapped = new ResponseWrapper(
62
        }
74
					(HttpServletResponse) response, encoding);
63
    }
75
			chain.doFilter(request, wrapped);
64
76
		} else {
65
    public void init(FilterConfig filterConfig) throws ServletException {
77
			chain.doFilter(request, response);
66
        // NOOP
78
		}
67
    }
79
	}
68
80
69
    /**
81
	@Override
70
     * Wrapper that adds the default character set for text media types if no
82
	public void init(FilterConfig filterConfig) throws ServletException {
71
     * character set is specified.
83
		String encoding = filterConfig.getInitParameter("encoding");
72
     */
84
		if (encoding == null || "".equals(encoding)
73
    public class ResponseWrapper extends HttpServletResponseWrapper {
85
				|| "default".equalsIgnoreCase(encoding)) {
74
86
			this.encoding = "ISO-8859-1";
75
        @Override
87
		} else if ("system".equalsIgnoreCase(encoding)) {
76
        public void setContentType(String ct) {
88
			this.encoding = Charset.defaultCharset().name();
77
            
89
		} else {
78
            if (ct != null && ct.startsWith("text/") &&
90
			if (Charset.isSupported(encoding)) {
79
                    ct.indexOf("charset=") < 0) {
91
				this.encoding = encoding;
80
                // Use getCharacterEncoding() in case the charset has already
92
			} else {
81
                // been set by a separate call.
93
				throw new IllegalArgumentException("Charset " + encoding
82
                super.setContentType(ct + ";charset=" + getCharacterEncoding());
94
						+ " seems not to be supported");
83
            } else {
95
			}
84
                super.setContentType(ct);
96
		}
85
            }
97
	}
86
98
87
        }
99
	/**
88
100
	 * Wrapper that adds the default character set for text media types if no
89
        public ResponseWrapper(HttpServletResponse response) {
101
	 * character set is specified.
90
            super(response);
102
	 */
91
        }
103
	public static class ResponseWrapper extends HttpServletResponseWrapper {
92
        
104
93
    }
105
		private String currentEncoding;
106
107
		@Override
108
		public void setCharacterEncoding(String charset) {
109
			super.setCharacterEncoding(charset);
110
			this.currentEncoding = charset;
111
		}
112
113
		@Override
114
		public void setContentType(String ct) {
115
116
			if (ct != null && ct.startsWith("text/")
117
					&& ct.indexOf("charset=") < 0) {
118
				// set charater set, if it is a text-type content-type and
119
				// character set is not set within content-type
120
				super.setCharacterEncoding(this.currentEncoding);
121
			}
122
			super.setContentType(ct);
123
124
		}
125
126
		public ResponseWrapper(HttpServletResponse response,
127
				String defaultEncoding) {
128
			super(response);
129
			if (defaultEncoding == null) {
130
				this.currentEncoding = response.getCharacterEncoding();
131
			} else {
132
				this.currentEncoding = defaultEncoding;
133
			}
134
		}
135
136
	}
94
}
137
}
(-)a/webapps/docs/config/filter.xml (-2 / +14 lines)
Lines 79-86 Link Here
79
79
80
  <subsection name="Initialisation parameters">
80
  <subsection name="Initialisation parameters">
81
81
82
    <p>The Add Default Character Set Filter does not support any initialization
82
    <p>The Add Default Character Set Filter supports the following initialization
83
    parameters.</p>
83
    parameters:</p>
84
85
    <attributes>
86
87
      <attribute name="encoding" required="false">
88
        <p>Name of the character set which should be set, if no other character set 
89
        was set explicitly by a servlet. This parameter has two special values 
90
        <code>default</code> and <code>system</code>. A value of <code>system</code>
91
        uses the jvm wide default character set, which is usually set by locale.
92
        A value of <code>default</code> will use <strong>ISO-8859-1</strong>.</p>
93
      </attribute>
94
95
    </attributes>
84
96
85
  </subsection>
97
  </subsection>
86
98

Return to bug 49478