22. Security with Wicket

Security is one of the most important non-functional requirements we must implement in our applications. This is particularly true for enterprise applications as they usually support multiple concurrent users, and therefore they need to have an access control policy.

In this chapter we will explore the security infrastructure provided by Wicket and we will learn how to use it to implement authentication and authorizations in our web applications.

22.1. Authentication

The first step in implementing a security policy is assigning a trusted identity to our users, which means that we must authenticate them. Web applications usually adopt a form-based authentication with a login form that asks user for a unique username and the relative password:

wikipedia login form

Wicket supports form-based authentication with session class AuthenticatedWebSession and application class AuthenticatedWebApplication, both placed inside package org.apache.wicket.authroles.authentication.

22.1.1. AuthenticatedWebSession

Class AuthenticatedWebSession comes with the following set of public methods to manage user authentication:

  • authenticate(String username, String password): this is an abstract method that must be implemented by every subclass of AuthenticatedWebSession. It should contain the actual code that checks for user’s identity. It returns a boolean value which is true if authentication has succeeded or false otherwise.

  • signIn(String username, String password): this method internally calls authenticate and set the flag signedIn to true if authentication succeeds.

  • isSignedIn():getter method for flag signedIn.

  • invalidate(): sets the flag signedIn to false and invalidates session.

  • signOut(): an alias of invalidate().

Another abstract method we must implement when we use AuthenticatedWebSession is getRoles which is inherited from parent class AbstractAuthenticatedWebSession. This method can be ignored for now as it will be discussed later when we will talk about role-based authorization.

22.1.2. AuthenticatedWebApplication

Class AuthenticatedWebApplication provides the following methods to support form-based authentication:

  • getWebSessionClass(): abstract method that returns the session class to use for this application. The returned class must be a subclass of AbstractAuthenticatedWebSession.

  • getSignInPageClass(): abstract method that returns the page to use as sign in page when a user must be authenticated.

  • restartResponseAtSignInPage(): forces the current response to restart at the sign in page. After we have used this method to redirect a user, we can make her/him return to the original page calling Component‘s method continueToOriginalDestination().

The other methods implemented inside AuthenticatedWebApplication will be introduced when we talk about authorization.

22.1.3. A basic example of authentication

Project BasicAuthenticationExample is a basic example of form-based authentication implemented with classes AuthenticatedWebSession and AuthenticatedWebApplication.

The homepage of the project contains only a link to page AuthenticatedPage which can be accessed only if user is signed in. The code of AuthenticatedPage is this following:

  1. public class AuthenticatedPage extends WebPage {
  2. @Override
  3. protected void onConfigure() {
  4. super.onConfigure();
  5. AuthenticatedWebApplication app = (AuthenticatedWebApplication)Application.get();
  6. //if user is not signed in, redirect him to sign in page
  7. if(!AuthenticatedWebSession.get().isSignedIn())
  8. app.restartResponseAtSignInPage();
  9. }
  10. @Override
  11. protected void onInitialize() {
  12. super.onInitialize();
  13. add(new BookmarkablePageLink<Void>("goToHomePage", getApplication().getHomePage()));
  14. add(new Link<Void>("logOut") {
  15. @Override
  16. public void onClick() {
  17. AuthenticatedWebSession.get().invalidate();
  18. setResponsePage(getApplication().getHomePage());
  19. }
  20. });
  21. }
  22. }

Page AuthenticatedPage checks inside onConfigure if user is signed in and if not, it redirects her/him to the sign in page with method restartResponseAtSignInPage. The page contains also a link to the homepage and another link that signs out user.

The sign in page is implemented in class SignInPage and contains the form used to authenticate users:

  1. public class SignInPage extends WebPage {
  2. private String username;
  3. private String password;
  4. @Override
  5. protected void onInitialize() {
  6. super.onInitialize();
  7. StatelessForm<Void> form = new StatelessForm<Void>("form") {
  8. @Override
  9. protected void onSubmit() {
  10. if(Strings.isEmpty(username))
  11. return;
  12. boolean authResult = AuthenticatedWebSession.get().signIn(username, password);
  13. //if authentication succeeds redirect user to the requested page
  14. if(authResult)
  15. continueToOriginalDestination();
  16. }
  17. };
  18. form.setModel(new CompoundPropertyModel(this));
  19. form.add(new TextField("username"));
  20. form.add(new PasswordTextField("password"));
  21. add(form);
  22. }
  23. }

