Skip to content

guide security quick S4 Basic Access Control

Santos Jiménez edited this page Oct 25, 2019 · 2 revisions

S4 Basic Access Control

This package describes basic security access controls.

S4-1

The principle of least privilege exists and is properly implemented in the application.

Purpose: Least privilege means, that the user has the minimum access rights to applications functionality that is needed for him to perform his tasks. This is one of the core principles of Application Security. Users with to big access rights might use them to influence the confidentiality, integrity or availability of the system.

Solution: This is a business requirement in the first place and has little to do with the technical platform the application is using. We need to identify all user roles that deal with the application, understand their work and the scope of their competences, and then implement security controls in the application as described with solution S4-2.

S4-2

Users should only be able to access functions, data files, URLs, controllers, services, and other resources, for which they possess specific authorization.

The access to the application functions is controlled by a group of roles with specific permissions. All groups and permissions are defined in one XML file which example looks like below:

<?xml version="1.0" encoding="UTF-8"?>
<access-control-schema>
    <group id="ReadMasterData" type="group">
       <permissions>
          <permission id="FindOffer"/>
       </permissions>
    </group>
    <group id="Cook" type="role">
      <inherits>
        <group-ref>ReadMasterData</group-ref>
      </inherits>
      <permissions>
        <permission id="SaveOffer"/>
      </permissions>
    </group>
</access-control-schema>

The usual place to store this definition is in the src/main/resources directory under /config/app/security/. To make the schema definition visible to Spring, following bean definitions must be included:

<bean id="AccessControlProvider" class="io.oasp.module.security.common.impl.accesscontrol.AccessControlProviderImpl"/>
<bean id="AccessControlSchemaProvider" class="io.oasp.module.security.common.impl.accesscontrol.AccessControlSchemaProviderImpl"/>

Based on the definition above users access rights (meaning belonging to a group containing a defined permission) can be enforced by using annotation @RolesAllowed as as in the example below:

@RolesAllowed(PermissionConstants.FIND_OFFER)
public List<OfferEto> findAllOffers() {
  return getBeanMapper().mapList(getOfferDao().findAll(), OfferEto.class);
}

If the user accessing the findAllOffers method belongs to a group which contains the permission PermissionConstants.FIND_OFFER, the method will be executed, else an exception will be thrown.

Additionally annotations like @PermitAll and @DenyAll can be used, to allow or deny all user access to a function.

S4-3

Access to sensitive records is protected, such that only authorized objects or data is accessible to each user (for example, protect against users tampering with a parameter to see or alter another user’s account).

Purpose: Access control must be made on two levels: functional (as described in S4-2) and data (S4-3). The user might be able to e.g. edit blog data, but he still must not be able to edit data he’s not the owner of. This problem is closely related to the problem of insecure direct object references. Refer to the (OWASP pages) for more information.

Solution: This requirement is not addressed by the devonfw platform (yet). In case a need exists to protect direct object references in the system (different objects are accessible for different user groups), it needs to be custom implemented and tested.

Note
The proper solution of the problem of insecure direct object references are secure direct object references (access control rights are checked), NOT indirect object references (parameters contain some kind of a secret) as some pages might suggest.

S4-4

Directory browsing is disabled unless deliberately desired.

Purpose: Directory browsing allows for listing of directories and directory contents that reside on the server. If not deliberately needed, this might be very interesting for the attacker, because: - it reveals information about internal directory and file structure on the server, - it can reveal sensitive information (database backups, .git folders, …​).

Solution: Secure default of the Tomcat server. The DefaultServlet configuration (Tomcats /conf/web.xml file), which can be used to list directory contents, has the parameter listings set to false. Let’s keep it this way.

S4-5

Access controls fail securely.

Purpose: The opposite of fail securely - fail open - means, that the attacker can gain something out of an access control failure, be it: - access to the system resources, - stack trace revealing technical system information, - other.

Solution: We expect the devonfw access control mechanisms as described in S4-1 together with S6.1 (system error handling) to allow applications to fail securely.

S4-6

The same access control rules implied by the presentation layer are enforced on the server side.

Purpose: Every client side control can be bypassed by the attacker. In the end only server side controls can prevent the attack.

Solution: This must be properly implemented and tested by the developer team.

S4-7

The application or framework generates strong random anti-CSRF tokens unique to the user as part of all high value transactions or accessing sensitive data, and that the application verifies the presence of this token with the proper value for the current user when processing these requests.

Purpose: Cross-site Request Forgery is an attack, that forces the end user to perform unwanted actions on his behalf (see OWASP).

Solution: CRFS tokens are generated using Spring HttpSessionCsrfTokenRepository. They are loaded from the HttpServletRequest and stored in the HttpSession. The Spring implementation generates strong random tokens → UUID.randomUUID().toString().

Spring configuration for Cross Site Request Forgery is stored in beans-security-filters.xml:

<bean id="HttpSessionCsrfTokenRepository" class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository"/>
<bean id="CsrfFilterWrapper" class="io.oasp.module.web.common.base.ToggleFilterWrapper">
   <property name="delegateFilter" ref="CsrfFilter"/>
   <property name="enabled" value="${oasp.filter.csrf}"/>
