001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.kernel.servlet;
016    
017    import com.liferay.portal.kernel.util.CharPool;
018    import com.liferay.portal.kernel.util.GetterUtil;
019    import com.liferay.portal.kernel.util.HttpUtil;
020    import com.liferay.portal.kernel.util.KeyValuePair;
021    import com.liferay.portal.kernel.util.PropertiesUtil;
022    import com.liferay.portal.kernel.util.ServerDetector;
023    import com.liferay.portal.kernel.util.SortedProperties;
024    import com.liferay.portal.kernel.util.StringPool;
025    import com.liferay.portal.kernel.util.StringUtil;
026    import com.liferay.portal.kernel.util.SystemProperties;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.util.PortalUtil;
029    
030    import java.io.IOException;
031    
032    import java.util.ArrayList;
033    import java.util.Comparator;
034    import java.util.List;
035    import java.util.Map;
036    import java.util.Properties;
037    
038    import javax.portlet.PortletRequest;
039    import javax.portlet.PortletResponse;
040    
041    import javax.servlet.http.HttpServletRequest;
042    import javax.servlet.http.HttpServletResponse;
043    import javax.servlet.http.HttpServletResponseWrapper;
044    import javax.servlet.http.HttpSession;
045    
046    /**
047     * @author L??szl?? Csontos
048     * @author Shuyang Zhou
049     * @author Tomas Polesovsky
050     */
051    public class SanitizedServletResponse extends HttpServletResponseWrapper {
052    
053            public static void disableXSSAuditor(HttpServletResponse response) {
054                    response.setHeader(HttpHeaders.X_XSS_PROTECTION, "0");
055            }
056    
057            public static void disableXSSAuditor(PortletResponse portletResponse) {
058                    disableXSSAuditor(PortalUtil.getHttpServletResponse(portletResponse));
059            }
060    
061            public static void disableXSSAuditorOnNextRequest(
062                    HttpServletRequest request) {
063    
064                    HttpSession session = request.getSession();
065    
066                    session.setAttribute(_DISABLE_XSS_AUDITOR, Boolean.TRUE);
067            }
068    
069            public static void disableXSSAuditorOnNextRequest(
070                    PortletRequest portletRequest) {
071    
072                    disableXSSAuditorOnNextRequest(
073                            PortalUtil.getHttpServletRequest(portletRequest));
074            }
075    
076            public static HttpServletResponse getSanitizedServletResponse(
077                    HttpServletRequest request, HttpServletResponse response) {
078    
079                    setXContentOptions(request, response);
080                    setXFrameOptions(request, response);
081                    setXXSSProtection(request, response);
082    
083                    if (ServerDetector.isResin()) {
084                            response = new SanitizedServletResponse(response);
085                    }
086    
087                    return response;
088            }
089    
090            @Override
091            public void addHeader(String name, String value) {
092                    super.addHeader(
093                            HttpUtil.sanitizeHeader(name), HttpUtil.sanitizeHeader(value));
094            }
095    
096            @Override
097            public void sendRedirect(String location) throws IOException {
098                    super.sendRedirect(HttpUtil.sanitizeHeader(location));
099            }
100    
101            @Override
102            public void setCharacterEncoding(String charset) {
103                    super.setCharacterEncoding(HttpUtil.sanitizeHeader(charset));
104            }
105    
106            @Override
107            public void setContentType(String type) {
108                    super.setContentType(HttpUtil.sanitizeHeader(type));
109            }
110    
111            @Override
112            public void setHeader(String name, String value) {
113                    super.setHeader(
114                            HttpUtil.sanitizeHeader(name), HttpUtil.sanitizeHeader(value));
115            }
116    
117            protected static void setXContentOptions(
118                    HttpServletRequest request, HttpServletResponse response) {
119    
120                    if (!_X_CONTENT_TYPE_OPTIONS) {
121                            return;
122                    }
123    
124                    if (_X_CONTENT_TYPE_OPTIONS_URLS_EXCLUDES.length > 0) {
125                            String requestURI = request.getRequestURI();
126    
127                            for (String url : _X_CONTENT_TYPE_OPTIONS_URLS_EXCLUDES) {
128                                    if (requestURI.startsWith(url)) {
129                                            return;
130                                    }
131                            }
132                    }
133    
134                    response.setHeader(HttpHeaders.X_CONTENT_TYPE_OPTIONS, "nosniff");
135            }
136    
137            protected static void setXFrameOptions(
138                    HttpServletRequest request, HttpServletResponse response) {
139    
140                    if (!_X_FRAME_OPTIONS) {
141                            return;
142                    }
143    
144                    String requestURI = request.getRequestURI();
145    
146                    for (KeyValuePair xFrameOptionKVP : _xFrameOptionKVPs) {
147                            String url = xFrameOptionKVP.getKey();
148                            String value = xFrameOptionKVP.getValue();
149    
150                            if (requestURI.startsWith(url)) {
151                                    if (value != null) {
152                                            response.setHeader(
153                                                    HttpHeaders.X_FRAME_OPTIONS,
154                                                    xFrameOptionKVP.getValue());
155                                    }
156    
157                                    return;
158                            }
159                    }
160    
161                    response.setHeader(HttpHeaders.X_FRAME_OPTIONS, "DENY");
162            }
163    
164            protected static void setXXSSProtection(
165                    HttpServletRequest request, HttpServletResponse response) {
166    
167                    HttpSession session = request.getSession(false);
168    
169                    if ((session != null) &&
170                            (session.getAttribute(_DISABLE_XSS_AUDITOR) != null)) {
171    
172                            session.removeAttribute(_DISABLE_XSS_AUDITOR);
173    
174                            response.setHeader(HttpHeaders.X_XSS_PROTECTION, "0");
175    
176                            return;
177                    }
178    
179                    if (_X_XSS_PROTECTION == null) {
180                            return;
181                    }
182    
183                    response.setHeader(HttpHeaders.X_XSS_PROTECTION, _X_XSS_PROTECTION);
184            }
185    
186            private SanitizedServletResponse(HttpServletResponse response) {
187                    super(response);
188            }
189    
190            private static final String _DISABLE_XSS_AUDITOR =
191                    SanitizedServletResponse.class.getName() + "DISABLE_XSS_AUDITOR";
192    
193            private static final boolean _X_CONTENT_TYPE_OPTIONS =
194                    GetterUtil.getBoolean(
195                            SystemProperties.get("http.header.secure.x.content.type.options"),
196                            true);
197    
198            private static final String[] _X_CONTENT_TYPE_OPTIONS_URLS_EXCLUDES =
199                    StringUtil.split(
200                            SystemProperties.get(
201                                    "http.header.secure.x.content.type.options.urls.excludes"));
202    
203            private static final boolean _X_FRAME_OPTIONS;
204    
205            private static final String _X_XSS_PROTECTION;
206    
207            private static final KeyValuePair[] _xFrameOptionKVPs;
208    
209            static {
210                    String httpHeaderSecureXFrameOptionsKey =
211                            "http.header.secure.x.frame.options";
212    
213                    Properties properties = new SortedProperties(
214                            new Comparator<String>() {
215    
216                                    @Override
217                                    public int compare(String key1, String key2) {
218                                            return GetterUtil.getIntegerStrict(key1) -
219                                                    GetterUtil.getIntegerStrict(key2);
220                                    }
221    
222                            },
223                            PropertiesUtil.getProperties(
224                                    SystemProperties.getProperties(),
225                                    httpHeaderSecureXFrameOptionsKey.concat(StringPool.PERIOD),
226                                    true));
227    
228                    List<KeyValuePair> xFrameOptionKVPs = new ArrayList<KeyValuePair>(
229                            properties.size());
230    
231                    for (Map.Entry<Object, Object> entry : properties.entrySet()) {
232                            String propertyValue = (String)entry.getValue();
233    
234                            String[] propertyValueParts = StringUtil.split(
235                                    propertyValue, CharPool.PIPE);
236    
237                            if (propertyValueParts.length > 2) {
238                                    continue;
239                            }
240    
241                            String url = StringUtil.trim(propertyValueParts[0]);
242    
243                            if (Validator.isNull(url)) {
244                                    continue;
245                            }
246    
247                            if (propertyValueParts.length == 1) {
248                                    xFrameOptionKVPs.add(new KeyValuePair(url, null));
249    
250                                    continue;
251                            }
252    
253                            String value = StringUtil.trim(propertyValueParts[1]);
254    
255                            if (Validator.isNull(value)) {
256                                    value = null;
257                            }
258    
259                            xFrameOptionKVPs.add(new KeyValuePair(url, value));
260                    }
261    
262                    _xFrameOptionKVPs = xFrameOptionKVPs.toArray(
263                            new KeyValuePair[xFrameOptionKVPs.size()]);
264    
265                    if (_xFrameOptionKVPs.length == 0) {
266                            _X_FRAME_OPTIONS = false;
267                    }
268                    else {
269                            _X_FRAME_OPTIONS = GetterUtil.getBoolean(
270                                    SystemProperties.get(httpHeaderSecureXFrameOptionsKey), true);
271                    }
272    
273                    String xXssProtection = SystemProperties.get(
274                            "http.header.secure.x.xss.protection");
275    
276                    if (Validator.isNull(xXssProtection)) {
277                            _X_XSS_PROTECTION = null;
278                    }
279                    else {
280                            _X_XSS_PROTECTION = xXssProtection;
281                    }
282            }
283    
284    }