The form is responsible for handling user authentication inside its method onSubmit(). The username and password are passed to AuthenticatedWebSession‘s method signIn(username, password) and if authentication succeeds, the user is redirected to the original page with method continueToOriginalDestination.

The session class and the application class used in the project are reported here:

Session class:

  1. public class BasicAuthenticationSession extends AuthenticatedWebSession {
  2. public BasicAuthenticationSession(Request request) {
  3. super(request);
  4. }
  5. @Override
  6. public boolean authenticate(String username, String password) {
  7. //user is authenticated if both username and password are equal to 'wicketer'
  8. return username.equals(password) && username.equals("wicketer");
  9. }
  10. @Override
  11. public Roles getRoles() {
  12. return new Roles();
  13. }
  14. }

Application class:

  1. public class WicketApplication extends AuthenticatedWebApplication {
  2. @Override
  3. public Class<HomePage> getHomePage(){
  4. return HomePage.class;
  5. }
  6. @Override
  7. protected Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass(){
  8. return BasicAuthenticationSession.class;
  9. }
  10. @Override
  11. protected Class<? extends WebPage> getSignInPageClass() {
  12. return SignInPage.class;
  13. }
  14. }

The authentication logic inside authenticate has been kept quite trivial in order to make the code as clean as possible. Please note also that session class must have a constructor that accepts an instance of class Request.

22.1.4. Redirecting user to an intermediate page

Method restartResponseAtSignInPage is an example of redirecting user to an intermediate page before allowing him to access to the requested page. This method internally throws exception org.apache.wicket.RestartResponseAtInterceptPageException which saves the URL and the parameters of the requested page into session metadata and then redirects user to the page passed as constructor parameter (the sign in page).

Component’s method redirectToInterceptPage(Page) works in much the same way as restartResponseAtSignInPage but it allows us to specify which page to use as intermediate page:

  1. redirectToInterceptPage(intermediatePage);
Since both restartResponseAtSignInPage and redirectToInterceptPage internally throw an exception, the code placed after them will not be executed.

22.2. Authorizations

The authorization support provided by Wicket is built around the concept of authorization strategy which is represented by interface IAuthorizationStrategy (in package org.apache.wicket.authorization):

  1. public interface IAuthorizationStrategy {
  2. //interface methods
  3. <T extends IRequestableComponent> boolean isInstantiationAuthorized(Class<T> componentClass);
  4. boolean isActionAuthorized(Component component, Action action);
  5. //default authorization strategy that allows everything
  6. public static final IAuthorizationStrategy ALLOW_ALL = new IAuthorizationStrategy()
  7. {
  8. @Override
  9. public <T extends IRequestableComponent> boolean isInstantiationAuthorized(final Class<T> c)
  10. {
  11. return true;
  12. }
  13. @Override
  14. public boolean isActionAuthorized(Component c, Action action)
  15. {
  16. return true;
  17. }
  18. };
  19. }

This interface defines two methods:

  • isInstantiationAuthorized() checks if user is allowed to instantiate a given component.

  • isActionAuthorized() checks if user is authorized to perform a given action on a component’s instance. The standard actions checked by this method are defined into class Action and are Action.ENABLE and Action.RENDER.

Inside IAuthorizationStrategy we can also find a default implementation of the interface (called ALLOW_ALL) that allows everyone to instantiate every component and perform every possible action on it. This is the default strategy adopted by class Application.

To change the authorization strategy in use we must register the desired implementation into security settings (class SecuritySettings) during initialization phase with method setAuthorization Strategy:

  1. //Application class code...
  2. @Override
  3. public void init()
  4. {
  5. super.init();
  6. getSecuritySettings().
  7. setAuthorizationStrategy(myAuthorizationStrategy);
  8. }
  9. //...

If we want to combine the action of two or more authorization strategies we can chain them with strategy CompoundAuthorizationStrategy which implements composite pattern for authorization strategies.

Most of the times we won’t need to implement an IAuthorizationStrategy from scratch as Wicket already comes with a set of built-in strategies. In the next paragraphs we will see some of these strategies that can be used to implement an effective and flexible security policy.

