To be Reviewed By: July 12th 2019
Authors: Juan José Ramos
Status: Draft | Discussion | Development | Active | Dropped | Superseded
Superseded by: N/A
Related: N/A
...
Table of Contents |
---|
...
Introduction
The query engine allows applications to access public field values or execute methods on the objects stored within a Geode Region
. If a field used in query is not declared as public
within the object, the query engine automatically tries to get the value for the field using accessor methods. Prior to the release of Geode 1.3.0, OQL used to allow any method invocation on objects present in the member’s classpath, including setters
mutators
and, through the usage of Java Reflection, internal Geode, and even JDK or external library (Spring, Tomcat, etc.), methods. This could impact the integrity of the data, the region, and the platform on which Geode is running (for more details see CVE-2017-9795).
As part of GEODE-3247, several options were analysed and, after considering the wealth the weight of security holes and the difficulty of determining which methods deployed by the developer were intended to be available for queries and which were not, the decision was made to tighten up the Security and, by default, disallow any method call not explicitly added to an acceptance list.
After the solution was released and deployed by several users, the feedback wasn’t exactly the best and the overall feeling was that enabling this feature made OQL unusable. Our customers ended up changing the access of the attributes on their domain model to be public
so the Field
can be directly accessed by the query engine without requiring a method invocation (we shouldn't impose this restriction on the data model!!) or disabling OQL security altogether through the system property QueryService.allowUntrustedMethodInvocation
.
We certainly know that malicious users can write complex OQL expression and potentially compromise the entire system, but just disallowing everything is not the best solution; allowing normal method invocations on the user’s domain model should be allowed and easily achieved. That said, we know also that we can’t automatically define the trust boundaries for deployed code, and that trusting by default all code deployed by a user with enough privileges is not feasible either: the user might have deployed the code but it might not want that code to be invoked through OQL by a READ only user. What we can do, however, is to make the integration easier and provide our customers with the right tools and means to easily decide which methods should be allowed and which ones should be denied.
As a summary, the current OQL security implementation is too restrictive and turns the whole OQL feature almost unusable when security is enabled. The experience has also demonstrated that there is no "One size fits all" solution for this requirement, so the goal of this document is to analyze possible options to loosen the security restrictions and facilitate the developer’s experience, along with taking a final step to decide which option to pursue once the community has approved the suggestions.
From now on and in the context of this document, Method Authorization Implementation refers to the actual mechanism/execution of the logic involved in determining whether a specific method should be allowed or denied during the execution of a particular query.
Goals
- Method Authorization Implementation should be pluggable.
- Method Authorization Implementation should be “on” by default when Security is enabled at cluster level.
- Geode should prevent RCE (Remote Code Execution) exploits and other vulnerabilities in OQL expressions.
- Users should be able to invoke methods on domain classes (present on the system classpath or deployed through gfsh) as part of OQL, relatively easy and with little to no configuration changes.
Requirements
The main threats, already addressed through GEODE-3247, are shown below:
- Reflection (Allows the user to do everything)
SELECT * FROM /region r WHERE r.getClass().forName('java.lang.Runtime').getDeclaredMethods()[0].invoke()
- Cache Access (Allows the user to do anything with the cache: closing, accessing all regions, etc.)
SELECT * FROM /region.getCache().close()
- Region Access (Allows the user to destroy, add or invalidate the entire region or specific entries)
SELECT * FROM
/region.destroyRegion()
SELECT * FROM
/region.invalidate('xyz')
SELECT * FROM
/region.put('xyz','abc') - Entry Modification (Allows the user to modify an object in place)
SELECT
r.setName('Zaraza') FROM /region r
Problem
After the solution was released and deployed by several users, the feedback received wasn’t the best and the overall feeling was that enabling this feature made OQL unusable. Our customers ended up changing the access of the attributes on their domain model to be public
so the Field
can be directly accessed by the query engine without requiring a method invocation (we shouldn't impose this restriction on the data model!!) or disabling OQL security altogether through the system property QueryService.allowUntrustedMethodInvocation
.
We certainly know that malicious users can write complex OQL expression and potentially compromise the entire system, but just disallowing everything is not the best solution; allowing method invocations on the user’s domain model should be configurable and easily achieved. That said, we also know that we can’t automatically define the trust boundaries for deployed code, and that trusting by default all code deployed by a user with enough privileges is not feasible either: the user might have deployed the code but it might not want that code to be invoked through OQL by a READ only user. What we can do, however, is to make the integration easier and provide our customers with the right tools and means to easily decide which methods should be allowed and which ones should be denied.
As a summary, the current OQL security implementation is too restrictive and turns the whole OQL feature almost unusable when security is enabled. The experience has also demonstrated that there is no "One size fits all" solution for this requirement, so we need to analyze possible options to loosen the security restrictions and facilitate the developer’s experience, along with taking a final step to decide which option to pursue once the community has approved the proposal.
From now on and in the context of this document, Method Authorization Implementation refers to the actual mechanism/execution of the logic involved in determining whether a specific method should be allowed or denied during the execution of a particular query.
Goals
- Method Authorization Implementation should be pluggable.
- Method Authorization Implementation should be "on" by default when Security is enabled at cluster level.
- Method Authorization Implementation should be stored and retrieved through the cluster configuration service.
- The
Developers/Operators should be allowed to provide their own Method Authorization implementation.
The Method Authorization Implementation should be stored and retrieved through the cluster configuration service.
- The current system property
QueryService.allowUntrustedMethodInvocation
should be kept but marked as deprecated. - Geode should prevent RCE (Remote Code Execution) exploits and other vulnerabilities in OQL expressions when Security is enabled at cluster level.
- The Method Authorization Implementation should be configurable/exchangeable in runtime (doesn’t need to be instantaneous update, eventual consistency is tolerable).
Current Implementation
MethodInvocationAuthorizer
The single internal interface responsible of verifying whether a method can be invoked on a certain object or not:
public interface MethodInvocationAuthorizer {
void authorizeMethodInvocatioon(Method method, Object target);
}
The implementation first checks whether the method is part of the allowed list. If it is and the target object is an actual Geode region (passed as a bind parameter to the query), it proceeds to check whether the user has the required privileges to execute queries on that particular region (DATA:READ:regionName
). The current approach denies everything and only allows some methods to be executed, this list can not be configured or changed in runtime, it’s hard coded and any addition/deletion requires a new release of the product. The list of currently allowed methods is attached below.
- Users should be able to invoke methods on domain classes (present on the system class path or deployed through gfsh) as part of OQL, relatively easy and with little to no configuration changes.
Current Implementation
In order to execute any query, the user executing it needs to have the DATA:READ:regionName ResourcePermission
assigned to it, that's the first thing checked no matter where the query execution comes from. Once the verification succeeds, the query engine proceeds to execute the query and, whenever needed, executes the Method Authorization Implementation.
MethodInvocationAuthorizer
The single internal interface responsible of verifying whether a method can be invoked on a certain object or not:
public interface MethodInvocationAuthorizer {
void authorizeMethodInvocatioon(Method method, Object target);
}
The single implementation (RestrictedMethodInvocationAuthorizer
) first checks whether the method is part of the allowed list. If it is and the target object is an actual Geode region (passed as a bind parameter to the query), it proceeds to check whether the user has the required privileges to execute queries on that particular region (DATA:READ:RegionName
). The current approach denies everything and only allows some methods to be executed, this list can not be configured or changed in runtime, it’s hard coded and any addition/deletion requires a new release of the product. The list of currently allowed methods is attached below.
Class | Method |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Class | Method |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
There is one instance of MethodInvocationAuthorizer
per QueryService:
Geode creates a new one every time the user requests a QueryService
from the Cache instance. Within the initialization:
- If the
SecurityManager
is not enabled, or if the flagQueryService.allowUntrustedMethodInvocation
is set astrue
, theMethodInvocationAuthorizer
is created as a no-op (no security). - If the
SecurityManager
is enabled and the flagQueryService.allowUntrustedMethodInvocation
is set asfalse
(default), theRestrictedMethodInvocationAuthorizer
is configured.
The method authorizeMethodInvocation
is only invoked during the OQL execution through the MethodDispatch
and AttributeDescriptor
classes. It's important to note that the query engine doesn't have any information about the actual type of the objects while pre processing or parsing the query string (neither can it be obtained before executing the query), the actual check to determine whether a method is allowed or not (authorizeMethodInvocation
) is executed for every object traversed by the query while it's processing the intermediate results.
MethodDispatch
The class is used to execute a specific an arbitrary method on a target object while an OQL is being executed, is worth noticing that currently there is no context nor metadata about what where the target class is or where it comes from.
The class is only used for explicit method invocations. As an example, within the expression user.isEnabled()
the method would be isEnabled() and the target object would be the actual runtime type/class for user. The single entry point to this class is the invoke()
method, which is called through CompiledOperation.evaluate(ExecutionContext context)
. This particular class, CompiledOperation
, is just a node within the AST tree generated by the OQLParser
, and it basically represents a method invocation in OQL.
...
return Method.invoke("user")
}
AttributeDescriptor
The class is used to read a specific attribute from a target object while an OQL is being executed, is worth noticing again that currently there is no context nor metadata about what the target class is or where it comes from.
As an example, within the expression user.name
the attribute would be name and the target object would be the actual runtime type/class for user. The single entry point to this class is the read(Object target)
method, which is basically invoked through CompiledPath.evaluate(ExecutionContext context)
. This class, CompiledPath
, is just a node within the AST tree generated by the OQLParser
, and it represents an identifier that follows a “dot” operator.
Assuming that the OQL is looking for user.name
, below is a simplified pseudo code of how this is achieved in runtime:
read {
if "user" instance of PdxInstance
if "name" is a field directly accessible through the PdxInstance
read "name" using only the PdxInstance (getRawField, getPdxField, etc)
else
deserialize the PDX object and get an instance of "user"
readReflection
else
readReflection
}
readReflection {
MethodDispatch
class is created based on the method name, the class on which the method will be invoked and the type of its arguments, and it is stored within an internal local cache that never expires. The authorize
method, however (and no matter that the current acceptance list is immutable), is called by the MethodDispatch
class for every method invocation and every object traversed by the query.
AttributeDescriptor
The class is used to read a specific attribute from a target object while an OQL is being executed, is worth noticing again that currently there is no context nor metadata about where the target class comes from.
The class is used for accessing attributes directly and implicit method invocations. As an example, within the expression user.name
the attribute would be name and the target object would be the actual runtime type/class for user. The single entry point to this class is the read(Object target)
method, which is basically invoked through CompiledPath.evaluate(ExecutionContext context)
. This class, CompiledPath
, is just a node within the AST tree generated by the OQLParser
, and it represents an identifier that follows a “dot” operator.
Assuming that the OQL is looking for user.name
, below is a simplified pseudo code of how this is achieved in runtime:
read {
if "user" instance of PdxInstance
if "name" is a field directly accessible through the PdxInstance
read "name" using only the PdxInstance (getRawField, getPdxField, etc)
else
deserialize the PDX object and get an instance of "user"
readReflection
else
readReflection
}
readReflection {
member = getReadMember() // accessType = get available access strategy to "name" attribute on "user" instance through reflection
switch (accessTypemember):
case Field: {
return Field.get("user")
...
return Method.invoke(“user”)
}
}
Proposal
Summary
The AttributeDescriptor
class stores within an internal local cache that never expires the actual Member
read by reflection; the authorize
method, however (and no matter that the current acceptance list is immutable), is called by the AttributeDescriptor
class for every non public field for which there's a accessor method available on every object traversed by the query.
Solution
Summary
Make the currently internal MethodInvocationAuthorizer
Make the currently internal MethodInvocationAuthorizer
interface public and allow users to provide their own, it will hold the entire Method Authorization Implementation. The QueryService
will use an empty implementation of the interface if security is disabled or the flag QueryService.allowUntrustedMethodInvocation
is set as true
(as it works right now). If security is enabled, the default RestrictedMethodAuthorizer
will be set or, if configured, the custom implementation will be used.
Based on the comments added to the original proposal, and the feedback gathered through the Geode dev list within the relevant thread, and having in mind the current requirements, the following implementations the following implementations should be provided out of the box:
RestrictedMethodAuthorizer
GeodeBasedMethodAuthorizer
RegexBasedMethodAuthorizer
JavaBeanAccessorBasedMethodAuthorizer
The RestrictedMethodAuthorizer
will be the authorizer used by default out of the box, as it contains the list of methods per object type that are currently considered safe and it also prevents any possible re-introduction of CVE-2017-9795.
The GeodeBasedMethodAuthorizer
will basically allow any method execution as long as the target object does not belong to a Geode package or, if it does belong to a geode package, it's considered safe (sub-set of methods already allowed by the RestrictedMethodAuthorizer
). The other two authorizers will always delegate to RestrictedMethodAuthorizer
and expand the set of allowed methods with whatever . The other two authorizers will only permit the methods currently allowed by RestrictedMethodAuthorizer
plus whatever the internal implementation is configured to do. The JavaBeanAccessorBasedMethodAuthorizer
covers the most common use cases and requires little to no configuration effort. For those use cases not covered, the users can choose to use the RegexBasedMethodAuthorizer
, which allows them to configure which methods to allow directly through regex expressions. If none of the above is a good fit for a particular situation, the user ultimately has the option to provide its own implementation of the MethodInvocationAuthorizer
interface and do whatever needed in order to allow/deny the execution of particular methods.
Implementation Details
This section is just an overview and it contains some ideas of how the proposal could be achieved, no PoC has been done so far so the implementations details might certainly change.
...
All out of the box authorizers will be implemented to prevent security problems but, due to the fact that we can't automatically detect in place modifications nor automatically define the trust boundary, the configurable ones will require extra care regarding the configuration or domain model design on the user side. The following table presents a brief summary of what "threats" (of the ones shown within the Introduction) are fully addressed by each implementation, and which ones might be exploitable depending on how the administrator configures the authorizer (the details will be described in each individual section when applicable, and clear documentation around this should be added to the user guide if we choose to implement these authorizers).
Authorizer \ Threat | Reflection | Cache Access | Region Access | Entry Modification |
---|---|---|---|---|
RestrictedMethodAuthorizer | ||||
GeodeBasedMethodAuthorizer | ||||
RegexBasedMethodAuthorizer | ||||
JavaBeanAccessorBasedMethodAuthorizer |
Implementation Details
This section is just an overview and it contains some ideas of how the proposal could be achieved, no PoC has been done so far so the implementations details might change.
draw.io Diagram | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Interface MethodInvocationAuthorizer (Public)
This interface is intended to be implemented by users that want a custom authorization mechanism, and by the out of the box implementations as well. The interface will have only one method and it should return a boolean
indicating whether the specified method
is allowed to be executed on the target
object or not. For those situations on which the authorization can not be determined, the non-checked NotAuthorizedException
should be thrown.
The authorize
method will be called for every traversed object as part of the query execution, so it's extremely important that the implementation is lighting fast.
public interface MethodInvocationAuthorizer {
boolean authorize(Method method, Object target) throws NotAuthorizedException;
}
Implementations of this interface must be thread-safe as more than one thread might invoke the method at the same time.
The MethodDispatch
class will be modified to remember whether the method was allowed by a previous invocation, preventing further calls to the respective authorizer
if it was; this basically means that the authorize
method for explicit method invocations will be called once in the lifetime of a Geode member for every new method seen while traversing the objects.
The AttributeDescriptor
class will be modified to remember whether the accessor method was allowed by a previous invocation, preventing further calls to the respective authorizer
if it was; this basically means that the authorize
method for implicit method invocations will be called once in the lifetime of a Geode member for every new accessor method to a non-public field seen while traversing the objects.
Class RestrictedMethodAuthorizer (Public)
The current RestrictedMethodInvocationAuthorizer
implementation logic, but made public so applications can delegate to this class and use it as the starting point for providing custom authorizers.
Configuration Options
Create a new QueryServiceConfig
element at the CacheConfig
level to contain any configuration related to OQL, including the custom MethodInvocationAuthorizer
. Even though at the beginning this new configuration element will be used to configure only the MethodInvocationAuthorizer
implementation, it’s worth noting that it provides a single configuration entry point for the whole query engine. This basically means that it can also be used in the future to allow further additions and configuration options, even replacing the current system properties used to configure the QueryService
with new child elements and/or attributes.
The resulting XML element would look something like the following (the properties are just examples):
<query-service>
<queryVerbose>true</queryVerbose>
<allowUntrustedMethodInvocation>false</allowUntrustedMethodInvocation>
<method-authorizer>
<class-name>test.Authorizer</class-name>
<parameter name="allowedMethodsDataBaseUrl">
<string>jdbc:mysql://myHost/allowedMethodsDatabase</string>
</parameter>
</method-authorizer>
</query-service>
This new configuration element and its properties should be stored and retrieved through the cluster configuration service, and must also be modifiable through gfsh
commands:
alter query-service --allowUntrustedMethodInvocation=false
alter query-service --method-authorizer=test.Authorizer{'param':'value'}
Out of the Box Implementations
The code below is shown only as an example, the final implementation might differ.
RestrictedMethodAuthorizer
The public implementation of the current RestrictedMethodInvocationAuthorizer
, basically denying everything except a small list of methods already known to be safe.
Advantages
- No extra configuration needed .
- Already implemented and tested .
- No chance of re-introducing CVE-2017-9795 .
Risks / Unknowns / Disadvantages
- Too restrictive .
- User can't use methods in queries .
- User can't use non-public fields in queries as the implicit method invocation is also denied .
GeodeBasedMethodAuthorizer
Allow any method execution as long as the target object does not belong to a Geode package, or does belong but it's marked as safe (Region.get, Region.entrySet, Region.keySet, Region.values, Region.getEntries, Region.getValues, Region.containsKey, Region.getKey
and Region.getValue
). Some known dangerous methods (like getClass
) will also be rejected, no matter whether the target object belongs to a Geode package or not. The implementation will be immutable and thread-safe.
When using the methodAuthorizer
, users with DATA:READ:RegionName
privileges will be able to execute ANY method (even those modifying the entry) on the objects stored within the region and on instances used as bind parameters of the query, so it must be used with extreme care and only in cases on which only trusted users and applications have access to the cluster.
@Override
public boolean authorize(Method method, Object target) throws NotAuthorizedException {
String packageName = target.getClass().getPackage().getName().toLowerCase();
if (restrictedAuthorizer.isKnownDangerousMethod(method, target) {
return false;
}
if (!packageName.startsWith("org.apache.geode")) {
return true;
}
return restrictedAuthorizer.isAllowedGeodeMethod(method, target);
}
Advantages
- Easy to use .
- No extra configuration needed .
- Implicit and Explicit methods can be executed on objects stored within the regions .
Risks / Unknowns / Disadvantages
- In place modifications are allowed .
- Users with
DATA:READ:RegionName
privileges can modify the entries within the region through methods that mutate the object, thus part of CVE-2017-9795 can be re-introduced .
RegexBasedMethodAuthorizer
Methods allowed to be executed should match some regex expression(s) configured by the user, similar to what we currently do today with the PDX ReflectionBasedAutoSerializer
. The implementation will have an internal structure containing already compiled Pattern
instances and use them to verify the actual method that should be executed by the OQL engine, denying or allowing the execution based on the match result. If there is no match, it will delegate the final decision to the RestrictedMethodAuthorizer
. The implementation will be immutable and thread-safe.
When using the methodAuthorizer
, if the regular expression is not restrictive enough, users with DATA:READ:RegionName
privileges will be able to execute methods (even those modifying the entry) on the objects stored within the region and on instances used as bind parameters of the query, so this authorizer must be used with extreme care.
@Override
public boolean authorize(Method method, Object target) throws NotAuthorizedException {
boolean matches = false;
String methodName = target.getClass().getName() + "." + method.getName();
if (restrictedAuthorizer.isKnownDangerousMethod(method, target) {
return false;
}
Iterator<Pattern> iterator = this.patternsCache.iterator();
while (iterator.hasNext() && !matches) {
matches = iterator.next().matcher(methodName).matches();
}
if (!matches) {
matches = restrictedAuthorizer.authorize(method, target);
}
return matches;
}
Advantages
- Easy to use and configure what to allow/deny .
- Regular expressions are standard, everyone “should know” how to use them .
Risks / Unknowns / Disadvantages
- Performance impact .
- Customers still need to configure “something” (the regex) .
- Customers need to learn regex expressions, if they don't do already .
- Operators with little Regex knowledge can accidentally allow everything depending on which wildcards are used and, thus, reintroduce part of CVE-2017-9795 .
JavaBeanAccessorBasedMethodAuthorizer
Allow the OQL engine to execute any method that follows the design patterns for accessor methods described in the JavaBean specification 1.01; that is, basically, allow any method starting with get
or is
. For extra security, only methods belonging to classes under certain packages (configured by the user) should be allowed, and some known dangerous methods (like getClass
) should be disabled. If there is no match, it will delegate the final decision to the RestrictedMethodAuthorizer
. The implementation will be immutable and thread-safe.
This methodAuthorizer
is fully based on the design patterns described in JavaBean specification 1.01 and, by definition, in the fact that accessor methods don't modify the instance's state. If an accessor method follows the naming conventions described in the JavaBean specification 1.01 but actually change the instance's state, users with with DATA:READ:RegionName
privileges will be able to make in place modifications, so this methodAuthorizer
must not be used in those scenarios
Interface MethodInvocationAuthorizer (Public)
This interface is intended to be implemented by users that want a custom authorization mechanism, and by the out of the box implementations as well. The interface will have only one method and it should return a boolean
indicating whether the specified method
is allowed to be executed on the target
object or not. For those situations on which the authorization can not be determined, the non-checked NotAuthorizedException
should be thrown.
public interface MethodInvocationAuthorizer {
boolean authorize(Method method, Object target) throws NotAuthorizedException;
}
Class RestrictedMethodAuthorizer (Public)
The current RestrictedMethodInvocationAuthorizer
implementation logic, but made public so applications can delegate to this class and use it as the starting point for providing custom authorizers.
Configuration Options
Create a new QueryServiceConfig
element at the CacheConfig
level to contain any configuration related to OQL, including the custom MethodInvocationAuthorizer
. Even though at the beginning this new configuration element will be used to configure only the MethodInvocationAuthorizer
implementation, it’s worth noting that it provides a single configuration entry point for the whole query engine. This basically means that it can also be used in the future to allow further additions and configuration options, even replacing the current system properties used to configure the QueryService
with new child elements and/or attributes.
The resulting XML element would look something like the following (the properties are just examples):
<query-service>
<queryVerbose>true</queryVerbose>
<allowUntrustedMethodInvocation>false</allowUntrustedMethodInvocation>
<method-authorizer>
<class-name>test.Authorizer</class-name>
<parameter name="allowedMethodsDataBaseUrl">
<string>jdbc:mysql://myHost/allowedMethodsDatabase</string>
</parameter>
</method-authorizer>
</query-service>
This new configuration element and its properties should be stored and retrieved through the cluster configuration service, and must also be modifiable through gfsh
commands:
alter query-service --allowUntrustedMethodInvocation=false
alter query-service --method-authorizer=test.Authorizer{'param':'value'}
Out of the Box Implementations
The code below is shown only as an example, the final implementation might differ.
RegexBasedMethodAuthorizer
Methods allowed to be executed should match some regex expression(s) configured by the user, similar to what we currently do today with the PDX ReflectionBasedAutoSerializer
. The implementation will have an internal structure containing already compiled Pattern
instances and use them to verify the actual method that should be executed by the OQL engine, denying or allowing the execution based on the match result. If there is no match, it will delegate the final decision to the RestrictedMethodAuthorizer
.
@Override
public boolean authorize(Method method, Object target) throws NotAuthorizedException {
boolean matches = false;
String methodName = target.getClass().getName() + "." + method.getName();
Iterator<Pattern> iterator = this.patternsCache.iterator();
while (iterator.hasNext() && !matches) {
matches = iterator.next().matcher(methodName).matches();
}
if (!matches) {
matches = this.restrictedAuthorizer.authorize(method, target);
}
return matches;
}
Advantages
- Easy to use and configure what to allow/deny.
- Regular expressions are standard, everyone “should know” how to use them.
Risks / Unknowns / Disadvantages
- Performance impact?.
- Customers still need to configure “something” (the regex).
- Customers need to learn regex expressions, if they don't do already.
- Operators with little Regex knowledge can accidentally allow everything, depending on which wildcards are used.
JavaBeanAccessorBasedMethodAuthorizer
Allow the OQL engine to execute any method that follows the design patterns for accessor methods described in the JavaBean specification 1.01; that is, basically, allow any method starting with get
or is
. For extra security, only methods belonging to classes under certain packages (configured by the user) should be allowed, and some known dangerous methods (like getClass
) should be disabled. If there is no match, it will delegate the final decision to the RestrictedMethodAuthorizer
.
@Override
public boolean authorize(Method method, Object target) throws NotAuthorizedException {
...
String packageName = target.getClass().getPackage().getName().toLowerCase()().toLowerCase();
if (restrictedAuthorizer.isKnownDangerousMethod(method, target) {
return false;
}
if ((methodName.startsWith("get") || methodName.startsWith("is")) && (!methodName.equalsIgnoreCase("getClass"))) {
Iterator<String> iterator = this.packagesCache.iterator();
...
if (!matches) {
matches = this. restrictedAuthorizer.authorize(method, target);
}
return matches;
}
Advantages
- No major changes neededneeded .
- Solves the general use case: most customers use
get*/is*
as the name for accessor methods and the configurable package restricts access only to methods from the domain modelmodel .
Risks / Unknowns / Disadvantages
Performance impact?.
Customers need to configure “something” (the package) .
Some There might be some safe accessor methods might that do not start with
get*/is* .
Not every method starting with
get*/is*
(even on the domain model) might be safe to invoke might be safe to invoke and, thus, this authorizer can reintroduce part of CVE-2017-9795 .
Examples
This section contains some examples showing how the feature should work, once it’s implemented, for different use cases. All examples assume that the cluster is already up and running with security enabled and that the current default method authorizer is configured (nothing is allowed). For the sake of simplicity, let’s also assume that the domain model is entirely contained within packages "order.model
" and "tickets.model
".
...
is allowed). For the sake of simplicity, let’s also assume that the domain model is entirely contained within packages "order.model
" and "tickets.model
".
Happy Path
# The cluster is secured, only in-house developed apps execute queries and operators/developers know which methods can be invoked or not.
# Customer deploys the jar and configures the GeodeBasedMethodAuthorizer
$> deploy --jar=/tmp/model-1.0.0.jar
$> alter query-service --method-authorizer=GeodeBasedMethodAuthorizer
# All classes follow the JavaBean specification 1.01 for accessor methods
...
$> alter query-service --method-authorizer=JavaBeanAccessorBasedMethodAuthorizer{'packages' : 'order.model,tickets.model'}
Change Method Authorizer in Runtime
# All classes follow the JavaBean specification 1.01 for accessor methods
...
alter query-service --method-authorizer=JavaBeanAccessorBasedMethodAuthorizer{'packages' : 'order.model,tickets.model'}
Custom Method Authorizer with Hot Deploy
# Out of the box implementations are not suitable for the use case
...
parseEnvironment(METHODS_ALLOWED_PER_REGION_KEY);
configured = true;
...