001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
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.PropsKeys;
022    import com.liferay.portal.kernel.util.PropsUtil;
023    import com.liferay.portal.kernel.util.ServerDetector;
024    import com.liferay.portal.kernel.util.SortedProperties;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    
029    import java.io.IOException;
030    
031    import java.util.ArrayList;
032    import java.util.Comparator;
033    import java.util.List;
034    import java.util.Map;
035    import java.util.Properties;
036    
037    import javax.servlet.http.HttpServletRequest;
038    import javax.servlet.http.HttpServletResponse;
039    import javax.servlet.http.HttpServletResponseWrapper;
040    
041    /**
042     * @author L??szl?? Csontos
043     * @author Shuyang Zhou
044     * @author Tomas Polesovsky
045     */
046    public class SanitizedServletResponse extends HttpServletResponseWrapper {
047    
048            public static HttpServletResponse getSanitizedServletResponse(
049                    HttpServletRequest request, HttpServletResponse response) {
050    
051                    setXContentOptions(request, response);
052                    setXFrameOptions(request, response);
053                    setXXSSProtection(request, response);
054    
055                    if (ServerDetector.isResin()) {
056                            response = new SanitizedServletResponse(response);
057                    }
058    
059                    return response;
060            }
061    
062            @Override
063            public void addHeader(String name, String value) {
064                    super.addHeader(
065                            HttpUtil.sanitizeHeader(name), HttpUtil.sanitizeHeader(value));
066            }
067    
068            @Override
069            public void sendRedirect(String location) throws IOException {
070                    super.sendRedirect(HttpUtil.sanitizeHeader(location));
071            }
072    
073            @Override
074            public void setCharacterEncoding(String charset) {
075                    super.setCharacterEncoding(HttpUtil.sanitizeHeader(charset));
076            }
077    
078            @Override
079            public void setContentType(String type) {
080                    super.setContentType(HttpUtil.sanitizeHeader(type));
081            }
082    
083            @Override
084            public void setHeader(String name, String value) {
085                    super.setHeader(
086                            HttpUtil.sanitizeHeader(name), HttpUtil.sanitizeHeader(value));
087            }
088    
089            protected static void setXContentOptions(
090                    HttpServletRequest request, HttpServletResponse response) {
091    
092                    if (!_X_CONTENT_TYPE_OPTIONS) {
093                            return;
094                    }
095    
096                    if (_X_CONTENT_TYPE_OPTIONS_URLS_EXCLUDES.length > 0) {
097                            String requestURI = request.getRequestURI();
098    
099                            for (String url : _X_CONTENT_TYPE_OPTIONS_URLS_EXCLUDES) {
100                                    if (requestURI.startsWith(url)) {
101                                            return;
102                                    }
103                            }
104                    }
105    
106                    response.setHeader(HttpHeaders.X_CONTENT_TYPE_OPTIONS, "nosniff");
107            }
108    
109            protected static void setXFrameOptions(
110                    HttpServletRequest request, HttpServletResponse response) {
111    
112                    if (!_X_FRAME_OPTIONS) {
113                            return;
114                    }
115    
116                    String requestURI = request.getRequestURI();
117    
118                    for (KeyValuePair xFrameOptionKVP : _xFrameOptionKVPs) {
119                            String url = xFrameOptionKVP.getKey();
120                            String value = xFrameOptionKVP.getValue();
121    
122                            if (requestURI.startsWith(url)) {
123                                    if (value != null) {
124                                            response.setHeader(
125                                                    HttpHeaders.X_FRAME_OPTIONS,
126                                                    xFrameOptionKVP.getValue());
127                                    }
128    
129                                    return;
130                            }
131                    }
132    
133                    response.setHeader(HttpHeaders.X_FRAME_OPTIONS, "DENY");
134            }
135    
136            protected static void setXXSSProtection(
137                    HttpServletRequest request, HttpServletResponse response) {
138    
139                    if (!_X_XSS_PROTECTION) {
140                            return;
141                    }
142    
143                    response.setHeader(HttpHeaders.X_XSS_PROTECTION, "1; mode=block");
144            }
145    
146            private SanitizedServletResponse(HttpServletResponse response) {
147                    super(response);
148            }
149    
150            private static final boolean _X_CONTENT_TYPE_OPTIONS =
151                    GetterUtil.getBoolean(
152                            PropsUtil.get(PropsKeys.HTTP_HEADER_SECURE_X_CONTENT_TYPE_OPTIONS),
153                            true);
154    
155            private static final String[] _X_CONTENT_TYPE_OPTIONS_URLS_EXCLUDES =
156                    PropsUtil.getArray(
157                            PropsKeys.HTTP_HEADER_SECURE_X_CONTENT_TYPE_OPTIONS_URLS_EXCLUDES);
158    
159            private static final boolean _X_FRAME_OPTIONS;
160    
161            private static final boolean _X_XSS_PROTECTION = GetterUtil.getBoolean(
162                    PropsUtil.get(PropsKeys.HTTP_HEADER_SECURE_X_XSS_PROTECTION), true);
163    
164            private static final KeyValuePair[] _xFrameOptionKVPs;
165    
166            static {
167                    Properties properties = new SortedProperties(
168                            new Comparator<String>() {
169    
170                                    @Override
171                                    public int compare(String key1, String key2) {
172                                            return GetterUtil.getIntegerStrict(key1) -
173                                                    GetterUtil.getIntegerStrict(key2);
174                                    }
175    
176                            },
177                            PropsUtil.getProperties(
178                                    PropsKeys.HTTP_HEADER_SECURE_X_FRAME_OPTIONS +
179                                            StringPool.PERIOD,
180                                    true));
181    
182                    List<KeyValuePair> xFrameOptionKVPs = new ArrayList<KeyValuePair>(
183                            properties.size());
184    
185                    for (Map.Entry<Object, Object> entry : properties.entrySet()) {
186                            String propertyValue = (String)entry.getValue();
187    
188                            String[] propertyValueParts = StringUtil.split(
189                                    propertyValue, CharPool.PIPE);
190    
191                            if (propertyValueParts.length > 2) {
192                                    continue;
193                            }
194    
195                            String url = StringUtil.trim(propertyValueParts[0]);
196    
197                            if (Validator.isNull(url)) {
198                                    continue;
199                            }
200    
201                            if (propertyValueParts.length == 1) {
202                                    xFrameOptionKVPs.add(new KeyValuePair(url, null));
203    
204                                    continue;
205                            }
206    
207                            String value = StringUtil.trim(propertyValueParts[1]);
208    
209                            if (Validator.isNull(value)) {
210                                    value = null;
211                            }
212    
213                            xFrameOptionKVPs.add(new KeyValuePair(url, value));
214                    }
215    
216                    _xFrameOptionKVPs = xFrameOptionKVPs.toArray(
217                            new KeyValuePair[xFrameOptionKVPs.size()]);
218    
219                    if (_xFrameOptionKVPs.length == 0) {
220                            _X_FRAME_OPTIONS = false;
221                    }
222                    else {
223                            _X_FRAME_OPTIONS = GetterUtil.getBoolean(
224                                    PropsUtil.get(PropsKeys.HTTP_HEADER_SECURE_X_FRAME_OPTIONS),
225                                    true);
226                    }
227            }
228    
229    }