22.2.1. SimplePageAuthorizationStrategy

Abstract class SimplePageAuthorizationStrategy (in package org.apache.wicket.authorization.strategies.page) is a strategy that checks user authorizations calling abstract method isAuthorized only for those pages that are subclasses of a given supertype. If isAuthorized returns false, the user is redirected to the sign in page specified as second constructor parameter:

  1. SimplePageAuthorizationStrategy authorizationStrategy = new SimplePageAuthorizationStrategy(
  2. PageClassToCheck.class, SignInPage.class)
  3. {
  4. protected boolean isAuthorized()
  5. {
  6. //Authentication code...
  7. }
  8. };

By default SimplePageAuthorizationStrategy checks for permissions only on pages. If we want to change this behavior and check also other kinds of components, we must override method isActionAuthorized() and implement our custom logic inside it.

22.2.2. Role-based strategies

At the end of paragraph 22.1 we have introduced AbstractAuthenticatedWebSession’s method getRoles() which is provided to support role-based authorization returning the set of roles granted to the current user.

In Wicket roles are simple strings like “BASIC_USER” or “ADMIN” (they don’t need to be capitalized) and they are handled with class org.apache.wicket.authroles.authorization.strategies.role.Roles. This class extends standard HashSet collection adding some functionalities to check whether the set contains one or more roles. Class Roles already defines roles Roles.USER and Roles.ADMIN.

The session class in the following example returns a custom “SIGNED_IN” role for every authenticated user and it adds an Roles.ADMIN role if username is equal to superuser:

  1. class BasicAuthenticationRolesSession extends AuthenticatedWebSession {
  2. private String userName;
  3. public BasicAuthenticationRolesSession(Request request) {
  4. super(request);
  5. }
  6. @Override
  7. public boolean authenticate(String username, String password) {
  8. boolean authResult= false;
  9. authResult = //some authentication logic...
  10. if(authResult)
  11. userName = username;
  12. return authResult;
  13. }
  14. @Override
  15. public Roles getRoles() {
  16. Roles resultRoles = new Roles();
  17. if(isSignedIn())
  18. resultRoles.add("SIGNED_IN");
  19. if(userName.equals("superuser"))
  20. resultRoles.add(Roles.ADMIN);
  21. return resultRoles;
  22. }
  23. }

Roles can be adopted to apply security restrictions on our pages and components. This can be done using one of the two built-in authorization strategies that extend super class AbstractRoleAuthorizationStrategyWicket: MetaDataRoleAuthorizationStrategy and AnnotationsRoleAuthorizationStrategy

The difference between these two strategies is that MetaDataRoleAuthorizationStrategy handles role-based authorizations with Wicket metadata while AnnotationsRoleAuthorizationStrategy uses Java annotations.

Application class AuthenticatedWebApplication already sets MetaDataRoleAuthorizationStrategy and AnnotationsRoleAuthorizationStrategy as its own authorization strategies (it uses a compound strategy as we will see in paragraph 22.2).

The code that we will see in the next examples is for illustrative purpose only. If our application class inherits from AuthenticatedWebApplication we won’t need to configure anything to use these two strategies.

Using roles with metadata

Strategy MetaDataRoleAuthorizationStrategy uses application and components metadata to implement role-based authorizations. The class defines a set of static methods authorize that can be used to specify which roles are allowed to instantiate a component and which roles can perform a given action on a component.

The following code snippet reports both application and session classes from project MetaDataRolesStrategyExample and illustrates how to use MetaDataRoleAuthorizationStrategy to allow access to a given page (AdminOnlyPage) only to ADMIN role:

Application class:

  1. public class WicketApplication extends AuthenticatedWebApplication {
  2. @Override
  3. public Class<? extends WebPage> getHomePage(){
  4. return HomePage.class;
  5. }
  6. @Override
  7. protected Class<? extends AbstractAuthenticatedWebSession> getWebSessionClass() {
  8. return BasicAuthenticationSession.class;
  9. }
  10. @Override
  11. protected Class<? extends WebPage> getSignInPageClass() {
  12. return SignInPage.class;
  13. }
  14. @Override
  15. public void init(){
  16. getSecuritySettings().setAuthorizationStrategy(new MetaDataRoleAuthorizationStrategy(this));
  17. MetaDataRoleAuthorizationStrategy.authorize(AdminOnlyPage.class, Roles.ADMIN);
  18. }
  19. }

