Prevent privileged users from logging in externally

The wc-server.xml file includes a customizable web server header. You can edit this header to control whether privileged users can log in to the store from external networks. This framework reduces the possibility of an external attack if an account with administrative level access, such as a customer service representative or site administrator, is compromised.

The following security configuration wc-server.xml defines the custom request header EXTERNAL_WEBSERVER:
<RestrictAdminLogonToStore display="false" enabled="false" webServerHeader="EXTERNAL_WEBSERVER">
    <RolesAllowedWithoutHeader>
        <Role name="Registered Customer"/>
        <Role name="Buyer Administrator"/>
        <Role name="Buyer Approver"/>
    </RolesAllowedWithoutHeader>
</RestrictAdminLogonToStore>

By default, the feature is disabled, but you can enable it (enabled="true"). After this framework is enabled, you can define a custom header (EXTERNAL_WEBSERVER), and list the specific user roles permitted to accessing the site from external networks. The roles are defined under the <RolesAllowedWithoutHeader> header. With those properties defined, you can specify the identified header on web servers that are serving external traffic; web servers that are serving internal traffic would remove this header. That way, if the custom header is present in a web request (external traffic), only users with roles defined under <RolesAllowedWithoutHeader> (or unauthenticated users) are permitted to access the site (a CSR, Site Administrator, or other sell-side role would be blocked). If the header is absent from the request (internal traffic), a user with any role can access the site.

If you are using IBM HTTP Server, you can add the following to the httpd.confto add the EXTERNAL_WEBSERVER header value of <code>true</code> to the HttpServletRequest object:
RequestHeader set EXTERNAL_WEBSERVER true
In the example, only users with the following roles (or no roles at all) can access the site externally when the header is present in the web request:
  • Registered Customer
  • Buyer Administrator
  • Buyer Approver

Extension point for customization

This framework contains a task command CheckLogonAllowedCmd that can be used as an extension point for customization. The default behavior uses the CheckLogonAllowedCmdImpl, which implements the isAllowed() public method. This method returns true if the logon request is received from the internal network, otherwise it checks the user's roles against the roles defined under the <RolesAllowedWithoutHeader> section in the wc-server.xml file.

With the customer header EXTERNAL_WEBSERVER set in REST or the web request header, CheckLogonAllowedCmdImpl method getExternalWebServerFlag() detects the setting and return true to indicate that the request is from an external network.

In a scenario where isAllowed() returns false, the following new error code is thrown:
  • ERR_LOGON_NOT_ALLOWED_FROM_EXTERNAL_WEBSITE = "2340";
The following screen capture illustrates the error code on the store front:

By using this framework, you can take two possible approaches to control internal and external network access to your site. Keep in mind that you are not limited to these approaches.
  1. Control internal and external access through separate web servers. Set the specified custom header in each request from the external web server to prevent privileged users from logging in to the site externally.
  2. Use a custom filter, outlined below, to validate request information to differentiate internal from external network requests.

Sample code for customization

The following sample code can be used for customization to enable the external logon check on your store. This sample uses the creation of a new filter, which determines whether the incoming request is received from an external website. In this case, the IP address is for such determination. The following filter can be added to your store web.xml file:
public class MyRuntimeServletFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest)req;
        // limit filter to only logon request
        if (httpServletRequest.getPathInfo()!= null && httpServletRequest.getPathInfo().contains("AjaxLogon")) {
            String customHeaderName = WcsApp.configProperties.getValue(ECSecurityConstants.WCSAPP_CUSTOM_HEADER_URL);
            // check RestrictAdminLogonToServer is enabled and custom header is set in wc-server.xml
            if (SecurityHelper.isRestrictAdminLogonToStore() && customHeaderName != null) {
                String remoteIP = null;
                if (httpServletRequest.getHeader("x-forwarded-for") == null) {  
                    remoteIP = httpServletRequest.getRemoteAddr();  
                } else {
                    remoteIP = httpServletRequest.getHeader("x-forwarded-for");  
                } 
                if (remoteIP != null && remoteIP.startsWith("9.186")) {
                   // set external network request flag
                    SecurityHelper.setExternalWebServerFlag(customHeaderName, Boolean.TRUE);
                }
            }
        }
        chain.doFilter(req, res); 
    }
Note: SecurityHelper.setExternalWebServerFlag() is a utility in the SecurityHelper class that helps to set the custom flag in the transaction cache. Be sure to add the custom filter as the final filter in the web.xml file because the transaction cache might be cleared by transition operation in other filters.