</bean>
<bean id="CsrfFilter" class="org.springframework.security.web.csrf.CsrfFilter">
   <constructor-arg>
       <ref bean="HttpSessionCsrfTokenRepository"/>
   </constructor-arg>
   <property name="accessDeniedHandler" ref="ApplicationAccessDeniedHandler"/>
</bean>

The ToggleFilterWrapper class is responsible for wrapping the CsrfFilter and allows it to be disabled e.g. for development tests.

public class ToggleFilterWrapper implements Filter {
/** Logger instance. */
private static final Logger LOG = LoggerFactory.getLogger(ToggleFilterWrapper.class);
/**
 * The delegated Filter.
 */
private Filter delegateFilter;
/**
 * Is set if this filter is enabled.
 */
private Boolean enabled = Boolean.FALSE;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@PostConstruct
public void initialize() {
  if (!this.enabled) {
    String message =
        "****** FILTER " + this.delegateFilter
            + " HAS BEEN DISABLED! THIS FEATURE SHOULD ONLY BE USED IN DEVELOPMENT MODE ******";
    LOG.warn(message);
    System.err.println(message);
  }
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
  if (this.enabled) {
    this.delegateFilter.doFilter(request, response, chain);
  } else {
    chain.doFilter(request, response);
  }
}
@Override
public void destroy() {
}
/**
 * @param delegateFilter the filter to delegate to
 */
public void setDelegateFilter(Filter delegateFilter) {
  this.delegateFilter = delegateFilter;
}
/**
 * @param enabled the enabled flag
 */
public void setEnabled(Boolean enabled) {
  this.enabled = enabled;
}
  /**
   * @return disabled
   */
  public Boolean isEnabled() {
    return this.enabled;
  }
}

Server side - implementation

Retrieving the token on the server can be implemented as follows:

/**
 * The security REST service provides access to the csrf token, the authenticated user's meta-data. Furthermore, it
 * provides functionality to check permissions and roles of the authenticated user.
 *
 */
@Named("SecurityRestService")
@Transactional
public class SecurityRestServiceImpl {
/** Logger instance. */
private static final Logger LOG = LoggerFactory.getLogger(SecurityRestServiceImpl.class);
/**
 * Use {@link CsrfTokenRepository} for CSRF protection.
 */
private CsrfTokenRepository csrfTokenRepository;
/**
 * Retrieves the CSRF token from the server session.
 *
 * @param request {@link HttpServletRequest} to retrieve the current session from
 * @param response {@link HttpServletResponse} to send additional information
 * @return the Spring Security {@link CsrfToken}
 */
@Produces(MediaType.APPLICATION_JSON)
@GET
@Path("/csrftoken/")
@PermitAll
public CsrfToken getCsrfToken(@Context HttpServletRequest request, @Context HttpServletResponse response) {
  CsrfToken token = this.csrfTokenRepository.loadToken(request);
  if (token == null) {
    LOG.warn("No CsrfToken could be found - instanciating a new Token");
    token = this.csrfTokenRepository.generateToken(request);
    this.csrfTokenRepository.saveToken(token, request, response);
  }
  return token;
}
/**
 * Gets the profile of the user being currently logged in.
 *
 * @param request provided by the RS-Context
 * @return the {@link UserData} taken from the Spring Security context
 */
@Produces(MediaType.APPLICATION_JSON)
@GET
@Path("/currentuser/")
@PermitAll
public UserDetailsClientTo getCurrentUser(@Context HttpServletRequest request) {
  if (request.getRemoteUser() == null) {
    throw new NoActiveUserException();
  }
  return UserData.get().toClientTo();
}
  /**
   * @param csrfTokenRepository the csrfTokenRepository to set
   */
  @Inject
  public void setCsrfTokenRepository(CsrfTokenRepository csrfTokenRepository) {
    this.csrfTokenRepository = csrfTokenRepository;
  }
}

Client side - implementation

The client adds the CSRF token to all requests in oasp-security-service.service.js:

enableCsrfProtection = function () {
   return getSecurityRestService().getCsrfToken()
       .then(function (response) {
          var csrfProtection = response.data;
          // from now on a CSRF token will be added to all HTTP requests
          $http.defaults.headers.common[csrfProtection.headerName] = csrfProtection.token;
          currentCsrfProtection.set(csrfProtection.headerName, csrfProtection.token);
          return csrfProtection;
        }, function () {
          return $q.reject('Requesting a CSRF token failed');
        });
  };

S4-8

The application correctly enforces context-sensitive authorisation so as to not allow unauthorised manipulation by means of parameter tampering.

Purpose: Context-sensitive authorisation can take multiple parameters into account like e.g. the location of the user making the request to the system (if the location is not the office network, deny access). This is all nice, as long as a simple parameter tampering cannot bypass this control.

Solution: The devonfw platform currently does not support context-sensitive authorisation.

Clone this wiki locally