Session class:

  1. public class BasicAuthenticationSession extends AuthenticatedWebSession {
  2. private String username;
  3. public BasicAuthenticationSession(Request request) {
  4. super(request);
  5. }
  6. @Override
  7. public boolean authenticate(String username, String password) {
  8. //user is authenticated if username and password are equal
  9. boolean authResult = username.equals(password);
  10. if(authResult)
  11. this.username = username;
  12. return authResult;
  13. }
  14. public Roles getRoles() {
  15. Roles resultRoles = new Roles();
  16. //if user is signed in add the relative role
  17. if(isSignedIn())
  18. resultRoles.add("SIGNED_IN");
  19. //if username is equal to 'superuser' add the ADMIN role
  20. if(username!= null && username.equals("superuser"))
  21. resultRoles.add(Roles.ADMIN);
  22. return resultRoles;
  23. }
  24. @Override
  25. public void signOut() {
  26. super.signOut();
  27. username = null;
  28. }
  29. }

The code that instantiates MetaDataRoleAuthorizationStrategy and set it as application’s strategy is inside application class method init().

Any subclass of AbstractRoleAuthorizationStrategyWicket needs an implementation of interface IRoleCheckingStrategy to be instantiated. For this purpose in the code above we used the application class itself because its base class AuthenticatedWebApplication already implements interface IRoleCheckingStrategy. By default AuthenticatedWebApplication checks for authorizations using the roles returned by the current AbstractAuthenticatedWebSession. As final step inside init we grant the access to page AdminOnlyPage to ADMIN role calling method authorize.

The code from session class has three interesting methods. The first is authenticate() which considers as valid credentials every pair of username and password having the same value. The second notable method is getRoles() which returns role SIGNED_IN if user is authenticated and it adds role ADMIN if username is equal to superuser. Finally, we have method signOut() which has been overridden in order to clean the username field used internally to generate roles.

Now if we run the project and we try to access to AdminOnlyPage from the home page without having the ADMIN role, we will be redirected to the default access-denied page used by Wicket:

authorization access denied

The access-denied page can be customized using method setAccessDeniedPage(Class<? extends Page>) of setting class ApplicationSettings:

  1. //Application class code...
  2. @Override
  3. public void init(){
  4. getApplicationSettings().setAccessDeniedPage(
  5. MyCustomAccessDeniedPage.class);
  6. }

Just like custom “Page expired” page (see chapter 8.2.5), also custom “Access denied” page must be bookmarkable.

Using roles with annotations

Strategy AnnotationsRoleAuthorizationStrategy relies on two built-in annotations to handle role-based authorizations. These annotations are AuthorizeInstantiation and AuthorizeAction. As their names suggest the first annotation specifies which roles are allowed to instantiate the annotated component while the second must be used to indicate which roles are allowed to perform a specific action on the annotated component.

In the following example we use annotations to make a page accessible only to signed-in users and to enable it only if user has the ADMIN role:

  1. @AuthorizeInstantiation("SIGNED_IN")
  2. @AuthorizeAction(action = "ENABLE", roles = {"ADMIN"})
  3. public class MyPage extends WebPage {
  4. //Page class code...
  5. }

Remember that when a component is not enabled, user can render it but he can neither click on its links nor interact with its forms.

Example project AnnotationsRolesStrategyExample is a revisited version of MetaDataRolesStrategyExample where we use AnnotationsRoleAuthorizationStrategy as authorization strategy. To ensure that page AdminOnlyPage is accessible only to ADMIN role we have used the following annotation:

  1. @AuthorizeInstantiation("ADMIN")
  2. public class AdminOnlyPage extends WebPage {
  3. //Page class code...
  4. }

22.2.3. Catching an unauthorized component instantiation

Interface IUnauthorizedComponentInstantiationListener (in package org.apache.wicket.authorization) is provided to give the chance to handle the case in which a user tries to instantiate a component without having the permissions to do it. The method defined inside this interface is onUnauthorizedInstantiation(Component) and it is executed whenever a user attempts to execute an unauthorized instantiation.

