Set X-Frame-Options header to avoid clickjacking

Add HTTP filter which is applied to all HTTP responses.
Based on gerrit.canLoadInIFrame and gerrit.xframeOption
properties filter adds the X-Frame-Options HTTP
response header. The X-Frame-Options HTTP response header
can be used to indicate whether or not a browser should
be allowed to render a page in a <frame>, <iframe>,
<embed> or <object>. Gerrit can use this to avoid
click-jacking attacks, by ensuring that the content is
not embedded into other sites.

Bug: Issue 12926
Change-Id: If3f6a770332ade9924b3d1a20c092637c9380e0c
This commit is contained in:
Marcin Czech
2020-06-15 17:36:18 +02:00
parent 5691839454
commit 559ea2b49f
4 changed files with 254 additions and 1 deletions

View File

@@ -18,6 +18,8 @@ import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.server.plugins.Plugin;
import com.google.gerrit.server.plugins.StopPluginListener;
import com.google.inject.Inject;
import com.google.inject.Module;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
import com.google.inject.internal.UniqueAnnotations;
import com.google.inject.servlet.ServletModule;
@@ -32,11 +34,15 @@ import javax.servlet.ServletResponse;
/** Filters all HTTP requests passing through the server. */
public abstract class AllRequestFilter implements Filter {
public static ServletModule module() {
public static Module module() {
return new ServletModule() {
@Override
protected void configureServlets() {
DynamicSet.setOf(binder(), AllRequestFilter.class);
DynamicSet.bind(binder(), AllRequestFilter.class)
.to(AllowRenderInFrameFilter.class)
.in(Scopes.SINGLETON);
filter("/*").through(FilterProxy.class);
bind(StopPluginListener.class)

View File

@@ -0,0 +1,59 @@
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.httpd;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.lib.Config;
public class AllowRenderInFrameFilter extends AllRequestFilter {
static final String X_FRAME_OPTIONS_HEADER_NAME = "X-Frame-Options";
public static enum XFrameOption {
ALLOW,
SAMEORIGIN;
}
private final String xframeOptionString;
private final boolean skipXFrameOption;
@Inject
public AllowRenderInFrameFilter(@GerritServerConfig Config cfg) {
XFrameOption xframeOption =
cfg.getEnum("gerrit", null, "xframeOption", XFrameOption.SAMEORIGIN);
boolean canLoadInIFrame = cfg.getBoolean("gerrit", "canLoadInIFrame", false);
xframeOptionString = canLoadInIFrame ? xframeOption.name() : "DENY";
skipXFrameOption = xframeOption.equals(XFrameOption.ALLOW) && canLoadInIFrame;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (skipXFrameOption) {
chain.doFilter(request, response);
} else {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.addHeader(X_FRAME_OPTIONS_HEADER_NAME, xframeOptionString);
chain.doFilter(request, httpResponse);
}
}
}