Skip to content

Commit

Permalink
Merge pull request #213 from atlassian-forks/WW-5364-allowlist
Browse files Browse the repository at this point in the history
WW-5364 Document new OGNL security features
  • Loading branch information
kusalk authored Dec 5, 2023
2 parents 0d19038 + e2b7f75 commit 1528cc8
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 65 deletions.
4 changes: 3 additions & 1 deletion source/plugins/plugins-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ The following extension points are available in Struts 2:
| struts.acceptedPatterns.checker | Used across different interceptors to check if given string matches one of the accepted patterns | request | com.opensymphony.xwork2.AcceptedPatternsChecker |
| struts.contentTypeMatcher | Matches content type of uploaded files (since 2.3.22) | singleton | org.apache.struts2.util.ContentTypeMatcher |
| struts.localizedTextProvider | Provides access to resource bundles used to localise messages (since 2.5.11) | singleton | com.opensymphony.xwork2.LocalizedTextProvider |
| struts.date.formatter | Allow define a date formatter used by `<s:date/>` tag (since 6.0.0) | singleton | org.apache.struts2.components.date.DateFromatter |
| struts.date.formatter | Allow define a date formatter used by `<s:date/>` tag (since 6.0.0) | singleton | org.apache.struts2.components.date.DateFormatter |
| struts.ognlGuard | Define a custom OgnlGuard implementation to block raw or compiled OGNL expressions (since 6.4.0) | singleton | org.apache.struts2.ognl.OgnlGuard |
| struts.securityMemberAccess | Define a custom SecurityMemberAccess implementation, used to restrict OGNL evaluations based on classes involved (since 6.4.0) | prototype | com.opensymphony.xwork2.ognl.SecurityMemberAccess |

## Plugin Examples

