Security in Vaadin applications
- Architecture
- Security Practices at Vaadin
- Authentication and Authorization
- Application state
- Data Validation
- Cross-Site Request Forgery (CSRF / XSRF)
- Cross-Site Scripting (XSS)
- Web Services
- SSL and HTTPS
- Java Serialization Vulnerability
- Frequently reported issues (false positives)
Architecture
Vaadin is a server-side framework, where all of your application state, business and UI logic resides on the server. Unlike client driven frameworks, a Vaadin application never exposes its internals to the browser where vulnerabilities can be leveraged by an attacker.
Vaadin automates the communication between server and client through a single, secure endpoint. This endpoint has multiple built-in security features detailed in the following chapters.
Here is an example where we fetch sensitive user data from the database, but don’t expose anything we don’t want on the client in any form:
-
User clicks on a button in the UI to edit their own data (such as their name)
-
This click is caught with framework JavaScript and translated to these two things that are sent to the server:
-
The unique id of the
Button
instance (sequentially assigned previously by the framework) -
What was done with the button, in this case a click with associated mouse details (what type of click it was, and cursor position)
-
-
The data is sent to the server using the single server endpoint. The application server uses standard Servlet Session methods to locate the correct user session, which is then given to Vaadin along with the request payload.
-
Vaadin double-checks the session info and makes sure a
Button
with the given ID exists. If it does, the server-side click handler for the button is invoked and given the event details. -
The server-side code fetches the user details from storage. This data does not need to be filtered at this point, but can be if the developer so chooses.
-
The developer chooses what parts of the data to show the user by setting it as the content for any components.
-
For example:
nameTextField.setValue(user.getName());
-
-
After the handler code has executed, Vaadin will not store any reference to the fetched data. Any data that is not specifically set to be displayed is therefore discarded, such as user ID or hashed passwords or salts.
-
Only the user name string is sent to the client to be displayed, not the full user object. The client is not even aware there is a user object on the server side.
-
After the user has modified the name, the new value is sent back to the server. On the server, the developer can load the User object again, set the new name, and store the object.
As the example shows, the developer can safely handle confidential data in their code, even when dealing with UI code. The framework only stores data the developer gives it explicitly, and only data that should be visible on the client is actually sent there.
3rd party libraries
Vaadin always updates dependencies to 3rd part libraries when security patches for them are released. When necessary, a new maintenance version of Vaadin is created to apply the fix.
In most cases, developers can also specifically update versions of external libraries using Maven, where updated versions of Vaadin libraries aren’t yet available. This is done by adding a new dependency definition to the project pom.xml file with the wanted library and version number. This will make Maven override the Vaadin-defined version of the dependency with whatever version the developer added.
Older versions of Vaadin (namely, Vaadin 7 and 8) additionally depend on libraries with known security issues. These libraries are used only for compiling Java code into JavaScript, and the insecure libraries are never deployed into the runtime and hence do not pose a security risk. These are the known libraries where this is the case:
-
org.codehaus.plexus
Security Practices at Vaadin
Releasing security patches
Security fixes are implemented as fast as possible and released for all supported versions (currently versions 8.9, 10.0 and 14.0). The fix is mentioned in the release notes, and we also send a separate security notification email to all our registered users explaining the issue and how to fix it (typically updating to a new mainenance version).
How users can report security issues
If a developer or user finds a potential security issue, they can report it directly to security@vaadin.com
. The issue will be reviewed and fixed internally, before publishing to GitHub.
If the issue is minor and public discussion is OK, then issues can reported directly in GitHub.
Internal security practices
All code that is commited at Vaadin goes through an internal code review before it is merged. Each change is also ran against our existing battery of tens of thousands of unit, integration and behavior tests that have to pass for the merge to be accepted.
Developers are also encouraged to actively think about security issues while developing the framework and its parts. At Vaadin, we take security extremely seriously. Anyone can escalate an issue that they think might be a security issue, and investigating the issue is given priority over other tasks.
Authentication and Authorization
Vaadin lets you choose which authentication and authorization framework you want to use, instead of bundling any specific one. Vaadin is fully compatible with the most used security solutions in the Java ecosystem, including but not limited to Spring Security, JAAS and Apache Shiro. The Vaadin-Spring and Vaadin-CDI addons have helpers for developers to integrate into the security mechanisms of those respective frameworks.
Since Vaadin is a server-side framework, credential processing always happens on the server, away from any possible attack surface. Credentials are never transmitted to the client unless explicitly done so by the developer.
Generally, it is recommended that the developer double-checks user identity and access rights for each call from the client. This can be automated with e.g. Spring Security and view-based authentication using roles. What typically can’t be automated by these frameworks is data-based access rights, such as limiting access to specific entities.
As an example, if the server receives an ID of a User object to be displayed in e.g. a URL request parameter ({yourapp.com}/users/4/edit
), then the ID in question can be freely changed by an attacker. The application needs to be aware of this and check if the currently logged in user has access rights to this entity. This is something that is common for all UI frameworks, and not specific to Vaadin.
Examples for integrating Spring Security can be found here: Securing your app with Spring Security.
Application state
The server is always aware of your application state. Compared to client side applications, this means that the server is aware of what is currently visible on the end-user’s screen. Hence Vaadin denies actions to components that are not currently visible on the screen, or components that have been disabled on the server.
For instance, if the developer sets a component to be disabled, this effect is set both on the server and the client. On the client, an attacker can circumvent this (attackers have full control over anything in the browser), but the server will block any attempt to interact with the component and a warning is printed to the server logs.
Button button = new Button("Click me for effect!");
button.setEnabled(false);
button.addClickListener(e -> {
// If the Button is disabled, this listener will not run,
// even if an attacker enables the button client side.
});
Data Validation
In a Vaadin application, the data binding API supports data validation on the server, which cannot be by-passed with client-side attacks. Vaadin components do support client-side validation to increase the responsiveness of the application, but the developer should be aware that these should be used purely for convenience, since they are easily circumvented in the browser.
As with other web applications, all data coming from the client should always be validated once it reaches the server. It is not safe to rely on only client-side validation. Vaadin provides a set of pre-created server side validators for this purpose. In addition, the developer is free to use any Java API for validating the data, including connecting to external services. There is also a built-in integration with Java’s Bean Validation (JSR 303) standard.
Data coming from a data store (such as a database) and inserted as HTML into DOM elements (e.g. setting innerHTML for elements or using HTML mode in component captions) should also be escaped. Please see the chapter for XSS for more information.
SQL Injections
Since Vaadin is a backend-agnostic UI framework, it doesn’t directly deal with backend access; instead, choosing a backend framework (e.g. Spring Data) is left to the developer. Vaadin does not provide mitigation for SQL injections, this is left to the backend provider and developer.
However, following the data validation and escaping guidelines (see the XSS section), as well as standard secure database access practices, SQL injections can be completely blocked in Vaadin applications.
Most providers have their own ways of dealing with injections out of the box and we recommend developers follow those guides. If the developer uses pure JDBC however, they will have to deal with injection risks themselves. Here is an example for pure JDBC demonstrating an SQL injection mitigation using the value from a TextField
in a Prepared Statement:
new TextField("Set new username:", valueChangeEvent -> {
String value = valueChangeEvent.getValue();
// 'value' can contain malicious content!
// This is the correct way
String sql = "UPDATE app_users WHERE id=? SET name=?";
try {
// Use prepared statement to safely call the DB
PreparedStatement ps = dbConnection.prepareStatement(sql);
ps.setLong(1, user.getId());
ps.setString(2, value);
ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}
// This is the INCORRECT way, DO NOT USE!
// sql = "UPDATE app_users WHERE id="+ user.getId() +" SET name=\"" + value +
// "\"";
});
Cross-Site Request Forgery (CSRF / XSRF)
All requests between the client and the server are included with a user session specific CSRF token. All communication between the server and the client is handled by Vaadin, so you do not need to remember to include and verify the CSRF tokens manually.
The CSRF token is passed inside the JSON message in the request body:
Sending xhr message to server:
{"csrfToken":"0bd61cf8-0231-455b-b39a-434f054352c5","rpc":[{"type":"mSync","node":5,"feature":1,"property":"invalid","value":false},{"type":"publishedEventHandler","node":9,"templateEventMethodName":"confirmUpdate","templateEventMethodArgs":[0]}],"syncId":0,"clientId":0}
The CSRF token mechanism can be overridden on the server to enable e.g. repeatable load test scripts using Gatling or similar tools. This is strongly discouraged when running in production.
Cross-Site Scripting (XSS)
Vaadin has built-in protection against cross-site scripting (xss) attacks. Vaadin uses Browser APIs that make the browser render content as text instead of HTML, such as using innerText
instead of innerHTML
. This negates the chance to accidentally inserting e.g. <script>
tags into the DOM by binding unsecure string values.
Some Vaadin components explicitly allow HTML content for certain attributes, in which case your application needs to ensure that the data does not contain XSS payloads. Allowing insecure HTML content is never the default, it is an explicit choice by developers. Vaadin recommends using e.g. JSoup for sanitation and escaping.
Here are a few examples of built-in escaping and some where escaping is left for the developer:
Div div = new Div();
// These are safe as they treat the content as plain text
div.setText("<b>This won't be bolded</b>");
div.getElement().setText("<b>This won't be bolded either</b>");
div.setTitle("<b>This won't be bolded either</b>");
// These are NOT safe
div.getElement().setProperty("innerHTML", "<b>This IS bolded</b>");
div.add(new Html("<b>This IS bolded</b>"));
new Checkbox().setLabelAsHtml("<b>This is bolded too</b>");
The developer can use helpers to mitigate the risk when data is not trusted. Here is an example that transforms data that might have dangerous HTML to a safe format:
String safeHtml = Jsoup.clean(dangerousText, Whitelist.relaxed());
new Checkbox().setLabelAsHtml(safeHtml);
Running custom JavaScript
Sometimes application developers need to run custom scripts inside the application. Running any script is an inherently unsafe operation because scripts have full access to the entire client side. It is especially dangerous if the script is stored somewhere else than the application code and loaded dynamically:
// The script below can do whatever it wants, use the method carefully!
UI.getCurrent().getPage().executeJs("window.alert('This method is inherently unsafe');");
// This is especially dangerous!
// We can't know what the script contains, nor can we make it safe.
String script = getExternalScript();
UI.getCurrent().getPage().executeJs(script);
Scripts can not be automatically escaped, since any escaping would make the script not work and that would defeat the purpose of running a script. Vaadin can not know which script is dangerous and which script isn’t. It is up to the application developer to make sure scripts that are run are safe. However, the developer can pass parameters to JS execution safely by using the following syntax:
// If the script is known:
String script = "window.alert($0)";
// These parameters are treated in a safe way
String scriptParam = getScriptParamFromDB();
UI.getCurrent().getPage().executeJs(script, scriptParam);
Using Templates
When using Polymer Templates in Vaadin applications, the developer needs to be extra careful when inserting data into the DOM as well as using JavaScript. Vaadin automatically uses String values safely when using a TemplateModel
from the server side, but the framework has no control over what the developers do using HTML or JavaScript inside the template itself. An example is binding a TextField
with javascript value directly to client-side logic; there is no guarantee that the input is safe, and it should be sanitized before use.
Reading values from Template Models and receiving RPC calls in server side methods has the same caveats as discussed in the section Data Validation; the developer should never trust values sent from the client.
Web Services
No public Web Services are necessary in Vaadin applications. All communication in Vaadin goes through a single HTTP request handler used for RPC requests using the standard Servlet Java API. With Vaadin, you never open up your business logic as web services and thus there are less attack entry points to your Vaadin application.
SSL and HTTPS
Vaadin always recommend developers to set up secure server endpoints and run all communication exclusively under HTTPS. Vaadin works out-of-the-box with HTTPS, and there is nothing for the developer to configure in your application code. Please refer to the documentation of your servlet container for details on how to set up HTTPS on your server.
Java Serialization Vulnerability
A general security issue has been identified in programming language mechanics where the language allows execution of code that comes from serialized objects. The Java language is not immune to this; at least the Java Serialization framework, RMI, JMX and JMS features are vulnerable to this.
If the application is set up to de-serialize Java objects (e.g. using the libraries above) and an attacker can feed the system a malicious payload that gets de-serialized into Java objects. Then, the attacker can execute arbitrary code using specific language features (such as reflection).
Vaadin has published a security alert for this vulnerability, <<https://v.vaadin.com/security-alert-for-java-deserialization-of-untrusted-data-in-vaadin-severity-level-moderate, please click here for the report>>.
The vulnerability can not be fixed in the Vaadin framework, but instead developers must mitigate the risk using methods described in the alert appendices.
Frequently reported issues (false positives)
From time to time, Vaadin users perform security tests on the framework and report issues they find. Most of the time the issues are false positives. Here is a list of commonly reported false positives and why they are false.
Content-Security-Policy (CSP) set to unsafe values
The settings script-src 'unsafe-inline' 'unsafe-eval'
and style-src 'unsafe-inline'
are required during Vaadin application start, i.e. bootstrap process. The bootstrap process that starts the application loads the widgetset, which is the client side engine part of the application (precompiled javascript logic for e.g. the communication protocol, DOM control, Buttons, Layouts, etc; not application code). The widgetset is a static resource. After being loaded, the client side engine needs to be started using JavaScript.eval()
.
As such, these settings are architectural limitations in Vaadin so that the framework can start its client side engine in the browser.
Reported as: Missing or insecure “Content-Security-Policy” header
v-curdate and v-wn reported as CSRF tokens
These values are not used as CSRF tokens, and they are not processed in a way that would let an attacker compromise the application state.
Vaadin uses its own CSRF scheme, see above.
Cross-Site Request Forgery (CSRF) when fetching static resources
Many tools report a CSRF vulnerability when Vaadin fetches static resources. These requests can not change app state. Here is a list of resources that are safe to fetch without a CSRF token:
-
Widgetset files (such as
AppWidgetset.nocache.js
) -
vaadinBootstrap.js
-
vaadin-bundle-(hash).cache.js
-
vaadin-flow-bundle-(hash).cache.html
-
client-(hash).cache.js
-
frontend-es[56]/bower_components/webcomponentsjs/*
-
VAADIN/build/webcomponentsjs/*
Authentication bypass when fetching static resources
As with above, some tools mis-represent getting static resources, especially client engine javascript files (see listing above). These files should not be behind authentication, as they are necessary for the app to start even before the user has authenticated.
Reported as: Authentication Bypass Using HTTP Verb Tampering
Temporary File Download
Some tools mark downloading the vaadinBootstrap.js file as an issue; this file is a required part of starting the application, and is a static resource.
Oracle Log File Information Disclosure
Some tools that check for this do not check the content of the response, only the response status. Vaadin does not send server log files to the client, even though the response status is set to 200.
Content type incorrectly stated
This happens when Vaadin sends user events to the server and receives JSON data back. The response content type is text/plain, even though the response is JavaScript. This is done because some older Portlet vendors do not treat javascript responses correctly, hence the client side would receive incoherent instructions. The data returned from the server is never treated as a script on the client, so there is no security risk here.
Open redirection - DOM based
This issue is reported because vaadinBootstrap.js
indeed opens a new HTTP request. This is done to fetch the initial application state (on first request to an app URL, Vaadin replies with the bootstrap file; that in turn loads the theme, widgetset, and app state).
The way this request is done can not be used by an attacker to modify the application state, hence this is a false positive.
Enabling X-Frame-Options
The X-Frame-Options are a way for web pages or apps to tell the browser that they should not be run inside frames (inside another page). This is done to try to ensure that these sites are not wrapped in malicious pages where attackers can intercept user actions.
Vaadin does not automatically set the X-Frame-Options
HTTP header, because many times apps should run inside frames. Developers can set this header either in their server options or by using the Java Servlet API (using e.g. the Vaadin BootstrapListener or creating a Servlet Filter).
29ACD42F-F9EA-488B-8570-0F34485F4AB1