Skip to content

Commit

Permalink
[GEOS-11346] Add a configurable Content-Security-Policy header
Browse files Browse the repository at this point in the history
  • Loading branch information
sikeoka committed Mar 26, 2024
1 parent 9deb352 commit 81c8190
Show file tree
Hide file tree
Showing 51 changed files with 5,686 additions and 4 deletions.
12 changes: 12 additions & 0 deletions doc/en/user/source/installation/upgrade.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ The general GeoServer upgrade process is as follows:
Notes on upgrading specific versions
------------------------------------

Content Security Policy (GeoServer 2.26 and newer)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As of GeoServer 2.26, the Content-Security-Policy HTTP response header will be added to all
responses by default in order to mitigate cross-site scripting and clickjacking attacks. The
default header value is intended to **block** use of inline JavaScript in all HTML output
except in cases where it is required (e.g., OpenLayers maps).

Users without any customized HTML output should not experience any issues but users that customize
FreeMarker HTML templates, static web files or have custom plugins generating HTML output may need
to update their settings. For more information, see :ref:`security_csp`.

External Entity Allow List default (GeoServer 2.25 and newer)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
28 changes: 27 additions & 1 deletion doc/en/user/source/production/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,18 @@ If you wish to change this behavior you can do so through the following properti
.. warning::
The ``ALLOW-FROM`` option is not supported by modern browsers and should only be used if you know
that browsers interacting with your GeoServer will support it. Applying this policy will be treated
as if no policy was set by browsers that do not support this (i.e., **NO** protection).
as if no policy was set by browsers that do not support this (i.e., **NO** protection). The
``Content-Security-Policy`` header provides more robust support for allowing certain hosts to
display frames from GeoServer using the ``frame-ancestors`` directive.

If the ``geoserver.csp.frameAncestors`` system property has not been set, the ``frame-ancestors``
directive of the ``Content-Security-Policy`` header will be set depending on the value of the
``X-Frame-Options`` header.

* ``SAMEORIGIN`` will be ``frame-ancestors 'self'``
* ``DENY`` will be ``frame-ancestors 'none'``
* if the ``X-Frame-Options`` header is not set or has any other value, the ``frame-ancestors``
directive will be omitted

These properties can be set either via Java system property, command line argument (-D), environment
variable or web.xml init parameter.
Expand Down Expand Up @@ -219,6 +230,21 @@ If you wish to change this behavior you can do so through the following properti
These properties can be set either via Java system property, command line argument (-D), environment
variable or web.xml init parameter.

Content-Security-Policy
-----------------------

In order to mitigate cross-site scripting and clickjacking attacks GeoServer defaults to setting
the Content-Security-Policy HTTP header based on rules loaded from a configuration file. See the
:ref:`security_csp` page for more details about this header, GeoServer's default configuration and
how to change the configuration.

* ``geoserver.csp.shouldSetPolicy``: controls whether to set the Content-Security-Policy header.
Default is true.
* ``geoserver.csp.policy``: controls the value of the Content-Security-Policy header. Default is to
leave this property blank, which will use the configuration file that will set the header based
on the request and server environment but this property allows the administrator to set a fixed
header value, which can be useful in cases where JavaScript is not needed at all.

OWS ServiceException XML mimeType
--------------------------------------------------

Expand Down
259 changes: 259 additions & 0 deletions doc/en/user/source/security/csp.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
.. _security_csp:

Content Security Policy
=======================

The :guilabel:`Content Security Policy` page controls the checks that are performed on GeoServer
requests that GeoServer will use to set an appropriate Content-Security-Policy (CSP) HTTP
response header to mitigate cross-site scripting and clickjacking attacks.

.. note::
Starting with version 9, Wicket will set its own CSP header with a very strict policy that will
overwrite what is set by GeoServer although there is a workaround in place to merge directives
set by GeoServer that are either not supported or not set by default by Wicket (particularly
``form-action`` and ``frame-ancestors``). Wicket's CSP is separate from GeoServer's
functionality and will otherwise not be affected by GeoServer's configuration.

Default Configuration
---------------------

The default CSP configuration is intended to support many GeoServer use cases and allow users to
securely run GeoServer without having to modify the configuration. It may be updated in future
releases to fix bugs, support new features or enhance security.