This listener must be registered into application’s security settings with method setUnauthorizedComponentInstantiationListener defined by setting class SecuritySettings. In the following code snippet we register a listener that redirect user to a warning page if he tries to do a not-allowed instantiation:

  1. public class WicketApplication extends AuthenticatedWebApplication{
  2. //Application code...
  3. @Override
  4. public void init(){
  5. getSecuritySettings().setUnauthorizedComponentInstantiationListener(
  6. new IUnauthorizedComponentInstantiationListener() {
  7. @Override
  8. public void onUnauthorizedInstantiation(Component component) {
  9. component.setResponsePage(AuthWarningPage.class);
  10. }
  11. });
  12. }
  13. }

In addition to interface IRoleCheckingStrategy, class AuthenticatedWebApplication implements also IUnauthorizedComponentInstantiationListener and registers itself as listener for unauthorized instantiations.

By default AuthenticatedWebApplication redirects users to sign-in page if they are not signed-in and they try to instantiate a restricted component. Otherwise, if users are already signed in but they are not allowed to instantiate a given component, an UnauthorizedInstantiationException will be thrown.

22.2.4. Strategy RoleAuthorizationStrategy

Class RoleAuthorizationStrategy is a compound strategy that combines both MetaDataRoleAuthorizationStrategy and AnnotationsRoleAuthorizationStrategy.

This is the strategy used internally by AuthenticatedWebApplication.

22.3. Using HTTPS protocol

HTTPS is the standard technology adopted on Internet to create a secure communication channel between web applications and their users.

In Wicket we can easily protect our pages with HTTPS mounting a special request mapper called HttpsMapper and using annotation RequireHttps with those pages we want to serve over this protocol. Both these two entities are in package org.apache.wicket.protocol.https.

HttpsMapper wraps an existing mapper and redirects incoming requests to HTTPS if the related response must render a page annotated with RequireHttps. Most of the times the wrapped mapper will be the root one, just like we saw before for CryptoMapper in paragraph 10.6.

Another parameter needed to build a HttpsMapper is an instance of class HttpsConfig. This class allows us to specify which ports must be used for HTTPS and HTTP. By default the port numbers used by these two protocols are respectively 443 and 80.

The following code is taken from project HttpsProtocolExample and illustrates how to enable HTTPS in our applications:

  1. //Application class code...
  2. @Override
  3. public void init(){
  4. setRootRequestMapper(new HttpsMapper(getRootRequestMapper(),
  5. new HttpsConfig(8080, 8443)));
  6. }

Now we can use annotation RequireHttps to specify which pages must be served using HTTPS:

  1. @RequireHttps
  2. public class HomePage extends WebPage {
  3. public HomePage(final PageParameters parameters) {
  4. super(parameters);
  5. }
  6. }

If we want to protect many pages with HTTPS without adding annotation RequireHttps to each of them, we can annotate a marker interface or a base page class and implement/extend it in any page we want to make secure:

  1. // Marker interface:
  2. @RequireHttps
  3. public interface IMarker {
  4. }
  5. // Base class:
  6. @RequireHttps
  7. public class BaseClass extends WebPage {
  8. //Page code...
  9. }
  10. // Secure page inheriting from BaseClass:
  11. public class HttpsPage extends BaseClass {
  12. //Page code...
  13. }
  14. // Secure page implementing IMarker:
  15. public class HttpsPage implements IMarker {
  16. //Page code...
  17. }

22.4. URLs encryption in detail

In chapter 10.6 we have seen how to encrypt URLs using CryptoMapper request mapper. To encrypt/decrypt page URLs CryptoMapper uses an instance of org.apache.wicket.util.crypt.ICrypt interface:

  1. public interface ICrypt
  2. {
  3. String encryptUrlSafe(final String plainText);
  4. String decryptUrlSafe(final String encryptedText);
  5. ...
  6. }

The default implementation for this interface is class org.apache.wicket.util.crypt.SunJceCrypt. It provides password-based cryptography using PBEWithMD5AndDES algorithm coming with the standard security providers in the Java Runtime Environment.

For better security it is recommended to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files

