Security

  Written by The Jahia Team
 
Developers
   Estimated reading time:
7.0 7.1 7.2
This section provides a brief overview of the security considerations and the ways for reducing risks and protecting system and data.

Authentication valves
Digital Experience Manager already contains a set of built-in authentication valves. Custom valves can be implemented and deployed easily to extend the standard authentication pipeline in Digital Experience Manager.

User authentication and data

Explain how the user data is protected and which security-related concerns can arise in regard to the user authentication

Template and actions protection
How configure correctly your modules, templates and actions executed on users request.

Miscellaneous
Other security issues.

 

Authentication valves

Overview

Digital Experience Manager contains a set of built-in valves, organized in a pipeline, which are used to authenticate the user, making a request to the web application, and keep track of user session.

The pipeline can be easily extended with custom valves, e.g., authenticating the user against an external system.

Next sections provide an overview of the built-in valves as well as a short How-to for implementing and registering a custom valve.

This description applies to Digital Experience Manager 6.6.0.0.

Built-in valves

The authentication valves pipeline is configured in a Spring bean definition file org/jahia/defaults/config/spring/auth/applicationcontext-auth-pipeline.xml, packaged into the jahia-impl-6.6.0.0.jar file.

The pipeline is executed in a sequence (as a chain). If a valve succeeds to authenticate the user, the pipeline execution is stopped. Otherwise it is delegated to the next valve in the pipeline.