The default header value for most GeoServer requests will be (without line breaks):

::
base-uri 'self'; form-action 'self'; default-src 'none'; child-src 'self'; connect-src 'self';
font-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self';,
frame-ancestors 'self';

and the ``'unsafe-inline'`` and ``'unsafe-eval'`` sources will be added to the ``script-src``
directive for specific requests that require unsafe JavaScript. The default configuration may be
updated as necessary by the GeoServer developers.

.. note::
While the ``'self'`` script source should be sufficient to prevent most reflected cross-site
scripting, it does leave the possibility of stored cross-site scripting by administrators
with permissions to upload static web files. It is possible that future work will further
restrict the default policy to completely disable JavaScript for most requests. See the
:ref:`tutorials_staticfiles` page for instructions to disable that feature if it is not needed.

The current CSP configuration is stored in the file :file:`csp.xml`, located in the ``security``
directory in the GeoServer data directory. The default CSP configuration will be stored in the
file :file:`csp_default.xml` in the same directory. It is important that administrators do **NOT**
modify :file:`csp_default.xml`. In order to detect when new GeoServer releases update the default
configuration, GeoServer will check the current default configuration against the configuration in
:file:`csp_default.xml`. If they are found to be different:

* :file:`csp.xml` will be updated with the policies from the new default configuration if they
have not been changed from the old default configuration. Other settings will not be changed.
* :file:`csp_default.xml` will be updated with the new default configuration.

Configuring CSP
---------------

Navigate to :menuselection:`Data > Content Security Policy` to manage and configure the CSP header.

.. figure:: images/csp-config.png

CSP Configuration

Use the :guilabel:`Enable Content-Security-Policy header` checkbox to enable/disable this security
feature if it has not been disabled using the ``geoserver.csp.shouldSetPolicy`` system property.

Use the :guilabel:`Inject proxy base URL into header` checkbox to inject the proxy base URL into
the ``form-action`` directive and all fetch directives that normally allow ``'self'``. This is only
necessary for certain use cases where web browsers are able to access a GeoServer host directly
rather than through the proxy and the HTML response contains absolute URLs to the proxy base URL.
This does not guarantee that other browser cross-origin restrictions will not prevent the page from
functioning properly.

Use the :guilabel:`Allowed sources for external web resources` text field to add sources to the
``font-src``, ``img-src``, ``style-src``, and ``script-src`` directives for static web files (if
not disabled by system property) and for WMS GetFeatureInfo HTML output (if enabled by system
property). This is intended to make it easier to allow loading these resources from a CDN or any
other external host. Only trusted hosts should be added here to prevent cross-site scripting
attacks. The ``geoserver.csp.externalResources`` system property will override this field if it has
been set.

Use the :guilabel:`Allowed frame-ancestors directive sources` text field to control the sources of
the ``frame-ancestors`` directive. This is intended to make it easier for administrators to allow
specific external hosts to load GeoServer content in frames. Only trusted hosts should be added
here to prevent clickjacking attacks. The ``geoserver.csp.frameAncestors`` system property will
override this field if it has been set.

Configuring Policies
--------------------

Each policy contains the rules and directives to set a single CSP header value. When there are CSP
directives from multiple policies, the directives will be concatenated into a single line using
commas rather than setting multiple Content-Security-Policy headers. If a value for a specific
directive is defined in multiple policies, web browsers will use the strictest value set for that
directive.

The button for adding policies can be found at the top of the :guilabel:`Policy List` table and a
policy can be edited by clicking on its name in the table or removed by clicking on the remove icon
at the end of the policy's row in the table. Policy positions can also be changed by using the
up/down arrows or by dragging and dropping the rule's row in the table.

.. figure:: images/csp-policy.png

CSP Policy Configuration

* A unique name must be provided in the :guilabel:`Name` text field when adding a new policy.
* The :guilabel:`Description` text field provides an optional description to help administrators
understand what the policy does.
* The :guilabel:`Enabled` checkbox will enable/disable the policy.

Configuring Rules
-----------------

Each rule contains a filter to match against user requests and the CSP directives to add to the
header value for matching requests. Rules will be checked in order against incoming requests and
only the first matching rule in each policy will be applied. If no rule in a policy matches the
request, then no directives will be added to the CSP header from that policy. If the matching
rule has no directives defined, then preceding rules will be checked until the first rule is
found that has directives and no directives will be added if no such rule exists.