Expand Down
209 changes: 145 additions & 64 deletions source/security/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ As mentioned in [S2-006](https://cwiki.apache.org/confluence/display/WW/S2-006)
error pages. This avoids exposing users to XSS attacks as Struts does not escape action's names in automatically
generated error pages.

You can eaither disable [DMI](../core-developers/action-configuration#dynamic-method-invocation)
You can either disable [DMI](../core-developers/action-configuration#dynamic-method-invocation)

```xml
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
Expand All @@ -176,66 +176,7 @@ or define an error page
</global-exception-mappings>
```

### Proactively protect from OGNL Expression Injections attacks if easily applicable

The framework has a history of critical security bugs, many tied to its use of OGNL technology; Due to its ability to
create or change executable code, OGNL is capable of introducing critical security flaws to any framework that uses it.
Multiple Struts 2 versions have been vulnerable to OGNL security flaws. Consequently, we've equipped OGNL and the
framework with following proactive optional possibilities since OGNL 3.1.24 and Struts 2.5.22. They're disabled by
default but via enabling them, you can proactively protect from potential still unknown OGNL Expression Injections flaws:

> **NOTE**: These might break your current app functionality. Before using in production environment, you're recommended
> to comprehensively test your app UI and functionalities with these enabled.
#### Run OGNL expressions inside sandbox

You can do this simply via adding `-Dognl.security.manager` to JVM arguments. OGNL thereupon utilizes Java Security
Manager to run OGNL expressions (which includes your actions either!) inside a sandbox with no permission. It is worth
noting that it affects only OGNL expression execution and thereafter OGNL reverts Java Security Manager to its previous
state.

#### Apply a maximum allowed length on OGNL expressions

You can enable this via Struts configuration key `struts.ognl.expressionMaxLength`. OGNL thereupon doesn't evaluate any
expression longer than specified value. You would choose a value large enough to permit ALL valid OGNL expressions used
within the application. Values larger than the 200-400 range have diminishing security value (at which point it is
really only a "style guard" for long OGNL expressions in an application).

## Internal security mechanism

The Apache Struts 2 contains internal security manager which blocks access to particular classes and Java packages -
it's a OGNL-wide mechanism which means it affects any aspect of the framework ie. incoming parameters, expressions
used in JSPs, etc. Matching is done based on both the target and member class of an OGNL expression.

There are 4 options that can be used to configure excluded packages and classes:

- `struts.excludedClasses`: comma-separated list of excluded classes. Note that superclasses are also matched.
- `struts.excludedPackageNames`: comma-separated list of excluded packages, matched using string
comparison via `startWith`. Note that classes in subpackages are also excluded.
- `struts.excludedPackageNamePatterns` - comma-separated list of RegEx patterns used to exclude packages. Note that this
option is slower than string comparison but more flexible.
- `struts.excludedPackageExemptClasses` - comma-separated list of classes to exempt from any of the excluded packages or
package name patterns. An exact exemption must exist for each exclusion match (target or member or both).

The defaults are defined [here](https://github.com/apache/struts/blob/master/core/src/main/resources/struts-excluded-classes.xml).

Any expression or target which evaluates to one of these will be blocked and you see a WARN in logs:

```
[WARNING] Target class [class example.MyBean] or declaring class of member type [public example.MyBean()] are excluded!
```

In that case `new MyBean()` was used to create a new instance of class (inside JSP) - it's blocked because `target`
of such expression is evaluated to `java.lang.Class`

It is possible to redefine the above constants in struts.xml but try to avoid this and rather change design of your application!

### Accessing static methods

Support for accessing static methods from expression will be disabled soon, please consider re-factoring your application
to avoid further problems! Please check [WW-4348](https://issues.apache.org/jira/browse/WW-4348).

### OGNL is used to call action's methods
### Ambiguous Action Methods

This can impact actions which have large inheritance hierarchy and use the same method's name throughout the hierarchy,
this was reported as an issue [WW-4405](https://issues.apache.org/jira/browse/WW-4405). See the example below:
Expand Down Expand Up @@ -267,12 +208,12 @@ In such case OGNL cannot properly map which method to call when request is comin
To solve the problem don't use the same method's names through the hierarchy, you can simply change the action's method
from `save()` to `saveAction()` and leaving annotation as is to allow call this action via `/save.action` request.

### Accepted / Excluded patterns
### Accepted / Excluded Patterns

As from version 2.3.20 the framework provides two new interfaces which are used to accept / exclude param names
and values - [AcceptedPatternsChecker](../maven/struts2-core/apidocs/com/opensymphony/xwork2/security/AcceptedPatternsChecker)
and [ExcludedPatternsChecker](../maven/struts2-core/apidocs/com/opensymphony/xwork2/security/ExcludedPatternsChecker)
with default implementations. These two interfaces are used by [Parameters Interceptor](../core-developers/parameters-interceptor)
with default implementations. These two interfaces are used by the [Parameters Interceptor](../core-developers/parameters-interceptor)
and [Cookie Interceptor](../core-developers/cookie-interceptor) to check if param can be accepted or must be excluded.
If you were using `excludeParams` previously please compare patterns used by you with these provided by the framework in default implementation.

Expand All @@ -286,7 +227,7 @@ more in the Strict Method Invocation section of [Action Configuration](../core-d

> Note: since Struts 6.0.0
Fetch Metadata is a mitigation against common cross origin attacks such as Cross-Site Request Forgery (CSRF). It is
Fetch Metadata is a mitigation against common cross-origin attacks such as Cross-Site Request Forgery (CSRF). It is
a web platform security feature designed to help servers defend themselves against cross-origin attacks based
on the preferred resource isolation policy. The browser provides information about the context of an HTTP request
in a set of `Sec-Fetch-*` headers. This allows the server processing the request to make decisions on whether the request
Expand Down Expand Up @@ -328,3 +269,143 @@ Cross-Origin-Opener-Policy: same-origin;

COOP and COEP are implemented in Struts using [CoopInterceptor](../core-developers/coop-interceptor)
and [CoepInterceptor](../core-developers/coep-interceptor).

## Proactively protecting against OGNL Expression Injections attacks

The framework has a history of critical security bugs, many tied to its use of OGNL technology; Due to its ability to
create or change executable code, OGNL is capable of introducing critical security flaws to any framework that uses it.
Multiple Struts 2 versions have been vulnerable to OGNL security flaws. Consequently, we've equipped OGNL and the
framework with a number of additional security capabilities, some of which need to be manually enabled.

> **NOTE**: These might break your current app functionality. Before using in production environment, you're recommended
> to comprehensively test your app UI and functionalities with these enabled.
### Run OGNL expressions inside sandbox

You can do this simply via adding `-Dognl.security.manager` to JVM arguments. OGNL thereupon utilizes Java Security
Manager to run OGNL expressions (which includes your actions either!) inside a sandbox with no permission. It is worth
noting that it affects only OGNL expression execution and thereafter OGNL reverts Java Security Manager to its previous
state.

Note: This feature does not work with JDK 21 and above.

### Apply a maximum allowed length on OGNL expressions

You can enable this via Struts configuration key `struts.ognl.expressionMaxLength` (defaults to 256). OGNL thereupon doesn't evaluate any
expression longer than specified value. You would choose a value large enough to permit ALL valid OGNL expressions used
within the application. Values larger than the 200-400 range have diminishing security value (at which point it is
really only a "style guard" for long OGNL expressions in an application).

### OGNL Member Access

Struts 2 implements an OGNL internal security mechanism which blocks access to particular classes and Java packages -
it's an OGNL-wide mechanism which means it affects any aspect of the framework i.e. incoming parameters, expressions
used in JSPs, etc. Matching is done based on both the target and member class of all components of an OGNL expression.

There are 4 options that can be used to configure excluded packages and classes:

- `struts.excludedClasses`: comma-separated list of excluded classes.
- `struts.excludedPackageNames`: comma-separated list of excluded packages, matched using string comparison via
`startWith`. Note that classes in subpackages are also excluded.
- `struts.excludedPackageNamePatterns` - comma-separated list of RegEx patterns used to exclude packages. Note that this
option is more flexible than `struts.excludedPackageNames` but will have a greater impact on performance and page
latency.
- `struts.excludedPackageExemptClasses` - comma-separated list of classes to exempt from any of the excluded packages or
package name patterns. An exact exemption must exist for each exclusion match (target or member or both).

The defaults are defined [here](https://github.com/apache/struts/blob/master/core/src/main/resources/struts-excluded-classes.xml).

Additionally, static methods are blocked, and static fields can also be blocked with 'struts.allowStaticFieldAccess'.

Any expression or target which does not pass this criteria will be blocked, and you will see a warning in the logs:

```
[WARNING] Target class [class example.MyBean] or declaring class of member type [public example.MyBean()] are excluded!
```

In that case `new MyBean()` was used to create a new instance of class (inside JSP) - it's blocked because the `target`
of such expression is `java.lang.Class` which is excluded.

It is possible to redefine the above constants in `struts.xml`, but avoid reducing the list, instead extending the list
with other known dangerous classes or packages in your application.

#### Allowlist Capability

> Note: since Struts 6.4.
For even more stringent OGNL protection, we recommend enabling the allowlist capability with `struts.allowlist.enable`.

Now, in addition to enforcing the exclusion list, classes involved in OGNL expression must also belong to a list of
allowlisted classes and packages. By default, all required Struts classes are allowlisted as well as any classes that
are defined in your `struts.xml` package configurations.

You can add additional classes and packages to the allowlist with:

- `struts.allowlist.classes`: comma-separated list of allowlisted classes.
- `struts.allowlist.packages`: comma-separated list of allowlisted packages, matched using string comparison via
`startWith`. Note that classes in subpackages are also allowlisted.

Generally, the only additional classes or packages you will need to configure are those model classes that you wish to
be constructed/manipulated by Struts form submissions (i.e. parameter injected).

#### Extensibility

> Note: since Struts 6.4.
The OGNL Member Access mechanism is extensible, allowing you to define your own rules for blocking access to OGNL
expression evaluations. To do so, you may use the `struts.securityMemberAccess` extension point. Please be vigilant when
overriding methods as not to reduce protections offered by the default implementation.

### Struts OGNL Guard

> Note: since Struts 6.4.
The Struts OGNL Guard allows applications to completely disable certain OGNL expression features/capabilities. This
feature is disabled by default but can be enabled and configured with `struts.ognl.excludedNodeTypes`.

It is recommended to disable any OGNL feature you are not leveraging in your application. For applications using a
minimal number of Struts features, you may find the following list a good starting point.

Please be aware that this list WILL break certain Struts features:

```xml
<constant name="struts.ognl.excludedNodeTypes"
value="
ognl.ASTAdd,
ognl.ASTAssign,
ognl.ASTBitAnd,
ognl.ASTBitNegate,
ognl.ASTBitOr,
ognl.ASTCtor,
ognl.ASTDivide,
ognl.ASTEval,
ognl.ASTIn,
ognl.ASTInstanceof,
ognl.ASTKeyValue,
ognl.ASTList,
ognl.ASTMap,
ognl.ASTMultiply,
ognl.ASTNegate,
ognl.ASTNotIn,
ognl.ASTProject,
ognl.ASTRootVarRef,
ognl.ASTSelect,
ognl.ASTSelectFirst,
ognl.ASTSelectLast,
ognl.ASTSequence,
ognl.ASTShiftLeft,
ognl.ASTShiftRight,
ognl.ASTStaticField,
ognl.ASTStaticMethod,
ognl.ASTThisVarRef,
ognl.ASTUnsignedShiftRight,
ognl.ASTVarRef,
ognl.ASTXor
"/>
```

#### Extensibility

The Struts OGNL Guard mechanism is extensible, allowing you to define your own rules for blocking access to both raw
OGNL expressions and compiled syntax trees. To do so, you may use the `struts.ognlGuard` extension point. You may choose
to override the default implementation or implement the `OgnlGuard` interface directly.

0 comments on commit 1528cc8

Please sign in to comment.