The default list of valves and their execution sequence (the most top have the precedence) is as follows:

  1. HttpBasicAuthValve - uses Basic access authentication mechanism for authenticating the user requests.
  2. TokenAuthValve - used by Jahia for internal requests, e.g. for e-mail newsletter content rendering, and relies on the jahiatoken HTTP request header.
  3. LoginEngineAuthValve - implements the login form processing for provided username and password values.
  4. SessionAuthValve - authenticates the request by looking up the valid user in the current HTTP session scope. If a non-guest user is found in the session scope the pipeline execution stops here. Otherwise a next valve is invoked.
  5. CookieAuthValve - relies on the usage of "Remember me" feature in the login screen, when a special persistent cookie is stored in the user's Web browser. This cookie is used to lookup and authenticate the user requests, having that particular cookie.
  6. ContainerAuthValve - utilizes the authentication scheme, provided by the Servlet container (application server itself). In such a case, the authentication is done by the server and the valid user is available to the ContainerAuthValve via request.getUserPrincipal().
  7. CasAuthValve (*) - implements the single sign-on using an external Central Authentication Service (e.g. provided by a Jasig project.
  8. SpnegoAuthValve (*) - authenticate users with SPNEGO mechanism, which relies on NTLM and Kerberos negotiable sub-mechanisms.

The default pipeline can be extended or overridden completely by providing a custom Spring bean definition with id authPipeline.

Implementing custom authentication valve

Here we will show how a custom authentication valve can be implemented and registered in Digital Experience Manager 6.6 using a hypothetical example.

Let's consider that an external service (server) is doing the user authentication and upon successful "login" operation it stores a special authentication ticket (token) in the header (say, Authentication-Ticket) of HTTP request that is forwarded to Jahia.

The skeleton code of the valve class will look as follows:

package org.jahia.params.valves;

import javax.servlet.http.HttpServletRequest;

import org.jahia.params.ProcessingContext;
import org.jahia.pipelines.PipelineException;
import org.jahia.pipelines.valves.ValveContext;
import org.jahia.registries.ServicesRegistry;
import org.jahia.services.usermanager.JahiaUser;

public class MyCustomAuthValve extends AutoRegisteredBaseAuthValve {

    public void invoke(Object context, ValveContext valveContext) throws PipelineException {
        // Retrieve the context, the current request and the header value
        AuthValveContext authContext = (AuthValveContext) context;
        HttpServletRequest request = authContext.getRequest();
        String ticket = request.getHeader("Authentication-Ticket");

        if (ticket != null) {
            // we got the authentication ticket -> verify it and get the username
            String username = retrieveAuthenticatedUsername(ticket, request);
            if (username != null) {
                // lookup the user by the username
                JahiaUser user = ServicesRegistry.getInstance().getJahiaUserManagerService()
                        .lookupUser(username);
                if (user != null) {
                    // check if the user account is not locked
                    if (isAccountLocked(user)) {
                        return;
                    }
                    // user found
                    // 1) set the current JCR session user
                    authContext.getSessionFactory().setCurrentUser(user);
                    // 2) update the HTPP session user
                    request.getSession().setAttribute(ProcessingContext.SESSION_USER, user);

                    // we are done, the authentication pipeline execution stops here
                    return;
                }
            }
        }

        // invoke next valve
        valveContext.invokeNext(context);
    }

    private String retrieveAuthenticatedUsername(String ticket, HttpServletRequest request) {
        String username = null;

        // Here an external authentication service is contacted in order to verify
        // the ticket validity and retrieve the username

        return username;
    }

}

The valve class extends the AutoRegisteredBaseAuthValve for easier registration in Digital Experience Manager authentication pipeline.

The main steps in the valve are:

  1. Check if the required authentication information is present or a special condition is met (in our example it is the presence of the Authentication-Ticket HTTP request header).
  2. Contact the external service, which did the authentication, in order to verify the ticket validity and obtain the corresponding username.
  3. Lookup the JahiaUser by the obtained username.
  4. Check if the user account is not locked.
  5. Update the JCR and HTTP session objects with the found user.
  6. If all successful, stop the execution of the authentication pipeline, otherwise invoke the next valve in the pipeline.

When the implementation is finished, the valve can be registered in Digital Experience Manager using a Spring bean definition (e.g. in your custom module). With our example, it can be done as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
       
        <bean id="MyCustomAuthValve" class="org.jahia.params.valves.MyCustomAuthValve"/>
</beans>

In such a case, the valve will be added at the end of the authentication pipeline.

In most of the cases a custom valve should go after the SessionAuthValve, which uses a valid user (non-guest), found in the current HTTP session, and does not invokes the next valves in the pipeline in such a case.

In some cases, it could be needed to add the custom valve "before" a particular one, e.g. before the SessionAuthValve, for example if the authentication scheme does not imply the explicit logout mechanism and the session is not invalidated. The registration of the valve in such case can use an explicit position:

    <bean id="MyCustomAuthValve" class="org.jahia.params.valves.MyCustomAuthValve">
        <property name="positionBefore" value="SessionAuthValve"/>
    </bean>

Available properties for positioning are:

  • position - absolute position in the pipeline (a 0-based number)
  • positionAfter - relative position to register valve after the specified one (the value is the ID of the valve, the current one should be inserted after)
  • positionBefore - relative position to register valve before the specified one (the value is the ID of the valve, the current one should be inserted before)

User authentication and data

This page describes how the user data is protected and which security-related concerns can arise in regard to the user authentication.

Password encryption

Passwords for Digital Experience Manager users are stored in the JCR repository one way encrypted. The default hash algorithm for user passwords is: SHA-256 (with random 32 byte salt and 4096 iterations). For the root user and the Jahia Tools user we use even more stonger hash: PBKDF2 (Password-Based Key Derivation Function 2) with random 64 byte salt, 32 byte hash size and 8192 iterations.

The password hashing algorithm could be changed for all user and also sepately for "powerful" users (root and Jahia Tools user).

Add into <digital-factory-config>/jahia/jahia.custom.properties the following entry to adjust the password hash function, used by default:

jahia.passwordService.defaultDigester=pwdDigesterSHA256RandomSalt32x4096

This password digester is used for all users in DX. A restart of DX is required for changes to be effective. After the changes all further user password (for new users or when password is modified for existing users) will use that new hash algorithm. The existing passwords will still continue to work. A password hash is prefix with an ID of the hashing algorithm, which was used for its encryption.

Out of the box, the following hash algorithms are available in DX (in the order of their strength: from weak to very strong):

  • pwdDigesterSHA1RandomSalt32x4096
  • pwdDigesterSHA256RandomSalt32x4096
  • pwdDigesterSHA512RandomSalt32x4096
  • pwdDigesterPBKDF2RandomSalt64Key32x8192

Their IDs are self speaking and depict the hashing algorithm used.

Note, please, the stronger the algorithm is, the slower it is in hashing and verifying the passwords (on user login). The default one (pwdDigesterSHA1RandomSalt32x4096) provides a good balance between strength and speed.

You could also define and use a custom algorithm in the following way. The example below shows how to define a new password hasher that uses SHA-384 with random 32 byte salt and 4096 iterations.

Add the following Spring been definitions into <digital-factory-config>/jahia/applicationcontext-custom.xml file:

    <bean id="pwdDigesterSHA384RandomSalt32x4096" class="org.jahia.services.pwd.JasyptPasswordDigester">
        <constructor-arg index="0" value="s384"/>
        <constructor-arg index="1">
            <bean class="org.jahia.services.pwd.ProccessorAwarePooledStringDigester">
                <property name="algorithm" value="SHA-384"/>
                <property name="saltSizeBytes" value="32"/>
                <property name="iterations" value="4096"/>
                <property name="unicodeNormalizationIgnored" value="true"/>
            </bean>
        </constructor-arg>
    </bean>

Note, the bean ID and the constructor argument with index 0 should be unique identifiers of your hash algorithm for DX.

Add into <digital-factory-config>/jahia/jahia.custom.properties the following entry to adjust the password hash function and use your new one by default:

jahia.passwordService.defaultDigester=pwdDigesterSHA384RandomSalt32x4096

Restart your DX to make the changes effective.

Jahia Tool Manager password

The Jahia Tool Manager password is specified in the jahia.properties (PBKDF2 hash). Due to security considerations, this password can be only reset manually directly in the jahia.properties file. After you do that, you need to restart the server. Jahia Tools Area provides a utility JSP for encrypting clear text passwords: http://localhost:8080/tools/pwdEncrypt.jsp

Resetting root user password

Here is the procedure for resetting the password of the root user in Digital Experience Manager 7.0:

  1. Create a plain text file named root.pwd in the data directory: <jahia-web-app-root>/WEB-INF/var (Digital Experience Manager 7.0.0.0 and 7.0.0.1) or <jahia-install-dir>/digital-factory-data (for Digital Experience Manager 7.0.0.2+) with a new root user password in clear text
  2. Restart the server
  3. You can now login with your new password as root

The following is the procedure for resetting the password of the root user in Jahia 6.6:

  1. Create a plain text file <jahia-web-app-root>/WEB-INF/etc/config/root.pwd with a new root user password in clear text
  2. Restart the server
  3. You can now login with your new password as root

Locking a user account

User account can be locked in Jahia 6.6 either using a User Management UI in Jahia Administration or using the Content Manager and setting the j:accountLocked property on the corresponding user node to true.

Sensitive user data

Non-public user profile data (user node properties) are not exposed for an unauthorized access.

Enabling SSL for logged in users

There is an option for "forcing" a switch to an SSL (HTTPS protocol) for a user session, from login to logout. This allows sites with higher security concerns to force secured connections for logged in users.

In order to enable it, please:

  1. Rename the file <jahia-web-app-root>/WEB-INF/etc/config/urlrewrite-ssl.xml.disable into urlrewrite-ssl.xml
  2. Go into Jahia Administration UI and flush output HTML caches
  3. Restart you server
  4. Now the login forms will contain a URL, leading to HTTPS and logout links will do a redirect back to HTTP.

The behavior could be adjusted by changing the URL rewrite rules in the urlrewrite-ssl.xml file.

Template and action protection

Module resources

In all Digital Experience Manager modules, the following sensitive resources are protected from a direct access via web browser:

  • content definition files (*.cnd)
  • rule files (*.drl and *.dsl)
  • I18N resource bundles and template properties (*.properties)
  • scripting templates for mail notifications (/mails/*.*)
  • all resources under META-INF and WEB-INF folders
  • JSP templates (*.jsp)

So, when developing new modules take care, please, of protecting sensitive files by e.g. placing them into the META-INF folder, which is protected by default.

META-INF is also the default folder for content and rule definition files.

XSS vulnerabilities

When developing custom JSP templates, especially those, requiring user interaction, the following rules of thumb can be used to reduce the risk of simple cross-site scripting XSS attacks, such as JavaScript code injection.

when using values of request parameters in templates markup, they need to be escaped, e.g.:

<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
...
<label for="username"><fmt:message key="label.username"/></label>
<input type="text" name="username" id="username" value="${fn:escapeXml(param.username)}" />

expect that values, coming from request parameters can be not well-formed or malicious. For example, if your template expects a JCR node path as a request parameter path, you should take care of checking if the path value really corresponds to a node, i.e.:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="jcr" uri="http://www.jahia.org/tags/jcr" %>
...
<jcr:node path="${param.path}" var="myNode"/>
<c:if test="${not empty myNode}">
   do something
</c:if>
<c:if test="${empty myNode}">
   unknown node path
</c:if>

Protecting render actions

Render actions, which are subclasses of org.jahia.bin.Action, can be protected in several ways from execution by a unauthorized user or in the wrong context.

Valid authenticated user

An execution of an action can require a valid authenticated user. This is the default level of protection for all render actions.

If your action should be "available" for non-authenticated users or a protection is done in a different manner, you can "relax" this constraint by specifying a false value for action's requireAuthenticatedUser property in its Spring bean definition file.

<bean class="org.jahia.modules.forum.actions.AddTopic" >
    <property name="name" value="addTopic"/>
    <property name="requireAuthenticatedUser" value="false"/>
</bean>

Required permissions

You can additionally specify a permission or multiple permissions a user, which is trying to execute this action, should have on the target content node.

For example:

<bean class="org.jahia.modules.blog.AddBlogEntryAction">
    <property name="requiredPermission" value="addBlogEntry"/>
</bean>

An action can require multiple permissions:

    <property name="requiredPermission" value="accessIntranetArea+addBlogEntry"/>

or any of the specified permissions:

    <property name="requiredPermission" value="addBlogEntry|moderateBlog"/>

Required workspace

An action execution can be limited to a particular workspace (default or live).

For example, the publish action is relevant for the default workspace only:

<bean class="org.jahia.modules.defaultmodule.actions.PublishAction">
    <property name="name" value="publish"/>
    <property name="publicationService" ref="jcrPublicationService"/>
    <property name="requiredPermission" value="publish"/>
    <property name="requiredWorkspace" value="default"/>
</bean>

 

Miscellaneous

Disabling directory listing for WebDAV servlet

In case a WebDAV client access is not used for managing resources in the JCR content repository, it is recommended that the directory listing is disabled for the WebDAV servlet, mapped to /repository.

For this the following key in digital-factory-config/jahia/jahia.properties (on versions, prior to 7.0.0.2 - WEB-INF/etc/config/jahia.properties) file needs to be set to true:

######################################################################
### WebDAV ###########################################################
######################################################################
# Disable the directory listing for /repository servlet.
# (WebDAV client access won't work in such a case)
repositoryDirectoryListingDisabled = true