Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http filter: add CSRF filter #6470

Merged
merged 18 commits into from
Apr 23, 2019
Merged
1 change: 1 addition & 0 deletions api/docs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ proto_library(
"//envoy/config/filter/accesslog/v2:accesslog",
"//envoy/config/filter/dubbo/router/v2alpha1:router",
"//envoy/config/filter/http/buffer/v2:buffer",
"//envoy/config/filter/http/csrf/v2:csrf",
"//envoy/config/filter/http/ext_authz/v2:ext_authz",
"//envoy/config/filter/http/fault/v2:fault",
"//envoy/config/filter/http/gzip/v2:gzip",
Expand Down
9 changes: 9 additions & 0 deletions api/envoy/config/filter/http/csrf/v2/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("@envoy_api//bazel:api_build_system.bzl", "api_proto_library_internal")

licenses(["notice"]) # Apache 2

api_proto_library_internal(
name = "csrf",
srcs = ["csrf.proto"],
deps = ["//envoy/api/v2/core:base"],
)
43 changes: 43 additions & 0 deletions api/envoy/config/filter/http/csrf/v2/csrf.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
syntax = "proto3";

package envoy.config.filter.http.csrf.v2;

option java_outer_classname = "CsrfPolicyProto";
option java_multiple_files = true;
option java_package = "io.envoyproxy.envoy.config.filter.http.csrf.v2";
option go_package = "v2";

import "envoy/api/v2/core/base.proto";

import "validate/validate.proto";
import "gogoproto/gogo.proto";

// [#protodoc-title: CSRF]
// Cross-Site Request Forgery :ref:`configuration overview <config_http_filters_csrf>`.

// CSRF filter config.
message CsrfPolicy {
// Specify if CSRF is enabled.
//
// More information on how this can be controlled via runtime can be found
// :ref:`here <csrf-runtime>`.
//
// .. note::
//
// This field defaults to 100/:ref:`HUNDRED
// <envoy_api_enum_type.FractionalPercent.DenominatorType>`.
envoy.api.v2.core.RuntimeFractionalPercent filter_enabled = 1
[(validate.rules).message.required = true];

// Specifies that CSRF policies will be evaluated and tracked, but not enforced.
// This is intended to be used when filter_enabled is off.
//
// More information on how this can be controlled via runtime can be found
// :ref:`here <csrf-runtime>`.
//
// .. note::
//
// This field defaults to 100/:ref:`HUNDRED
// <envoy_api_enum_type.FractionalPercent.DenominatorType>`.
envoy.api.v2.core.RuntimeFractionalPercent shadow_enabled = 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ message HttpFilter {
// * :ref:`envoy.cors <config_http_filters_cors>`
// * :ref:`envoy.ext_authz <config_http_filters_ext_authz>`
// * :ref:`envoy.fault <config_http_filters_fault_injection>`
// * :ref:`envoy.filters.http.csrf <config_http_filters_csrf>`
// * :ref:`envoy.filters.http.header_to_metadata <config_http_filters_header_to_metadata>`
// * :ref:`envoy.filters.http.grpc_http1_reverse_bridge \
// <config_http_filters_grpc_http1_reverse_bridge>`
Expand Down
1 change: 1 addition & 0 deletions docs/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ PROTO_RST="
/envoy/config/filter/accesslog/v2/accesslog/envoy/config/filter/accesslog/v2/accesslog.proto.rst
/envoy/config/filter/fault/v2/fault/envoy/config/filter/fault/v2/fault.proto.rst
/envoy/config/filter/http/buffer/v2/buffer/envoy/config/filter/http/buffer/v2/buffer.proto.rst
/envoy/config/filter/http/csrf/v2/csrf/envoy/config/filter/http/csrf/v2/csrf.proto.rst
/envoy/config/filter/http/ext_authz/v2/ext_authz/envoy/config/filter/http/ext_authz/v2/ext_authz.proto.rst
/envoy/config/filter/http/fault/v2/fault/envoy/config/filter/http/fault/v2/fault.proto.rst
/envoy/config/filter/http/gzip/v2/gzip/envoy/config/filter/http/gzip/v2/gzip.proto.rst
Expand Down
87 changes: 87 additions & 0 deletions docs/root/configuration/http_filters/csrf_filter.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
.. _config_http_filters_csrf:

CSRF
====

This is a filter which prevents Cross-Site Request Forgery based on a route or virtual host settings.
At it's simplest, CSRF is an attack that occurs when a malicious third-party
exploits a vulnerability that allows them to submit an undesired request on the
user's behalf.

There are many ways to mitigate CSRF, some of which have been outlined in the
`OWASP Prevention Cheat Sheet <https://github.com/OWASP/CheatSheetSeries/blob/5a1044e38778b42a19c6adbb4dfef7a0fb071099/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.md>`_.
This filter employs a stateless mitigation pattern known as origin verification.

This pattern relies on two pieces of information used in determining if
a request originated from the same host.
* The origin that the request is coming from (source origin).
dschaller marked this conversation as resolved.
Show resolved Hide resolved
* The origin that the request is going to (target origin).

When the filter is evaluating a request, it ensures both pieces of information are present
and compares their values. If the source origin is missing or the origins do not match
the request is rejected.

.. note::
Due to differing functionality between browsers this filter will determine
a request's source origin from the Host header. If that is not present it will
fall back to the host and port value from the requests Referer header.


For more information on CSRF please refer to the pages below.

* https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29
* https://seclab.stanford.edu/websec/csrf/csrf.pdf
* :ref:`v2 API reference <envoy_api_msg_config.filter.http.csrf.v2.CsrfPolicy>`

.. note::

This filter should be configured with the name *envoy.csrf*.

.. _csrf-runtime:

Runtime
-------

The CSRF filter supports the following RuntimeFractionalPercent settings:

filter_enabled
The % of requests for which the filter is enabled. The default is
100/:ref:`HUNDRED <envoy_api_enum_type.FractionalPercent.DenominatorType>`.

To utilize runtime to enabled/disable the CSRF filter set the
:ref:`runtime_key <envoy_api_msg_core.runtimefractionalpercent>`
value of the :ref:`filter_enabled <envoy_api_msg_config.filter.http.csrf.v2.CsrfPolicy>`
field.

shadow_enabled
The % of requests for which the filter is enabled in shadow only mode. Default is 0.
If present, this will evaluate a request's *Origin* and *Destination* to determine
if the request is valid but will not enforce any policies.

To utilize runtime to enabled/disable the CSRF filter's shadow mode set the
:ref:`runtime_key <envoy_api_msg_core.runtimefractionalpercent>`
value of the :ref:`shadow_enabled <envoy_api_msg_config.filter.http.csrf.v2.CsrfPolicy>`
field.

To determine if the filter and/or shadow mode are enabled you can check the runtime
values via the admin panel at :http:get:`/runtime`.

.. note::

If both ``filter_enabled`` and ``shadow_enabled`` are on, the ``filter_enabled``
flag will take precedence.

.. _csrf-statistics:

Statistics
----------

The CSRF filter outputs statistics in the <stat_prefix>.csrf.* namespace.

.. csv-table::
:header: Name, Type, Description
:widths: 1, 1, 2

missing_source_origin, Counter, Number of requests that are missing a source origin header.
request_invalid, Counter, Number of requests whose source and target origins do not match.
request_valid, Counter, Number of requests whose source and target origins match.
1 change: 1 addition & 0 deletions docs/root/configuration/http_filters/http_filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ HTTP filters

buffer_filter
cors_filter
csrf_filter
dynamodb_filter
ext_authz_filter
fault_filter
Expand Down
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Version history
* config: use Envoy cpuset size to set the default number or worker threads if :option:`--cpuset-threads` is enabled.
* config: added support for :ref:`initial_fetch_timeout <envoy_api_field_core.ConfigSource.initial_fetch_timeout>`. The timeout is disabled by default.
* cors: added :ref:`filter_enabled & shadow_enabled RuntimeFractionalPercent flags <cors-runtime>` to filter.
* csrf: added :ref:`CSRF filter <config_http_filters_csrf>`.
* ext_authz: added support for buffering request body.
* ext_authz: migrated from v2alpha to v2 and improved docs.
* ext_authz: added a configurable option to make the gRPC service cross-compatible with V2Alpha. Note that this feature is already deprecated. It should be used for a short time, and only when transitioning from alpha to V2 release version.
Expand Down
4 changes: 3 additions & 1 deletion source/common/http/headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,12 @@ class HeaderValues {

struct {
const std::string Connect{"CONNECT"};
const std::string Delete{"DELETE"};
const std::string Get{"GET"};
const std::string Head{"HEAD"};
const std::string Options{"OPTIONS"};
const std::string Post{"POST"};
const std::string Put{"PUT"};
dschaller marked this conversation as resolved.
Show resolved Hide resolved
const std::string Options{"OPTIONS"};
const std::string Trace{"TRACE"};
} MethodValues;

Expand Down
2 changes: 2 additions & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ EXTENSIONS = {

"envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config",
"envoy.filters.http.cors": "//source/extensions/filters/http/cors:config",
"envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config",
"envoy.filters.http.dynamo": "//source/extensions/filters/http/dynamo:config",
"envoy.filters.http.ext_authz": "//source/extensions/filters/http/ext_authz:config",
"envoy.filters.http.fault": "//source/extensions/filters/http/fault:config",
Expand Down Expand Up @@ -151,6 +152,7 @@ WINDOWS_EXTENSIONS = {

#"envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config",
#"envoy.filters.http.cors": "//source/extensions/filters/http/cors:config",
#"envoy.filters.http.csrf": "//source/extensions/filters/http/csrf:config",
#"envoy.filters.http.dynamo": "//source/extensions/filters/http/dynamo:config",
#"envoy.filters.http.ext_authz": "//source/extensions/filters/http/ext_authz:config",
#"envoy.filters.http.fault": "//source/extensions/filters/http/fault:config",
Expand Down
39 changes: 39 additions & 0 deletions source/extensions/filters/http/csrf/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
licenses(["notice"]) # Apache 2

# L7 HTTP filter which implements CSRF processing (https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF))
# Public docs: docs/root/configuration/http_filters/csrf_filter.rst

load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

envoy_package()

envoy_cc_library(
name = "csrf_filter_lib",
srcs = ["csrf_filter.cc"],
hdrs = ["csrf_filter.h"],
deps = [
"//include/envoy/http:filter_interface",
"//source/common/buffer:buffer_lib",
"//source/common/http:header_map_lib",
"//source/common/http:headers_lib",
"//source/common/http:utility_lib",
"//source/extensions/filters/http:well_known_names",
"@envoy_api//envoy/config/filter/http/csrf/v2:csrf_cc",
],
)

envoy_cc_library(
name = "config",
srcs = ["config.cc"],
hdrs = ["config.h"],
deps = [
"//include/envoy/registry",
"//source/extensions/filters/http:well_known_names",
"//source/extensions/filters/http/common:factory_base_lib",
"//source/extensions/filters/http/csrf:csrf_filter_lib",
],
)
38 changes: 38 additions & 0 deletions source/extensions/filters/http/csrf/config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "extensions/filters/http/csrf/config.h"

#include "envoy/config/filter/http/csrf/v2/csrf.pb.validate.h"
#include "envoy/registry/registry.h"

#include "extensions/filters/http/csrf/csrf_filter.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace Csrf {

Http::FilterFactoryCb CsrfFilterFactory::createFilterFactoryFromProtoTyped(
const envoy::config::filter::http::csrf::v2::CsrfPolicy& policy,
const std::string& stats_prefix, Server::Configuration::FactoryContext& context) {
CsrfFilterConfigSharedPtr config =
std::make_shared<CsrfFilterConfig>(policy, stats_prefix, context.scope(), context.runtime());
return [config](Http::FilterChainFactoryCallbacks& callbacks) -> void {
callbacks.addStreamDecoderFilter(std::make_shared<CsrfFilter>(config));
};
}

Router::RouteSpecificFilterConfigConstSharedPtr
CsrfFilterFactory::createRouteSpecificFilterConfigTyped(
const envoy::config::filter::http::csrf::v2::CsrfPolicy& policy,
Server::Configuration::FactoryContext& context) {
return std::make_shared<const Csrf::CsrfPolicy>(policy, context.runtime());
}

/**
* Static registration for the CSRF filter. @see RegisterFactory.
*/
REGISTER_FACTORY(CsrfFilterFactory, Server::Configuration::NamedHttpFilterConfigFactory);

} // namespace Csrf
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
35 changes: 35 additions & 0 deletions source/extensions/filters/http/csrf/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include "envoy/config/filter/http/csrf/v2/csrf.pb.h"
#include "envoy/config/filter/http/csrf/v2/csrf.pb.validate.h"

#include "extensions/filters/http/common/factory_base.h"
#include "extensions/filters/http/well_known_names.h"

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace Csrf {

/**
* Config registration for the CSRF filter. @see NamedHttpFilterConfigFactory.
*/
class CsrfFilterFactory
: public Common::FactoryBase<envoy::config::filter::http::csrf::v2::CsrfPolicy> {
public:
CsrfFilterFactory() : FactoryBase(HttpFilterNames::get().Csrf) {}

private:
Http::FilterFactoryCb
createFilterFactoryFromProtoTyped(const envoy::config::filter::http::csrf::v2::CsrfPolicy& policy,
const std::string& stats_prefix,
Server::Configuration::FactoryContext& context) override;
Router::RouteSpecificFilterConfigConstSharedPtr createRouteSpecificFilterConfigTyped(
const envoy::config::filter::http::csrf::v2::CsrfPolicy& policy,
Server::Configuration::FactoryContext& context) override;
};

} // namespace Csrf
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
Loading