By using CryptoMapper(IRequestMapper wrappedMapper, Application application) constructor the mapper will use the configured org.apache.wicket.util.crypt.ICryptFactory from org.apache.wicket.settings.SecuritySettings.getCryptFactory(). To use a stronger cryptography mechanism there are the following options:

  • The first option is to use constructor CryptoMapper(IRequestMapper wrappedMapper, Supplier cryptProvider) and give it an implementation of java.util.function.Supplier that returns a custom org.apache.wicket.util.crypt.ICrypt.

  • The second option is to register a cipher factory at application level with method setCryptFactory(ICryptFactory cryptFactory) of class SecuritySettings:

  1. @Override
  2. public void init() {
  3. super.init();
  4. getSecuritySettings().setCryptFactory(new SomeCryptFactory());
  5. setRootRequestMapper(new CryptoMapper(getRootRequestMapper(), this));
  6. }

Since version 6.19.0 Wicket uses org.apache.wicket.core.util.crypt.KeyInSessionSunJceCryptFactory as a default factory for ICrypt objects. This factory generates a unique key for each user that is stored in her HTTP session. This way it helps to protect the application against CSRF) for each user of the application. The url itself serves as encrypted token_Prevention_Cheat_Sheet#Encrypted_Token_Pattern)

org.apache.wicket.core.util.crypt.KeyInSessionSunJceCryptFactory binds the http session if it is not already bound! If the application needs to run in stateless mode then the application will have to provide a custom implementation of ICryptFactory that stores the user specific keys by other means.

22.5. CSRF protection

CryptoMapper helps preventing CSRF attacks by making the urls impossible to be guessed by an attacker but still there is some theoretical chance this to happen.

To further help against this kind of vulnerability Wicket provides CsrfPreventionRequestCycleListener - a IRequestCycleListener that forbids requests made from a different origin. By default only actions are forbidden, i.e. a request coming from different origin cannot execute Link.onClick() or submit forms (Form.onSubmit()). Any request to render pages are still allowed so Wicket pages could be easily embedded in other applications.

MyApplication.java

  1. @Override
  2. protected void init() {
  3. super.init();
  4. getRequestCycleListeners().add(new CsrfPreventionRequestCycleListener());
  5. // ...
  6. }

CsrfPreventionRequestCycleListener is highly configurable. It allows to define a whitelist of allowed origins via addAcceptedOrigin(String acceptedOrigin), to enable/disable it dynamically by overriding isEnabled(), to define different kind of actions when a request is rejected or allowed, to set custom error message and code for the rejected requests.

CsrfPreventionRequestCycleListener is not an alternative to CryptoMapper! Both of them could be used separately or in tandem to prevent CSRF attacks depending on the application requirements.

22.6. Content Security Policy (CSP)

In Wicket 9 support for a Content Security Policy (or CSP) has been added. CSP is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement to distribution of malware. See MDN for more information on CSP.

The default CSP set by Wicket is very strict. It requires all scripts and stylesheets to be rendered with a nonce. Wicket will automatically attach the nonce to all header contributions that support this (i.e. subclasses of the AbstractCspHeaderItem class). Images, fonts and (i)frames are allowed from self, the current host. All other resources are blocked. This includes any inline styling or Javascript (such as an onclick attribute). The exact CSP is:

  1. Content-Security-Policy: default-src 'none'; script-src 'strict-dynamic' 'nonce-XYZ'; style-src 'nonce-XYZ'; img-src 'self'; connect-src 'self'; font-src 'self'; manifest-src 'self'; child-src 'self'; frame-src 'self'; base-uri 'self';

In developer mode, the CSP is extended with a reporting directive that reports violations at a special endpoint in the application that logs the violation. This is conveniant while developing an application, but care should be taken when this is enabled on production. The cspviolation endpoint must be an open endpoint and all data sent to that URL will end up in the logs. To prevent the server log from filling the disk, make sure the org.apache.wicket.csp.ReportCSPViolationMapper logger has a limit on its disk usage.

22.6.1. Configuring the Content Security Policy

The Content Security Policy is managed by the ContentSecurityPolicySettings that can be accessed via WebApplication.getCspSettings(). This class maintains two instances of CSPHeaderConfiguration, each of which contains the directives for the CSP HTTP headers Content-Security-Policy and Content-Security-Policy-Report-Only. The first header defines the policies that are actually enforced by the browser, whereas the second header defines a policy for which the browser will only report violations. Note that violations can also be reported on the enforced policy and that reporting requires a report-uri directive.

