This is an automated email from the ASF dual-hosted git repository.
rzo1 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/storm.git
The following commit(s) were added to refs/heads/master by this push:
new efa7c73ea Improve security documentation
- Fix inverted LOG.warn in AuthorizedUserFilter that warned when the
impersonation authorizer was configured instead of when it was missing -
Disable directory listing on UI static content servlets - Deprecate
BlowfishTupleSerializer (Sweet32 vulnerable, 64-bit block) - Document:
reverse proxy recommendation, CSRF limitations, missing security headers,
doAsUser impersonation risk with part [...]
efa7c73ea is described below
commit efa7c73ea22417652e6b87cb7b433db3711dd1ab
Author: Richard Zowalla <[email protected]>
AuthorDate: Fri Apr 3 12:04:55 2026 +0200
Improve security documentation -
Fix inverted LOG.warn in AuthorizedUserFilter that warned when the
impersonation authorizer was configured instead of when it was missing
- Disable directory listing on UI static content servlets
- Deprecate BlowfishTupleSerializer (Sweet32 vulnerable, 64-bit block)
- Document: reverse proxy recommendation, CSRF limitations, missing
security headers, doAsUser impersonation risk with partial config, BlobStore
ACL validation, ZooKeeper SSL hostname verification, and serialization
security guidance
---
docs/SECURITY.md | 56 ++++++++++++++++++++++
.../serialization/BlowfishTupleSerializer.java | 8 +++-
.../java/org/apache/storm/daemon/ui/UIServer.java | 4 +-
.../daemon/ui/filters/AuthorizedUserFilter.java | 6 +--
4 files changed, 68 insertions(+), 6 deletions(-)
diff --git a/docs/SECURITY.md b/docs/SECURITY.md
index 1b9ea8cbb..41cf254f7 100644
--- a/docs/SECURITY.md
+++ b/docs/SECURITY.md
@@ -306,6 +306,35 @@ storm.messaging.netty.tls.client.truststore.password:
password
| `storm.messaging.netty.tls.client.truststore.password`| Password for the
Netty client truststore |
+## UI Security Hardening
+
+### Reverse Proxy Recommendation
+
+For production deployments, the Storm UI, Logviewer, and DRPC HTTP endpoints
should be placed behind a reverse proxy such as Apache httpd, nginx, or a
similar gateway. While Storm provides built-in SSL/TLS configuration, a reverse
proxy offers significant additional security benefits:
+
+- Proper TLS termination with up-to-date cipher suites and certificate
management
+- CSRF token validation or same-origin enforcement on state-mutating requests
+- Security response headers (see below)
+- Rate limiting and request filtering
+- Centralized access logging and monitoring
+
+This is especially important because the Storm UI exposes state-mutating
operations (topology kill, activate, deactivate, rebalance) via its REST API.
+
+### CSRF Protection
+
+The Storm UI REST API does not currently include built-in Cross-Site Request
Forgery (CSRF) protection. State-mutating endpoints can potentially be
triggered by a malicious website if a user has an active authenticated session
in the same browser. This is particularly relevant when using browser-based
authentication mechanisms such as Kerberos (SPNEGO), as the browser
automatically attaches credentials to cross-origin requests.
+
+To mitigate this risk, configure your reverse proxy to enforce CSRF token
validation or same-origin checks on POST requests.
+
+### Security Response Headers
+
+The Storm UI does not set security-related HTTP response headers by default.
Operators deploying a secured cluster should configure the following headers
via their reverse proxy or a custom servlet filter configured through
`ui.filter`:
+
+- `X-Frame-Options: DENY` or `SAMEORIGIN` to prevent clickjacking attacks
+- `X-Content-Type-Options: nosniff` to prevent MIME type sniffing
+- `Content-Security-Policy` to restrict resource loading
+- `Strict-Transport-Security` when using HTTPS, to enforce secure connections
+
## Authentication (Kerberos)
Storm offers pluggable authentication support through thrift and SASL. This
@@ -509,6 +538,14 @@ The Log servers have their own authorization
configurations. These are set thro
When a topology is submitted, the submitting user can specify users in this
list as well. The users and groups specified-in addition to the users in the
cluster-wide setting-will be granted access to the submitted topology's worker
logs in the logviewers.
+### BlobStore ACL Validation
+
+Storm uses a BlobStore to distribute topology code and configuration across
the cluster. Access control lists on blobs are not enforced by default
(`storm.blobstore.acl.validation.enabled: false`). In a secured multi-tenant
cluster this means any authenticated user can read or modify any topology's
blobs, regardless of ACL settings. To enforce BlobStore ACLs, set the following:
+
+```yaml
+storm.blobstore.acl.validation.enabled: true
+```
+
### Supervisors headless User and group Setup
To ensure isolation of users in multi-tenancy, there is need to run
supervisors and headless user and group unique to execution on the supervisor
nodes. To enable this follow below steps.
@@ -566,6 +603,8 @@ to get a nimbus client as some other user and perform any
nimbus action(i.e. kil
Impersonation authorization is disabled by default which means any user can
perform impersonation. To ensure only authorized users can perform
impersonation you should start nimbus with `nimbus.impersonation.authorizer`
set to `org.apache.storm.security.auth.authorizer.ImpersonationAuthorizer`.
The `ImpersonationAuthorizer` uses `nimbus.impersonation.acl` as the acl to
authorize users. Following is a sample nimbus config for supporting
impersonation:
+**Important:** If you have enabled authentication (e.g. Kerberos) and
authorization (e.g. `SimpleACLAuthorizer`) but have *not* configured
`nimbus.impersonation.authorizer`, any authenticated user can impersonate any
other user by setting the `doAsUser` HTTP header or query parameter. The
`DefaultHttpCredentialsPlugin` always processes `doAsUser` requests, and
without an impersonation authorizer these requests are silently allowed. When
deploying a secured cluster you should always confi [...]
+
```yaml
nimbus.impersonation.authorizer:
org.apache.storm.security.auth.authorizer.ImpersonationAuthorizer
nimbus.impersonation.acl:
@@ -618,6 +657,14 @@ By default storm allows any sized topology to be
submitted. But ZK and others ha
| nimbus.slots.perTopology | The maximum number of slots/workers a topology
can use. |
| nimbus.executors.perTopology | The maximum number of executors/threads a
topology can use. |
+### Serialization Security
+
+Storm uses Kryo for serializing tuple data between spouts and bolts. By
default, Kryo requires all classes to be explicitly registered
(`topology.fall.back.on.java.serialization: false`). When this setting is
changed to `true`, any unregistered class will be serialized using Java's
native `ObjectInputStream`/`ObjectOutputStream`, which is known to be
vulnerable to deserialization attacks if untrusted data reaches the
serialization path.
+
+**Do not set `topology.fall.back.on.java.serialization` to `true` in
production.** While topology submitters already run arbitrary code via their
spouts and bolts, enabling the Java serialization fallback broadens the attack
surface and may allow malicious data from external sources (e.g. message
queues) to trigger unintended code execution during deserialization.
+
+For tuple encryption, use TLS-based transport encryption
(`storm.messaging.netty.tls.enable`) instead of the deprecated
`BlowfishTupleSerializer`, which uses a 64-bit block cipher vulnerable to
birthday attacks.
+
### Log Cleanup
The Logviewer daemon now is also responsible for cleaning up old log files for
dead topologies.
@@ -693,4 +740,13 @@ Also, there are several configurations for topology
Zookeeper authentication:
Note: If storm.zookeeper.topology.auth.payload isn't set, Storm will generate
a ZooKeeper secret payload for MD5-digest with
generateZookeeperDigestSecretPayload() method.
+### ZooKeeper SSL Hostname Verification
+
+When enabling ZooKeeper SSL via `storm.zookeeper.ssl.enable`, be aware that
hostname verification is disabled by default
(`storm.zookeeper.ssl.hostnameVerification: false`). Without hostname
verification, a man-in-the-middle attacker with any valid certificate signed by
a trusted CA could intercept communication between Storm and ZooKeeper. For
production deployments you should enable this:
+
+```yaml
+storm.zookeeper.ssl.enable: true
+storm.zookeeper.ssl.hostnameVerification: true
+```
+
diff --git
a/storm-client/src/jvm/org/apache/storm/security/serialization/BlowfishTupleSerializer.java
b/storm-client/src/jvm/org/apache/storm/security/serialization/BlowfishTupleSerializer.java
index 66f14c95f..185817b82 100644
---
a/storm-client/src/jvm/org/apache/storm/security/serialization/BlowfishTupleSerializer.java
+++
b/storm-client/src/jvm/org/apache/storm/security/serialization/BlowfishTupleSerializer.java
@@ -30,7 +30,11 @@ import org.slf4j.LoggerFactory;
/**
* Apply Blowfish encryption for tuple communication to bolts.
+ *
+ * @deprecated since 2.8.6. Blowfish uses a 64-bit block size which is
vulnerable to birthday attacks (Sweet32).
+ * Use TLS-based transport encryption instead (see
storm.messaging.netty.tls.enable).
*/
+@Deprecated(since = "2.8.6", forRemoval = true)
public class BlowfishTupleSerializer extends Serializer<ListDelegate> {
/**
* The secret key (if any) for data encryption by blowfish payload
serialization factory (BlowfishSerializationFactory). You should use
@@ -48,7 +52,9 @@ public class BlowfishTupleSerializer extends
Serializer<ListDelegate> {
String encryptionkey;
try {
encryptionkey = (String) topoConf.get(SECRET_KEY);
- LOG.debug("Blowfish serializer being constructed ...");
+ LOG.warn("BlowfishTupleSerializer is deprecated and will be
removed in a future release. "
+ + "Blowfish uses a 64-bit block size which is vulnerable
to birthday attacks (Sweet32). "
+ + "Use TLS-based transport encryption instead
(storm.messaging.netty.tls.enable).");
byte[] bytes;
if (encryptionkey != null) {
diff --git
a/storm-webapp/src/main/java/org/apache/storm/daemon/ui/UIServer.java
b/storm-webapp/src/main/java/org/apache/storm/daemon/ui/UIServer.java
index 14ddc9751..ee7c8e4ac 100644
--- a/storm-webapp/src/main/java/org/apache/storm/daemon/ui/UIServer.java
+++ b/storm-webapp/src/main/java/org/apache/storm/daemon/ui/UIServer.java
@@ -164,7 +164,7 @@ public class UIServer {
}
}
- holderHome.setInitParameter("dirAllowed", "true");
+ holderHome.setInitParameter("dirAllowed", "false");
holderHome.setInitParameter("pathInfoOnly", "true");
context.addFilter(new FilterHolder(new
HeaderResponseServletFilter(metricsRegistry)), "/*",
EnumSet.allOf(DispatcherType.class));
context.addServlet(holderHome, "/*");
@@ -172,7 +172,7 @@ public class UIServer {
// Lastly, the default servlet for root content (always needed, to
satisfy servlet spec)
ServletHolder holderPwd = new ServletHolder("default",
DefaultServlet.class);
- holderPwd.setInitParameter("dirAllowed", "true");
+ holderPwd.setInitParameter("dirAllowed", "false");
context.addServlet(holderPwd, "/");
metricsRegistry.startMetricsReporters(conf);
diff --git
a/storm-webapp/src/main/java/org/apache/storm/daemon/ui/filters/AuthorizedUserFilter.java
b/storm-webapp/src/main/java/org/apache/storm/daemon/ui/filters/AuthorizedUserFilter.java
index f63ab9b69..6dd17755f 100644
---
a/storm-webapp/src/main/java/org/apache/storm/daemon/ui/filters/AuthorizedUserFilter.java
+++
b/storm-webapp/src/main/java/org/apache/storm/daemon/ui/filters/AuthorizedUserFilter.java
@@ -154,12 +154,12 @@ public class AuthorizedUserFilter implements
ContainerRequestFilter {
);
return;
}
-
- LOG.warn(" principal {} is trying to impersonate {} but {} has
no authorizer configured. "
+ } else {
+ LOG.warn("Principal {} is trying to impersonate {} but {} is
not configured. "
+ "This is a potential security hole. Please
see SECURITY.MD to learn how to "
+ "configure an impersonation authorizer.",
reqContext.realPrincipal().toString(),
reqContext.principal().toString(),
-
conf.get(DaemonConfig.NIMBUS_IMPERSONATION_AUTHORIZER));
+ DaemonConfig.NIMBUS_IMPERSONATION_AUTHORIZER);
}
}