The button for adding rules can be found at the top of the :guilabel:`Rule List` table and a rule
can be edited by clicking on its name in the table or removed by clicking on the remove icon at the
end of the rule's row in the table. Rule positions can also be changed by using the up/down arrows
or by dragging and dropping the rule's row in the table.

.. figure:: images/csp-rule.png

CSP Rule Configuration

* A name that is unique among the rules within the specific policy must be provided in the
:guilabel:`Name` text field when adding a new rule.
* The :guilabel:`Description` text field provides an optional description to help administrators
understand what the rule does.
* The :guilabel:`Enabled` checkbox will enable/disable the rule.
* The :guilabel:`Request Filter` text field contains the filter to apply to each user request to
determine whether to add this rule's directives to the CSP header value. (see
:ref:`security_csp_filters` below)
* The :guilabel:`Header Directives` text field contains the CSP directives to add to the header
value when a request matches this rule's filter. (see :ref:`security_csp_directives` below)

.. _security_csp_filters:

Request Filters
---------------

The filter contains a string of predicates concatenated with the string ``AND`` and the rule's
directives will be applied to a request only if all of the predicates match the request. There
are several different predicates that can be used:

* CLASS(name): Returns true if the specified class name is available on the classpath. For security
reasons, the class name must be in the ``org.geoserver``, ``org.geotools``, or
``org.geowebcache`` package or a subpackage. This is intended to restrict rules to only apply
when a specific module is installed.
Example: ``CLASS(org.geoserver.web.GeoServerApplication)``
* PROP(key,value_regex): Returns true if the value for the property key matches the regex. Keys are
case-sensitive and must contain must contain the string ``GeoServer``, ``GeoTools``, or
``GeoWebCache`` anywhere in the key (case-insensitive). The regex will be tested against an empty
string if the property is not set.
Example: ``PROP(GEOSERVER_CONSOLE_DISABLED,(?i)^(?!true).*$)``
* METHOD(methods): Returns true if the HTTP request method is in the comma-separated list of
allowed methods.
Example: ``METHOD(GET,HEAD)``
* PATH(regex): Returns true if the URL-decoded request path matches the regular expression. The
regex will be tested against the path that is relative to GeoServer's context root and starting
with a forward slash.
Example: ``PATH(^/([^/]+/){0,2}wms/?$)``
* QUERY(regex): Returns true if the raw request query string matches the regular expression. The
regex will be tested against an empty string if the URL did not include a query.
Example: ``QUERY(^$)``
* PARAM(key_regex,value_regex): Returns true if all query parameters with a URL-decoded key that
match the key_regex have a URL-decoded value that match the value_regex. The value regex will be
tested against an empty string if no query parameters matched the key regex.
Example: ``PARAM((?i)^service$,(?i)^wms$)``

.. note::
The ``(?i)`` at the beginning of the regular expression will use case-insensitive matching and
enclosing the pattern inside of the ``^$`` characters will match the entire string.

Leaving the filter blank will cause this rule to match all requests and should only be used on the
last rule in a policy.

.. _security_csp_directives:

Header Directives
-----------------

.. warning::
GeoServer gives administrators complete control over the CSP header directives and sources and
does not attempt to parse or validate them so it is the administrator's responsibility to
verify that the header is working as intended when modifying this field. See
:ref:`security_csp_references` for detailed information about valid Content Security Policy
header directives and sources.

Property keys can be used in the directives in the form ``${key}`` and they will be replaced with
the property's value before being written to the header. Property keys must contain ``GeoServer``,
``GeoTools``, or ``GeoWebCache`` (case-insensitive) and property values must not contain special
characters that are not allowed in valid CSP sources. Properties can be set either via Java system
property, command line argument (-D), environment variable or web.xml init parameter.
``geoserver.csp.externalResources`` and ``geoserver.csp.frameAncestors`` are special property keys
that will use the value from their corresponding fields in the CSP configuration if they are not
defined as properties.