For applications that cannot adhere to a CSP, the CSP can be disabled with the following call in your Application class:

  1. @Override
  2. protected void init() {
  3. super.init();
  4. getCspSettings().blocking().disabled();
  5. // ...
  6. }

As mentioned before, Wicket uses a very strict CSP by default. This preset can be selected with the following code:

  1. getCspSettings().blocking().strict();

A thrid preset is available that allows unsafe inline Javascript and styling and the use of unsafe eval. As can be inferred from the names, use of unsafe is not recommended. It removes the most important protection offered by CSP. However, older applications may not be ready to apply a strict CSP. For those applications, CSPHeaderConfiguration.unsafeInline() can be a starting point for the path to a strict CSP.

CSPHeaderConfiguration defines several methods to tune the Content Security Policy for your application. Additional sources can be whitelisted for certain via the add(CSPDirective, …​) methods. For example, the code below whitelists images rendered via a data: url, fonts loaded from https://maxcdn.bootstrapcdn.com and a single CSS file.

  1. getCspSettings().blocking()
  2. .add(CSPDirective.IMG_SRC, "data:")
  3. .add(CSPDirective.FONT_SRC, "https://maxcdn.bootstrapcdn.com")
  4. .add(CSPDirective.STYLE_SRC,
  5. "https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css");

22.6.2. Guidelines for strict CSP support

To comply to the strict CSP set by default, you’ll need to follow a few rules. Most importantly, you cannot use inline styling or Javascript. This includes:

  • style attributes in the markup or via an AttributeModifier.

  • Inline event handlers, such as onclick or onsubmit, in the markup or via an AttributeModifier.

  • Including stylesheets directly from the markup without whitelisting them.

  • Including Javascript directly from the markup. Whitelisting is not possible due to the strict-dynamic rule.

  • Rendering style attributes from Javascript in dynamically generated markup. Modifying the style DOM property is still possible.

  • Load other resources from external domains without whitelisting them.

For most of these restrictions Wicket provides alternative solutions that do work with a strict CSP. First of all, replace all inline styling with proper stylesheets and use CSS selectors to target the elements in the DOM. Replace the AttributeModifier with a style attribute for one with the class attribute. For Javascripts that manipulate the style attribute, you may have to update the script to use the DOM property instead.

When a component includes a stylesheet directly from the markup as seen below, there are two possible solutions:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <html xmlns:wicket="http://wicket.apache.org">
  3. <head>
  4. <link rel="stylesheet"
  5. href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" />
  6. </head>
  7. <body>...</body>
  8. </html>

The first solution is to whitelist the stylesheet, as seen above. The second solution is to move the stylesheet to a header contribution in renderHead. This will allow Wicket to render the required nonce attribute. For Javascript resources, this is the only possible solution:

  1. @Override
  2. public void renderHead(IHeaderResponse response) {
  3. super.renderHead(response);
  4. response.render(CssHeaderItem.forReference(
  5. new CssResourceReference(MyComponent.class, "mycomponent.css")));
  6. }

When your component relies on inline event handlers, such as onclick or onsubmit, you can convert these to an OnEventHeaderItem. Again, this will allow Wicket to add the required nonce attribute.

  1. @Override
  2. public void renderHead(IHeaderResponse response) {
  3. super.renderHead(response);
  4. response.render(OnEventHeaderItem.forComponent(this, "submit",
  5. "return confirm('Do you really want to submit?')"));
  6. }

22.7. Package Resource Guard

Wicket internally uses an entity called package resource guard to protect package resources from external access. This entity is an implementation of interface org.apache.wicket.markup.html.IPackageResourceGuard.

By default Wicket applications use as package resource guard class SecurePackageResourceGuard, which allows to access only to the following file extensions (grouped by type):

File

Extensions

JavaScript files

.js

CSS files

.css

HTML pages

.html

Textual files

.txt

Flash files

.swf

Picture files

.png, .jpg, .jpeg, .gif, .ico, .cur, .bmp, .svg

Web font files

