001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.security.http; 019 020import java.io.IOException; 021import java.util.Map; 022 023import javax.servlet.Filter; 024import javax.servlet.FilterChain; 025import javax.servlet.FilterConfig; 026import javax.servlet.ServletException; 027import javax.servlet.ServletRequest; 028import javax.servlet.ServletResponse; 029import javax.servlet.http.HttpServletResponse; 030import javax.servlet.http.HttpServletResponseWrapper; 031 032import org.apache.hadoop.classification.InterfaceAudience; 033import org.apache.hadoop.classification.InterfaceStability; 034import org.apache.hadoop.conf.Configuration; 035 036/** 037 * This filter protects webapps from clickjacking attacks that 038 * are possible through use of Frames to embed the resources in another 039 * application and intercept clicks to accomplish nefarious things. 040 */ 041@InterfaceAudience.Public 042@InterfaceStability.Evolving 043public class XFrameOptionsFilter implements Filter { 044 public static final String X_FRAME_OPTIONS = "X-Frame-Options"; 045 public static final String CUSTOM_HEADER_PARAM = "xframe-options"; 046 047 private String option = "DENY"; 048 049 @Override 050 public void destroy() { 051 } 052 053 @Override 054 public void doFilter(ServletRequest req, ServletResponse res, 055 FilterChain chain) throws IOException, ServletException { 056 ((HttpServletResponse) res).setHeader(X_FRAME_OPTIONS, option); 057 chain.doFilter(req, 058 new XFrameOptionsResponseWrapper((HttpServletResponse) res)); 059 } 060 061 @Override 062 public void init(FilterConfig config) throws ServletException { 063 String customOption = config.getInitParameter(CUSTOM_HEADER_PARAM); 064 if (customOption != null) { 065 option = customOption; 066 } 067 } 068 069 /** 070 * Constructs a mapping of configuration properties to be used for filter 071 * initialization. The mapping includes all properties that start with the 072 * specified configuration prefix. Property names in the mapping are trimmed 073 * to remove the configuration prefix. 074 * 075 * @param conf configuration to read 076 * @param confPrefix configuration prefix 077 * @return mapping of configuration properties to be used for filter 078 * initialization 079 */ 080 public static Map<String, String> getFilterParams(Configuration conf, 081 String confPrefix) { 082 return conf.getPropsWithPrefix(confPrefix); 083 } 084 085 /** 086 * This wrapper allows the rest of the filter pipeline to 087 * see the configured value when interrogating the response. 088 * It also blocks other filters from setting the value to 089 * anything other than what is configured. 090 * 091 */ 092 public class XFrameOptionsResponseWrapper 093 extends HttpServletResponseWrapper { 094 /** 095 * Ctor to take wrap the provided response. 096 * @param response the response to wrap 097 */ 098 public XFrameOptionsResponseWrapper(HttpServletResponse response) { 099 super(response); 100 } 101 102 @Override 103 public void addHeader(String name, String value) { 104 // don't allow additional values to be added along 105 // with the configured options value 106 if (!name.equals(X_FRAME_OPTIONS)) { 107 super.addHeader(name, value); 108 } 109 } 110 111 @Override 112 public void setHeader(String name, String value) { 113 // don't allow overwriting of configured value 114 if (!name.equals(X_FRAME_OPTIONS)) { 115 super.setHeader(name, value); 116 } 117 } 118 119 @Override 120 public void setDateHeader(String name, long date) { 121 // don't allow overwriting of configured value 122 if (!name.equals(X_FRAME_OPTIONS)) { 123 super.setDateHeader(name, date); 124 } 125 } 126 127 @Override 128 public void addDateHeader(String name, long date) { 129 // don't allow additional values to be added along 130 // with the configured options value 131 if (!name.equals(X_FRAME_OPTIONS)) { 132 super.addDateHeader(name, date); 133 } 134 } 135 136 @Override 137 public void setIntHeader(String name, int value) { 138 // don't allow overwriting of configured value 139 if (!name.equals(X_FRAME_OPTIONS)) { 140 super.setIntHeader(name, value); 141 } 142 } 143 144 @Override 145 // don't allow additional values to be added along 146 // with the configured options value 147 public void addIntHeader(String name, int value) { 148 if (!name.equals(X_FRAME_OPTIONS)) { 149 super.addIntHeader(name, value); 150 } 151 } 152 153 @Override 154 public boolean containsHeader(String name) { 155 boolean contains = false; 156 // allow the filterchain and subsequent 157 // filters to see that the header is set 158 if (name.equals(X_FRAME_OPTIONS)) { 159 return (option != null); 160 } else { 161 super.containsHeader(name); 162 } 163 return contains; 164 } 165 } 166} 167