``proxy.base.url`` is a special property key that can be used to add the proxy base URL into the
header if the request was not sent through the proxy. It will automatically be injected into the
form-action and all fetch directives with a ``'self'`` source when the
:guilabel:`Inject proxy base URL into header` feature is enabled, Only the protocol, host and port
of the proxy base URL will be added to the header. The ``X-Forwarded-Proto``, ``X-Forwarded-Host``,
``X-Forwarded-Port``, ``Forwarded`` and ``Host`` HTTP request headers are used to determine whether
or not the original request was sent to the proxy. Ensure that the proxy server is properly setting
these headers if the proxy base URL is being included in requests through the proxy.

.. note::
Because the CSP is set so early in GeoServer's request handling, a current limitation is that
it can not use proxy base URLs that depend on HTTP request headers.

Leaving the directives blank will cause this rule to use the directives from the first preceding
rule with directives. No header value will be assigned if all preceding rules have no directives.
It does not matter whether a rule is enabled or disabled when searching preceding rules for
directives. The keyword ``NONE`` can be used to specify that no header value will be assigned to
requests that match this rule.

Fallback Directives
-------------------

When an administrator is directly editing the CSP configuration file or uploading it through the
REST Resources API, it is possible to create a file that GeoServer cannot parse. In these cases,
GeoServer will fall back to using very strict header directives until the configuration file is
fixed. The ``geoserver.csp.fallbackDirectives`` property can be set either via Java system
property, command line argument (-D), environment variable or web.xml init parameter to change the
fallback directives from the following default value:

::
base-uri 'none'; form-action 'none'; default-src 'none'; frame-ancestors 'none';

The keyword ``NONE`` can be used to specify that no header value will be assigned to rquests when
there are CSP configuration errors.

Testing
-------

The :guilabel:`Test Content Security Policy` form allows a URL to be checked, reporting the CSP
header value that would be set for a GET request to that URL.

Enter the URL to test in the :guilabel:`Test URL` text field and press the :guilabel:`Test` button
to perform the test. The :guilabel:`Content-Security-Policy header value` text field will contain
the CSP for the test URL with the string `NONE` being shown if no header would be set.

.. figure:: images/csp-test.png

Test CSP with URL

.. _security_csp_references:

References
----------

See the following pages for details about CSP:

* `OWASP Cheat Sheet <https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html>`_
* `Mozilla Reference <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy>`_
* `Wicket Reference <https://nightlies.apache.org/wicket/guide/10.x/single.html#_content_security_policy_csp>`_
Binary file added doc/en/user/source/security/images/csp-config.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/en/user/source/security/images/csp-policy.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/en/user/source/security/images/csp-rule.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/en/user/source/security/images/csp-test.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions doc/en/user/source/security/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ The first page discusses configuration options in the web administration interfa
layer
rest
urlchecks
csp
disable
tutorials/index
15 changes: 15 additions & 0 deletions doc/en/user/source/tutorials/GetFeatureInfo/html.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,21 @@ Auto-Escaping
`````````````
Auto-escaping can be used to escape special characters so that they are displayed correctly in clients and to prevent injection. GeoServer administrators can enable or disable auto-escaping for FreeMarker template values for the HTML output format on a global or per virtual service basis. Template authors are able to override the WMS service setting to enable or disable escaping on a per template, per block or per value basis. See `Auto-escaping <https://freemarker.apache.org/docs/dgui_misc_autoescaping.html>`_ for more information.

Content Security Policy
```````````````````````
The ``Content-Security-Policy`` header will block unsafe JavaScript by default. In order to allow
``'unsafe-inline'`` and ``'unsafe-eval'`` scripts or loading font, image, style or script resources
from external hosts, the ``GEOSERVER_FEATUREINFO_HTML_SCRIPT`` property can be set either via Java
system property, command line argument (-D), environment variable or web.xml init parameter. The
property can be set to either ``SELF`` or ``UNSAFE`` with ``SELF`` being the default value.

See :ref:`security_csp` for instructions to modify the CSP header if it is continuing to block
certain functionality of custom HTML templates even with the ``UNSAFE`` property.

.. warning::
Allowing unsafe scripts could allow cross-site scripting attacks and should only be done if you
can fully trust your template authors.

Accessing static methods
````````````````````````
It is possible to call static methods and variables from within Freemarker templates to enable more sophisticated templates.
Expand Down

0 comments on commit 81c8190

Please sign in to comment.