.eot, .ttf, .woff

To modify the set of allowed files formats we can add one or more patterns with method addPattern(String). The rules to write a pattern are the following:

  • patterns start with either a “+” or a “-“ In the first case the pattern will add one or more file to the set while starting a pattern with a “-” we exclude all the files matching the given pattern. For example pattern “-web.xml” excludes all web.xml files in all directories.

  • wildcard character “*” is supported as placeholder for zero or more characters. For example pattern “+\*.mp4” adds all the mp4 files inside all directories.

  • subdirectories are supported as well. For example pattern “+documents/*.pdf” adds all pdf files under “documents” directory. Character “\*” can be used with directories to specify a nesting level. For example “+documents/*/\*.pdf” adds all pdf files placed one level below “documents” directory.

  • a double wildcard character “*\*” indicates zero or more subdirectories. For example pattern “+documents/*\*/\*.pdf” adds all pdf files placed inside “documents” directory or inside any of its subdirectories.

Patterns that allow to access to every file with a given extensions (such as “+\*.pdf”) should be always avoided in favour of more restrictive expressions that contain a directory structure:

  1. //Application class code...
  2. @Override
  3. public void init()
  4. {
  5. IPackageResourceGuard packageResourceGuard = application.getResourceSettings()
  6. .getPackageResourceGuard();
  7. if (packageResourceGuard instanceof SecurePackageResourceGuard)
  8. {
  9. SecurePackageResourceGuard guard = (SecurePackageResourceGuard) packageResourceGuard;
  10. //Allow to access only to pdf files placed in the “public” directory.
  11. guard.addPattern("+public/*.pdf");
  12. }
  13. }

22.8. External Security Checks

Since Mozilla released their site to check if web pages have security issues named Mozilla Observatory a few things which can be done to get a high grade within this ranking without using further frameworks.

Add a request cycle listener to your web application and adjust the headers to fit your requirements:

  1. @Override
  2. protected void init()
  3. {
  4. super.init();
  5. getRequestCycleListeners().add(new AbstractRequestCycleListener(){
  6. @Override
  7. public void onEndRequest(RequestCycle cycle)
  8. {
  9. WebResponse response = (WebResponse) cycle.getResponse();
  10. response.setHeader("X-XSS-Protection", "1; mode=block");
  11. response.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload");
  12. response.setHeader("X-Content-Type-Options", "nosniff");
  13. response.setHeader("X-Frame-Options", "sameorigin");
  14. response.setHeader("Content-Security-Policy", "default-src https:");
  15. }
  16. });
  17. }

Add this configuration to your web.xml (or let your server redirect to https):

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <security-constraint>
  3. <web-resource-collection>
  4. <web-resource-name>Entire Application</web-resource-name>
  5. <url-pattern>/*</url-pattern>
  6. </web-resource-collection>
  7. <user-data-constraint>
  8. <transport-guarantee>CONFIDENTIAL</transport-guarantee>
  9. </user-data-constraint>
  10. </security-constraint>

After this changes you have to check if your web application continues to work because it fits the requirements given with these headers. For example that resources could not be requested from other domains anymore.

22.9. Summary

In this chapter we have seen the components and the mechanisms that allow us to implement security policies in our Wicket-based applications. Wicket comes with an out of the box support for both authorization and authentication.

The central element of authorization mechanism is the interface IAuthorizationStrategy which decouples our components from any detail about security strategy. The implementations of this interface must decide if a user is allowed to instantiate a given page or component and if she/he can perform a given action on it.

Wicket natively supports role-based authorizations with strategies MetaDataRoleAuthorizationStrategy and AnnotationsRoleAuthorizationStrategy. The difference between these two strategies is that the first offers a programmatic approach for role handling while the second promotes a declarative approach using built-in annotations.

After having explored how Wicket internally implements authentication and authorization, we continued with how to configure our applications to support HTTPS and how to specify which pages must be served over this protocol.

We’ve explored the protection Wicket offers against CSRF attacks with URL encryption and the CsrfPreventionRequestCycleListener. This was followed by an explanation of the Content Security Policy used by Wicket and how to tune this for your application.

In the last paragraph we have seen how Wicket protects package resources with a guard entity that allows us to decide which package resources can be accessed from users.