How to develop secure web applications
These days implementing a secure web application isn't an option, it's an unnamed requirement for every application in the first place. It doesn't matter that your app will be only used internally, once used by a human operator, you must assume it can be used to break through into background systems.
Intro
How to secure an application? There are a variety of resources, which can help you understand what is needed to build a secure application. One of them is OWASP Top Ten - a list of the most critical security risks to be exploited against your application.
In this post, I will focus on one of the risks: Cross Side Scripting - XSS in short.
Read this OWASP article to better understand what XSS is and how to protect yourself.
There is no web application that doesn't use JavaScript these days, even the simplest one can use Bootstrap, and unintentionally is using JQuery behind the scenes. Without proper knowledge of how to securely use JavaScript, you expose your users to high danger. You must be familiar with the below techniques to secure your app and users fully.
Content Security Policy
Content Security Policy, CSP in short, is a built-in mechanism in every modern web browser to help prevent XSS and script injection attacks on your application.
To properly use this mechanism your backend must attach a proper HTTP header to each response: Content-Security-Policy.
The header specifies what policy the browser must apply to the content loaded from the URL. Please follow the links to find more information about possible values, which can be used to construct the policy. You can restrict how scripts are loaded, or if using eval()
is prohibited, and so on.
One of the most important options is the nonce
attribute, which was added in version 2.0 of the policy. You can add this attribute to every <script/>
tag to inform the browser, which scripts are safe to run and have been provided by you and not by an attacker.
The nonce
contains a securely random generated value, which must be unique across each request. The same value must be provided as a part of the CSP header, e.g.:
header
Content-Security-Policy: script-src 'nonce-CkZtNfGbz9t_1le0gkpbLMcc'
tag
<script nonce="CkZtNfGbz9t_1le0gkpbLMcc">
// your code
</script>
By specifying the nonce
, the browser knows exactly which of the scripts are safe and provided by your application.
Any other scripts won't be executed (or they will be reported depending on the policy configuration), and an attacker has no chance to guess the nonce
- you must be sure this is a really random value.
Cross Origin Opener Policy
Another very important option is to use Cross Origin Opener Policy header, which protects users from avoiding sharing a browsing context. In simple words, the attacker won't be able to use the <iframe/>
tag to load suspicious content. Yet this is not the only security level you gain, you also get access to a very powerful API like SharedArrayBuffer, which can skyrocket your application :)
There are three values of the header you can define:
unsafe-none
- the default value, and the less secure - basically no policy is appliedsame-origin-allow-popups
- this value keeps the same context for newly opened windows (pop ups) or tabssame-origin
- the most restrictive option, the browsing context is isolated only to the same document
You should at least consider applying one of the two most secure options.
Cross Origin Embedder Policy
Finally, by having Cross-Origin-Opener-Policy implemented you can use Cross Origin Embedder Policy to control which resources are allowed to be loaded by the browser. By default, all the resources are accessible with the value unsafe-none
.
To have better control over what browser can load over the wire you should use the second value require-corp
.
Cross-Origin-Embedder-Policy: unsafe-none | require-corp
To allow loading all the resources from the same origin, you can use these two headers:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
In such a case any calls to external resources like this:
<img src="https://thirdparty.com/img.png"/>
will be blocked. You can allow loading such resources by adding a crossorigin
attribute.
Fetch Metadata
And the final option is to use a fetch metadata request header to provide additional information about the context from which the resource originated. In such a case, a server can decide if the resource is accessible based on the origins of the request.
This allows implementing a resource isolation policy to only share resources, which are intended to be shared.
To define a specific context you can use one of these headers:
The fetch metadata headers protect your application against CSRF, XSSI, and cross-origin information leaks.
Secure with Apache Struts
Since Apache Struts version 6.0.0 you can use all
those goodies in your application. In this version of the framework, a set of interceptors has been added to support all the aspects of defining a proper Content Security Policy:
- Content Security Policy Interceptor
- Cross-Origin Opener Policy Interceptor
- Cross-Origin Embedder Policy Interceptor
- Fetch Metadata Interceptor
You can either use the default stack of interceptors with predefined options or (and this is the recommended approach) create your own interceptors stack and adjust how the Content Security Policy should be set to support your application in the most secure way.
Summary
When you develop modern web applications, you cannot just think about security. You must proactively use the current most important security options, which each web browser offers these days. Even the older browsers support a subset of the presented policies and even by addressing those browsers, you can still develop a secure web application.
Security isn't an option, it's a must-have choice :)