Skip to content

[SPARK-57614][WEBUI] Use CSP frame-ancestors instead of deprecated X-Frame-Options: ALLOW-FROM#56669

Open
sarutak wants to merge 4 commits into
apache:masterfrom
sarutak:fix-allow-from-frame-ancestors
Open

[SPARK-57614][WEBUI] Use CSP frame-ancestors instead of deprecated X-Frame-Options: ALLOW-FROM#56669
sarutak wants to merge 4 commits into
apache:masterfrom
sarutak:fix-allow-from-frame-ancestors

Conversation

@sarutak

@sarutak sarutak commented Jun 22, 2026

Copy link
Copy Markdown
Member

What changes were proposed in this pull request?

This PR fixes the spark.ui.allowFramingFrom configuration to use CSP frame-ancestors directive instead of the deprecated X-Frame-Options: ALLOW-FROM.

According to MDN Web Docs:

ALLOW-FROM origin — This is an obsolete directive. Modern browsers that encounter response headers with this direc
tive will ignore the header completely. The Content-Security-Policy HTTP header has a frame-ancestors directive which
you should use instead.

The fix:

  • When spark.ui.contentSecurityPolicy.enabled=true, uses CSP frame-ancestors 'self' <uri> to enforce the allowFramingFrom setting
  • Adds spark.ui.contentSecurityPolicy.frameAncestors.enabled (default true) as a sub-config to control whether frame-ancestors is included in the CSP header. Can be set to false as a fallback if frame-ancestors causes issues
  • When CSP is disabled (the default), no CSP header is emitted and X-Frame-Options: SAMEORIGIN is used
  • Sanitizes the config value to prevent CSP directive injection via semicolons or newlines
  • Moves X-Frame-Options: SAMEORIGIN header before access control checks so that even 403 error responses include clickjacking protection

Why are the changes needed?

The spark.ui.allowFramingFrom config has been non-functional in all modern browsers since ALLOW-FROM was obsoleted. Users relying on this setting for embedding Spark UI in iframes from specific origins have no working clickjacking protection. CSP frame-ancestors is the W3C-recommended replacement.

Does this PR introduce any user-facing change?

Yes.

  • spark.ui.allowFramingFrom now works correctly when CSP is enabled, via frame-ancestors
  • spark.ui.allowFramingFrom requires spark.ui.contentSecurityPolicy.enabled=true to take effect. When CSP is disabled, X-Frame-Options: SAMEORIGIN is always used regardless of the allowFramingFrom value
  • New config spark.ui.contentSecurityPolicy.frameAncestors.enabled (default true) controls whether frame-ancestors is included in the CSP header
  • X-Frame-Options header is always SAMEORIGIN (previously it was ALLOW-FROM <uri> which browsers ignored)
  • Migration guide updated

How was this patch tested?

  • Unit tests in HttpSecurityFilterSuite verify:
    • CSP frame-ancestors contains the configured URI when CSP is enabled
    • No CSP header when CSP is disabled
    • frame-ancestors excluded when frameAncestors.enabled=false
    • DENY maps to frame-ancestors 'none'
    • SAMEORIGIN maps to frame-ancestors 'self'
    • CSP directive injection prevention via semicolons
    • X-Frame-Options: SAMEORIGIN is always set, even on 403 responses
  • Integration test in UISuite verifies Jetty sanitizes newlines in the CSP header

Was this patch authored or co-authored using generative AI tooling?

Kiro CLI / Claude


