Безопасность CMS Engine

В этой статье вы узнаете, как настроить параметры безопасности для CMS Engine.

Настройка signed cookies с использованием CloudFront

Настройка доступа к ограниченному контенту через AWS CloudFront может быть достигнута с использованием подписанных куки (signed cookies). В этом разделе предоставляется подробное руководство по внедрению подписанных куки CloudFront для DC CMS с Single Sign-On (SSO).

Применение подписанных куки CloudFront обеспечивает точный контроль над доступом к контенту, особенно когда важно сохранить существующие URL-адреса или предоставить доступ к множеству ограниченных файлов.

Чтобы настроить подписанные файлы cookie с использованием CloudFront:

1. Настройте CloudFront для использования подписанных куки, обратившись к инструкциям в данном руководстве.

2. Интегрируйте класс Groovy в классы вашего сайта.

package org.dccms.aws.utils

@Grapes(
    @Grab(group='com.amazonaws', module='aws-java-sdk-cloudfront', version='1.11.435', initClass = false)
)

import java.util.Date;
import groovy.util.logging.Slf4j

import javax.servlet.http.Cookie

import com.amazonaws.auth.PEM;
import com.amazonaws.services.cloudfront.CloudFrontCookieSigner
import com.amazonaws.services.cloudfront.util.SignerUtils.Protocol

@Slf4j
class CloudFrontUtils {

    static void setSignedCookies(request, response, siteConfig) {
        if (!signedCookiesExist(request)) {
            def protocol = Protocol.https;
            def domain = siteConfig.getString('aws.cloudFront.signedCookies.domain')
            def resourcePath = siteConfig.getString('aws.cloudFront.signedCookies.resourcePath')
            def keyPairId = siteConfig.getString('aws.cloudFront.signedCookies.keyPairId')
            def privateKeyContent = siteConfig.getString('aws.cloudFront.signedCookies.privateKey')
            def privateKey = PEM.readPrivateKey(new ByteArrayInputStream(privateKeyContent.getBytes('UTF-8')))
            def cloudFrontTimeToExpire = siteConfig.getLong('aws.cloudFront.signedCookies.cloudFrontTimeToExpire')
            def cloudFrontExpiresOn = new Date(System.currentTimeMillis() + (cloudFrontTimeToExpire * 60 * 1000))
            def cookieMaxAge = siteConfig.getLong('aws.cloudFront.signedCookies.cookieMaxAge') * 60
            def cookieSecure = true
            def cookiePath = '/'

            def cookies = CloudFrontCookieSigner.getCookiesForCustomPolicy(protocol, domain, privateKey, resourcePath, keyPairId, cloudFrontExpiresOn, null, null)

            def signatureCookie = new Cookie(cookies.signature.key, cookies.signature.value)
                signatureCookie.secure = cookieSecure
                signatureCookie.maxAge = cookieMaxAge
                signatureCookie.path = cookiePath

            def keyPairIdCookie = new Cookie(cookies.keyPairId.key, cookies.keyPairId.value)
                keyPairIdCookie.secure = cookieSecure
                keyPairIdCookie.maxAge = cookieMaxAge
                keyPairIdCookie.path = cookiePath

            def policyCookie = new Cookie(cookies.policy.key, cookies.policy.value)
                policyCookie.secure = cookieSecure
                policyCookie.maxAge = cookieMaxAge
                policyCookie.path = cookiePath

            response.addCookie(signatureCookie)
            response.addCookie(keyPairIdCookie)
            response.addCookie(policyCookie)
        }
    }

    static boolean signedCookiesExist(request) {
        def cookies = request.cookies
        for (int i = 0; i < cookies.length; i++) {
          if ('CloudFront-Key-Pair-Id' == cookies[i].name) {
              return true
          }
        }

        return false
    }

}

Copy-icon

3. Разработайте фильтр Groovy, который проверяет аутентификацию/авторизацию пользователя для соответствующих запросов, а затем вызывает метод класса: CloudFrontUtils.setSignedCookies(request, response, siteConfig)

4. Добавьте следующую конфигурацию в site-config.xml:

<!-- Добавьте эту конфигурацию в site-config.xml движка (engine) -->
<CloudFrontConfiguration>
    <!-- Укажите дополнительные параметры конфигурации CloudFront при необходимости -->
    <Parameter1>Value1</Parameter1>
    <Parameter2>Value2</Parameter2>
    <!-- ... -->
</CloudFrontConfiguration>

Copy-icon

5. Настройте HTML-страницу ошибки на CloudFront, специально предназначенную для обработки ошибок 403. Эта страница должна быть настроена для вызова JavaScript-перенаправления для запуска потока единого входа (SSO), направляющего к Engine. Это может быть похоже на следующее:

<!DOCTYPE html>
<!-- saved from url=(0014)about:internet -->
<html lang="en">
  <head>
    ...
    <script>
      if(document.location.hash.indexOf("dlink") == -1) {
        document.location = "/auth-asset?a=" + document.location.pathname + "#dlink";
      }
    </script>
    ...
  </head>
  <main id="main-content">
    <!-- PAGE CONTENT -->
    <script>
      if(document.location.hash.indexOf("dlink") != -1) {
        document.getElementById("headline").innerHTML = "403";
        document.getElementById("message").innerHTML = "You do not have permissions to access the requested resource. You will be redirected to the home page momentarily.";
        setTimeout(function(){ document.location = "/" }, 5000);
      }
    </script>
</body></html>

Copy-icon

6. Кроме того, создайте страницу /auth-asset на вашем веб-сайте, содержащую сценарий Groovy. Его единственная цель – перенаправление обратно к ресурсу, предполагая, что аутентификация и настройка cookie были уже обработаны фильтрами.

if(params.a) {
  response.sendRedirect(params.a)
}

Copy-icon

Настройка безопасности Engine для проекта

Данное руководство поможет вам настроить CMS Engine, чтобы:

  • добавить аутентификацию на ваш проект
  • добавить авторизацию с целью ограничения доступа к определенным страницам и URL-адресам вашего проекта

Добавление аутентификации

Добавление страницы входа в систему

В CMS Studio создайте главную страницу и страницу входа в систему.
Шаблон страницы должен содержать форму, отправляющую запрос методом POST на /cms-security-login и передающую параметры usernamepassword и rememberMe, как показано ниже:

<form action="/cms-security-login" method="post">
    <label for="username">Username: </label>
    <input type="text" name="username"/>
    <br/>
    <label for="password">Password: </label>
    <input type="password" name="password"/>
    <br/>
    <input type="checkbox" name="rememberMe" value="true">Remember Me</input>
    <br/>
    <button type="submit">Sign in</button>
</form>

Copy-icon

Добавление страницы выхода из системы

Добавьте ссылку в глобальный заголовок, указывающую на /cms-security-logout:

<a href="/cms-security-logout">Log Out</a>

Copy-icon

Добавление авторизации

Добавление авторизации позволяет ограничить доступ к определенным страницам и URL-адресам вашего проекта.

Ограничение доступа к страницам

Чтобы ограничить доступ к страницам на основе аутентификации пользователя или наличия у него определенных ролей, следуйте следующим шагам:

1. Откройте CMS Studio и перейдите в Инструменты сайта в боковой панели слева.

Изображение статьи

2. Перейдите в Типы контента и выберите тип контента, соответствующий страницам, которые требуют ограничений.

Изображение статьи

3. В разделе Элементы управления выберите Repeating Group и через drag&drop добавьте ее на форму страницы.

Изображение статьи

4. Настройте свойства Repeating Group, указав Authorized Roles в поле Заголовок, а в поле Имя/Имя переменной- authorizedRoles.

Интерфейс пользователя (UI) автоматически заполняет поле Имя/Имя переменной и добавляет постфиксы во время ввода в поле Заголовок. Обязательно исключите постфикс _o, так как authorizedRoles - зарезервированное имя переменной, используемое DC CMS. Полный список имен переменных, используемых DC CMS, можно найти здесь.

Изображение статьи

Префикс ROLE_ является допустимым для значений в authorizedRoles.

5. Вставьте через drag&drop элемент управления Ввод в добавленную ранее Repeating Group, настроив поле Заголовок как Role и поле Имя/Имя переменной как role. Убедитесь, что Ввод обязателен, установив флажок в поле Обязательно в разделе Ограничения.

Интерфейс автоматически заполнит поле Имя/Имя переменной и добавит постфикс при вводе данных в поле Заголовок. Уберите постфикс, поскольку DC CMS полагается на переменную role для обеспечения доступа к странице.

Изображение статьи

DC CMS использует переменную role для контроля доступа. Обратитесь к разделу “Имена переменных элементов управления формой” для полного списка переменных, используемых DC CMS.

6. Сохраните изменения.

После внесения этих изменений любой автор контента, включая вас, может легко перейти на любую страницу этого типа контента и назначить необходимые роли для доступа к странице. Два различных статуса аутентификации, а именно "Anonymous" и "Authenticated", могут быть использованы в дополнение к ролям, указанным в профилях пользователей.

Алгоритм комплексной проверки доступа, выполняемый в CMS Engine, описан ниже:

  1. Если у страницы нет назначенных ролей, аутентификация не требуется.
  2. Если страница связана с ролью "Anonymous", аутентификация не требуется.
  3. Если страница связана с ролью "Authenticated", требуется только аутентификация.
  4. Если страница связана с другими ролями, пользователь должен быть аутентифицирован и обладать хотя бы одной из этих ролей.

Ограничение доступа к URL-адресам

Иногда ограничение доступа к отдельной странице может быть недостаточным. Существуют случаи, когда необходимо ограничить все поддерево проекта или ограничить доступ к нескольким статическим ресурсам. Для решения этой проблемы DC CMS предлагает параметры конфигурации, позволяющие ограничивать доступ на основе шаблонов URL. Просто включите конфигурации, подобные следующим, в файл конфигурации проекта, который находится по пути Инструменты сайта > Конфигурация > Конфигурация ядра проекта.

<security>
    <urlRestrictions>
        <restriction>
            <url>/user/*</url>
            <expression>hasAnyRole({'user'\, 'admin'})</expression>
        </restriction>
    </urlRestrictions>
</security>

Copy-icon

<urlRestrictions> может содержать любое количество элементов <restriction>. Каждое ограничение включает в себя шаблон пути в стиле Ant (<url>) и выражение Spring EL (<expression>), выполняемое относительно текущего профиля. В случае, если запрос соответствует указанному URL, и выражение оценивается как false, доступ будет запрещен.

Следующие выражения могут быть использованы:

  • isAnonymous()
  • isAuthenticated()
  • hasRole('role')
  • hasAnyRole({'role1'\, 'role2'})
  • permitAll()
  • denyAll()

Для шаблона пути в стиле Ant в элементе <url><url>/*</url> указывает только на один уровень URL, а <url>/**</url> указывает на все URL. Дополнительную информацию по сопоставлению шаблонов пути в стиле Ant можно найти здесь.

Для выражения hasAnyRole помните о необходимости экранировать запятую “,” между ролями внутри выражения.

Доступ к атрибутам пользователя

После завершения настройки аутентификации и авторизации вы можете использовать объект authToken в шаблонах и скриптах для получения атрибутов текущего пользователя. Класс объекта будет изменяться в зависимости от используемого поставщика аутентификации, но вы всегда можете получить экземпляр CustomUser, используя свойство principal.

Отображение имени текущего пользователя в Freemarker:

<#if authToken??>
  Hello ${authToken.principal.attributes.firstName}!
<#else>
  <#-- show login button -->
</#if>

Copy-icon

URL-ы

Вход в систему

Вы можете использовать следующие свойства для настройки различных аспектов URL-адресов входа в систему:

  • свойство security.login.formUrl позволяет установить URL-адрес страницы формы входа в систему. По умолчанию это /login.

  • свойство security.login.defaultSuccessUrl позволяет настроить URL-адрес для перенаправления, если вход в систему был успешным и пользователя не удалось перенаправить на предыдущую страницу. Значение по умолчанию - /.

  • свойство security.login.alwaysUseDefaultSuccessUrl позволяет настроить, всегда ли выполнять перенаправление на URL успешного входа по умолчанию. По умолчанию установлено значение false.

  • свойство security.login.failureUrl позволяет настроить URL-адрес для перенаправления в случае сбоя входа в систему. По умолчанию установлено значение /login?login_error=true.

<security>
  <login>
    <formUrl /> (The URL of the login form page)
    <defaultSuccessUrl /> (The URL to redirect to if the login was successful and the user could not be redirected to the previous page)
    <alwaysUseDefaultSuccessUrl /> (Sets whether to always redirect to the default success URL after a successful login)
    <failureUrl /> (The URL to redirect to if the login fails)
  </login>
</security>

Copy-icon

Выход из системы

Свойство security.logout.successUrl позволяет настроить URL-адрес для перенаправления после успешного выхода из системы. По умолчанию установлено значение /.

<security>
  <logout>
    <successUrl /> (The URL to redirect after a successful logout)
  </logout>
</security>

Copy-icon

Доступ запрещен

Свойство security.accessDenied.errorPageUrl позволяет установить URL страницы, отображаемой при отказе пользователю в доступе к определенному ресурсу. Значение по умолчанию — /access-denied.

<security>
  <accessDenied>
    <errorPageUrl /> (The URL of the page to show when access has been denied to a user to a certain resource)
  </accessDenied>
</security>

Copy-icon

Ограничения URL-адресов

Свойство security.urlRestrictions позволяет настроить ограничения URL и может включать несколько элементов ограничений. Каждое ограничение состоит из шаблона пути в стиле Ant (<url>) и выражения Spring EL (<expression>), выполняемым в контексте текущего профиля. Если запрос соответствует URL, и выражение оценивается как false, доступ будет запрещен.

Для шаблона пути в стиле Ant в виде <url> используется <url>/*</url> для указания только одного уровня URL и <url>/**</url> для указания всех URL. Дополнительную информацию по сопоставлению шаблонов путей в стиле Ant можно найти здесь.

<security>
  <urlRestrictions> (Contains any number of restriction elements)
    <restriction> (Restriction element, access is denied if a request matches the URL, and the expression evaluates to false)
      <url /> (URL pattern)
      <expression /> (Spring EL expression)
    </restriction>
  </urlRestrictions>
</security>

Copy-icon