question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Enable invoking JS from the server in a way that is compatible with CSP and avoids XSS

See original GitHub issue

This ticket suggests a new low-level mechanism in Flow that would allow defining all JS expression to invoke as static strings with no possibility of causing XSS issues by accidentally including user-provided values in the actual JS expressions. By extension, this means that all used JS can be known when the production build is created which in turn makes it possible to compute hashes to use with CSP.

The core idea is to avoid page.executeJs("window.alert($0)", "Hello"); which cannot be known in advance and which might accidentally be written as page.executeJs("window.alert('" + greeting "')"); which might lead to XSS. Instead, all JS snippets would be defined either in separate JS module files or as oneliners in annotations.

For oneliners, one alternative might be to define an interface.

public interface GreeterJs {
  @JsExpression("window.alert($0)")
  void showGreeting(String greeting);
}

Flow could then provide a way of creating an instance of that interface to actually invoke the JS:

page.getJsInvoker(GreeterJs.class).showGreeting("Hello");

Internally, this could work so that generated-flow-imports.js would contain something like this:

window.Flow.jsInvokers["com.acme.GreeterJs"] = {
 "showGreeting": $0 => window.alert($0)
}

When the Java method is run, a tuple like ["com.acme.GreeterJs", "showGreeting", ["Hello"]] could be sent to the client, based on which Flow could do window.Flow.jsInvokers[tuple[0]][tuple[1]).apply(null, tuple[2]) to invoke the JS without having to use eval or any similar mechanism that is unfriendly to CSP.

A corresponding concept could be used to instead define the invokeable JS that is exported from separate JS module file. In that case, the module file could be defined like this:

export function showGreeting(message) { window.alert(message) };

while the corresponding Java interface would instead use an annotation to define the JS module it should be bound to:

@JsExpressionModule("./myModule")
public interface GreeterJs {
  void showGreeting(String greeting);
}

Invoking from Java would be done in exactly the same way.

The code generated into generated-flow-imports.js would in this case be something like this:

import * as generated123 from './myModule';
window.Flow.jsInvokers["com.acme.GreeterJs"] = generated123;

The same mechanism could also be used to replace Element.executeJs with a small enhancement. What’s needed is just to pass the client-side element instance as this (i.e. as the first argument to apply) if invoked in this way:

someElement.getJsInvoker(GreeterJs.class).showGreeting("Hello");

For this to make sense, the targeted JS expression would of course also have to make use of this.

An additional benefit of this approach is that @JsExpression scripts will be included in the bundle processed by webpack, which means that newer JS features can be used without having to take browser compatibility into account.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:2
  • Comments:6 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
Legiothcommented, May 12, 2021

There are no specific plans at this moment - I’m just poking at things that might be interesting and seeing if there’s any wider interest among users. I plan to publish something related on vaadin.com/labs later this week to get something more tangible to refer to.

If there is enough interest within the next weeks or months, then I believe a basic implementation could be thrown together quickly enough to get into the next LTS. The challenges here are more on the conceptual side whereas an actual implementation should be quite straightforward if I’m not mistaken.

As an alternative for someone really eager to use strict CSP settings even if it adds some boilerplate to their application, there’s also the fallback mechanism in https://github.com/vaadin/flow/issues/10810 that could be implemented in a couple of hours. That approach could be added even to 14.x.

0reactions
Legiothcommented, May 25, 2021

Vaadin components are typically only using hardcoded JavaScript snippets that can be known in advance. One would just have to verify whether any expressions have changed whenever updating to a new Vaadin version or starting to use a component that hasn’t been used previously. I specifically reviewed Grid and ComboBox to have two quite complex cases and I didn’t spot anything there that would dynamically generate JS expressions to run.

The one exception that I’m aware of is ShortcutRegistration which is dynamically building event filter expressions based on the key and modifiers to listen for. Even a case like this could still be supported. If the application only uses a handful of different shortcut keys, then it would might make sense to just hardcode for the particular expressions that are generated for those keys. Supporting arbitrary shortcut keys could also be possible by building a simple parser that recognizes the structure used by shortcut expressions to extract the variable parts and then returning a regular function that uses those values from its own closure.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Defending against XSS with CSP - Auth0
First, the script-src directive also includes the 'unsafe-eval' keyword. By default, CSP prevents the use of the eval() function in JavaScript. eval() ...
Read more >
Using Content Security Policy to Prevent Cross-Site Scripting ...
It aims to prevent XSS by white-listing URLs the browser can load and execute JavaScript from. The server can, by specifying specific CSP...
Read more >
Mitigate cross-site scripting (XSS) with a strict Content Security ...
Learn how to deploy a CSP based on script nonces or hashes as a defense-in-depth against cross-site scripting.
Read more >
Fighting cross-site-scripting (XSS) with content security policy
We explain the danger cross-site-scripting (XSS) poses and how to fight it using Content Security Policy (CSP).
Read more >
Content Security Policy Prevents XSS - MST Solutions
According to W3C, “Content Security Policy (CSP), a tool which developers can use to lock down their applications in various ways, mitigating the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found