val cspNonce = CspNonce.generate()
try {
// SPARK-10589 avoid frame-related click-jacking vulnerability.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can remove this line.

} else {
// Even when the full CSP is disabled, set frame-ancestors to enforce
// the allowFramingFrom setting in browsers that support CSP.
hres.setHeader("Content-Security-Policy", s"$frameAncestors;")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I ask if there is a way to have this feature without CSP header?

Although I agree with your intention, this looks like a conflict to our CSP config option. CSP header should not be used when the CSP configuration is off. In other words, this is a sub feature of CSP parent configuration.

Even when the full CSP is disabled, set frame-ancestors to enforce the allowFramingFrom setting in browsers that support CSP.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I ask if there is a way to have this feature without CSP header?

There is no browser mechanism to allow framing from a specific origin without Content-Security-Policy: frame-ancestors. X-Frame-Options only supports DENY and SAMEORIGIN in modern browsers (ALLOW-FROM is obsolete and ignored).

So we have two options:

  1. Emit a minimal CSP header (frame-ancestors only) when CSP is disabled
    allowFramingFrom works regardless of CSP config, but it's inconsistent with the CSP toggle.

  2. No CSP header when CSP is disabled
    Consistent with the CSP config, but allowFramingFrom only takes effect when spark.ui.contentSecurityPolicy.enabled=true. When CSP is disabled, X-Frame-Options: SAMEORIGIN is always used regardless of the allowFramingFrom value.

@dongjoon-hyun dongjoon-hyun Jun 23, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spark.ui.contentSecurityPolicy.enabled means (2).

For this new feature, shall we add new sub-config like `spark.ui.contentSecurityPolicy.frameAncestors.enabled' because we need a fallback for this transition.

BTW, while testing 4.2.0, I found that CSP feature is too strong and Spark UIs are still immature. I need to revisit one by one.

@dongjoon-hyun

Copy link
Copy Markdown
Member

@sarutak ? Are you targeting this to deliver to Apache Spark 4.2.0? I saw that you put this under SPARK-55556 Improve Security which is resolved for Apache Spark 4.2.0 already.

@sarutak

sarutak commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

Are you targeting this to deliver to Apache Spark 4.2.0? I saw that you put this under SPARK-55556 Improve Security which is resolved for Apache Spark 4.2.0 already.

@dongjoon-hyun
I was just about to comment on that very thing. I intentionally added this as a sub-task of SPARK-55556. It's already marked as resolved but some other sub-tasks seems added recently.
I thought it's better to target this to 4.2.0 if we can make it in time because this is a security issue.

cc: @huaxingao

@dongjoon-hyun

Copy link
Copy Markdown
Member

Thank you for updating.

I'm also okay for the targeting 4.2.0 as long as Huaxin allows it.

@dongjoon-hyun dongjoon-hyun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-1 because Content-Security-Policy is exposed still when spark.ui.contentSecurityPolicy.enabled=false.

s"style-src 'self' 'unsafe-inline'; img-src 'self' data:; " +
s"object-src 'none'; base-uri 'self'; $frameAncestors;")
} else {
hres.setHeader("Content-Security-Policy", s"$frameAncestors;")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I don't think this is correct.

@sarutak

sarutak commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

@dongjoon-hyun
When you suggested adding spark.ui.contentSecurityPolicy.frameAncestors.enabled, I interpreted "fallback for this transition" as: since the full CSP is too strong right now, this sub-config allows emitting just frame-ancestors without the full CSP so that allowFramingFrom works during the transition to full CSP.

If your intent is that no CSP header should be emitted at all when contentSecurityPolicy.enabled=false, I can make that change. However, I'd like to point out that these two goals are mutually exclusive:

  • Design consistency: CSP disabled = no CSP header at all
  • allowFramingFrom working as intended: requires frame-ancestors, which can only exist in a CSP header

There is no browser mechanism to allow framing from a specific origin without a CSP header. If we choose design consistency, allowFramingFrom becomes non-functional unless users explicitly enable CSP. Since ALLOW-FROM was already broken in browsers, this doesn't cause a regression in practice, but it does mean the originally intended behavior of allowFramingFrom cannot work by default.

Which trade-off do you prefer?

@dongjoon-hyun

dongjoon-hyun commented Jun 23, 2026

Copy link
Copy Markdown
Member

Before going further, let's clarify this.

Apache Spark configuration namespace is organized in this way. Do you agree with this, @sarutak ?

  • spark.dynamicAllocation.enabled controls all dynamic allocation features including subfeatures.
  • When spark.dynamicAllocation.enabled=false, the followings are ignored completely.
    • spark.dynamicAllocation.minExecutors
    • spark.dynamicAllocation.maxExecutors
    • spark.dynamicAllocation.initialExecutors
    • spark.dynamicAllocation.executorAllocationRatio

@sarutak

sarutak commented Jun 23, 2026

Copy link
Copy Markdown
Member Author

I agree with the general principle that when spark.dynamicAllocation.enabled=false, sub-configs like maxExecutors are ignored. That makes sense because those sub-configs were designed as part of the dynamic allocation feature from the start.

I also understand your intent with the new sub-config: frameAncestors.enabled lives under contentSecurityPolicy.*, so when the parent (contentSecurityPolicy.enabled) is off, no CSP header should be emitted.

However, spark.ui.allowFramingFrom is not a sub-config of contentSecurityPolicy. It's an independent config that has existed since Spark 1.6.0, long before CSP support was added. If we enforce the parent-child rule strictly here, allowFramingFrom becomes non-functional unless users explicitly enable CSP, which changes the originally intended default behavior of this config.

@dongjoon-hyun

dongjoon-hyun commented Jun 25, 2026

Copy link
Copy Markdown
Member

Exactly!

However, spark.ui.allowFramingFrom is not a sub-config of contentSecurityPolicy. It's an independent config that has existed since Spark 1.6.0, long before CSP support was added. If we enforce the parent-child rule strictly here, allowFramingFrom becomes non-functional unless users explicitly enable CSP, which changes the originally intended default behavior of this config.

I believe this PR sticks to the legacy behavior when spark.ui.contentSecurityPolicy.enabled=false.

@sarutak sarutak force-pushed the fix-allow-from-frame-ancestors branch from 805253a to e732bec Compare June 30, 2026 21:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants