The ISAM Reverse Proxy has a wealth of settings that can be configured. This means that almost every deployment situation can be catered for, but also means that its easy to overlook some settings that may give you grief. In this post, I’ve decided to capture some of the more important ones to help you tune your ISAM deployment.
Some of these settings are configurable in the UI, some aren’t. For consistency, I’m going to detail the settings as they are described in the webseald.conf file where relevant – or the “Configuration File” as its now known in the UI.
Security Related Settings
Should your server allow HTTP access?
# Allow (unsecure) TCP HTTP access http = yes # Port to use for unsecure HTTP requests http-port = 80
These days, HTTP access is never recommended, it may be necessary to leave HTTP enabled though, simply for the purposes of redirecting the users to HTTPS. You can do this a number of ways, including rules in your load balancers, javascript in your forbidden page, and using an ISAM POP to enforce it.
Similarly, when you enable authentication mechanisms, be sure to only enable them on the HTTPS channel.
# Enable authentication using the forms authentication mechanism # One of <http, https, both, none> forms-auth = https
Here is the EAI mechanism set to HTTPS only.
# Enable EAI authentication. # # One of <http, https, both, none> eai-auth = https
Similarly, your LDAP connection should be connecting over SSL. This is typically configured by the runtime configuration, and it’s best to configure it there. But this config item can be an indicator of a bad config.
[ldap] ssl-enabled = no
If you’re using the default Forms authentication, you might want to prevent people from logging straight in without seeing the login form first:
# The following configuration entry is used to control whether unsolicited # authentication requests are allowed. If set to 'no' a login will only # be allowed if WebSEAL first returns a login form to the client. allow-unsolicited-logins = no
To ensure connections are coming in from the right source, you can validate that header values match expected values. Once example of this is the host header:
[validate-headers] # This stanza is used to list those headers which should be validated # on each request. If multiple headers of the same name are configured, # the corresponding header in the request must match one of the # configured values. The format of each configuration entry is: # <hdr> = <value> # # For example to ensure all requests are from www.ibm.com set: host = www.ibm.com
Alternatively, if you are only expecting access from a specific application, like a mobile app – you might choose to specify a specific User agent. Or a header that only your application sends.
Response headers can be set by ISAM, this is useful for HSTS Headers and content policy headers.
[rsp-header-names] # This stanza is used to define static HTTP headers which will be added # to every HTTP response from the WebSEAL server. This will provide the # administrator with the ability to insert some standard security headers # into the response, such as strict-transport-security, # content-security-policy and x-frame-options. # # Please note that the headers which are defined in this stanza will replace # any matching headers which might have been added to the response by a # junctioned application. # # If multiple headers of the same name are specified in this stanza all # but the last of the matching entries will be ignored. # # The format of each entry in this stanza is: # <header-name> = <header-value> # # Some pretty standard headers are these: # Described in more detail here: # https://philipnye.com/posts/isam-for-web-sending-security-http-headers/ strict-transport-security = max-age=31536000; includeSubDomains X-Frame-Options = SAMEORIGIN Content-Security-Policy = frame-ancestors 'self'
Before this feature existed in 8.0.1.3, I mentioned this and their purposes in posts here: Sending Security Headers and Clickjacking prevention.
Authentication Levels, be sure to capture the available mechanisms you are using. If you fail to set a default level for your mechanism, sometimes you might end up with an authentication level of 0.
[authentication-levels] #---------------------- # STEP UP #---------------------- # authentication levels # # Syntax: # level = <method-name> # # Valid method names are: # unauthenticated # password # token-card # ssl # ext-auth-interface # ltpa # kerberosv5 # oauth # level = unauthenticated level = password level = ext-auth-interface
Some people like the idea of Session management without cookies, it sounds great in theory, but when ISAM is not performing the SSL termination, it can really make things go bad fast.
(Think 1 load balancer with a single SSL session, with many users coming in.)
So, unless you know what you’re doing, this setting should NOT be enabled.
#---------------------- # SSL CLIENT SESSIONS #---------------------- # Use the SSL ID to maintain a user's HTTPS login session. ssl-id-sessions = no
Similar to the Header validation earlier, you can also configure your server to only accept referrers you trust to your management pages.
# It is also possible to enforce validation of the HTTP Request referer header # for all account management pages to protect against CSRF-style attacks. If # this functionality is enabled, a request for an account management page will # check to see if the referer header is present in the HTTP Request header and # then validate the hostname portion of that referer against a list of "allowed" # referer filters. If there are no allowed-referers entries here, then this # validation is not performed. The values for this allowed-referers keys # provide WebSEAL with a list of referer hostnames that should be considered # "valid". # # The default value for this entry, although originally commented out as to not # enable this functionality by default, is "allowed-referers = %HOST%". This # is a special entry in that it indicates to WebSEAL that a referer is "valid" # if the hostname portion of the referer HTTP Request header entry matches the # host HTTP Request header. # # There can be 0 or more entries set for this key. All entries are used when # validating the referer. Entries can contain wildcard characters: # * - match 0 or more characters # ? - match any single character # - Literal match of the following character # So for example, an entry "allowed-referers = ac*me" will match any referer # hostname that begins with the characters "ac", followed by 0 or more # characters, and ends with the characters "me". # #allowed-referers = %HOST%
It’s a pretty common requirement to hide the versions of the servers you are proxying, this can be done here:
#---------------------- # SUPPRESSING SERVER IDENTITY #---------------------- # WebSEAL writes a Server header with the value "WebSEAL/version.number" # with most responses (except those from a junctioned server). # Including this header can be suppressed by setting this to "yes". suppress-server-identity = yes # For responses that were from a junctioned server, WebSEAL writes the Server # header sent in the response from the backend. If the backend response did not # include a Server header, then WebSEAL will not write any Server header to the # client. # Writing this header can be suppressed by setting this to "yes". suppress-backend-server-identity = yes
Although I’ve also seen other server headers returned by things like ASP, and it might require some transformation rules where you cannot easily turn them off on the backed server. Convieniently I’ve included an example here of the “X-Powered-By” header being removed.
https://philipnye.com/posts/hide-a-header-from-a-junctioned-server-with-webseal/
Are you using the WAF? (If not, why not… ) The ISAM WAF is very low maintenance, and easily configured. There is the option below, and also a UI mechanism for enabling it by URL.
(You need to use both.)
[PAM] # # Whether PAM analysis is enabled. # pam-enabled = false
The request log is one of the best ways to see whats happening on your server, by default it is a fairly small subset of details on a given request.
192.168.42.1 - testuser1 10/Nov/2016:22:07:19 +1000 "POST /pkmslogin.form HTTP/1.1" 200 8015
This can be greatly expanded using the settings in the config file:
# The request-log-format to be written to the request log. # The following directives can be used to customize the log format. # # %a: Client IP Address # %A: Local IP Address # %b: Bytes in the response excluding HTTP headers in CLF format: '-' instead # of 0 when no bytes are returned. # %B: Bytes in the response excluding HTTP headers # %{attribute}C: # Attribute from the TAM credential named 'Attribute' # %{cookie}e: # Contents of the Cookie 'cookie' in the request # %{cookie}E: # Contents of the Cookie 'cookie' in the response # %d: Transaction identifier, or session sequence number. # %F: Time taken to serve the request in microseconds # %h: Client host # %H: Request protocol # %{header}i: # Contents of the Header 'header' in the request # %j: The name of the junction servicing the request # %l: Client logname (RFC 1314) (default -) # %m: Request method (i.e. GET, POST, HEAD) # %{header}o: # Contents of the Header 'header' in the response # %p: Port over which the request was received # %q: The decoded query string (prepended with '?' or empty) # %Q: The raw query string (prepended with '?' or empty). # %r: First line of the request with decoded URL # %R: First line of the request with decoded URL including HTTP://HOSTNAME # %s: Response status # %t: Time in Common Log Format format # %{format}t: # The time in the given format # %T Time taken to serve the request in seconds, or part thereof # %u: Remote user # %U: The URL requested # %v: Canonical ServerName of the server servicing the request # %z: The decoded path string # %Z: The raw path string request-log-format = %h %l %u %t "%r" %s %b
Consider an example where you have mobile clients coming in, and you want the user agent header, or just the time taken to serve the request, these are all available. Just keep in mind, that if you’re collecting more data, you will want to make sure you have the log rollover working properly, lest you fill up the ISAM appliance. Here is a technote on the rollover log config for the request log.
http://www-01.ibm.com/support/docview.wss?uid=swg21969684
Performance and Throughput
ISAM uses worker threads to handle traffic, the number of worker threads is configured using this configuration parameter. This parameter can can also be managed on a per user and per junction basis.
Additional worker threads consume memory (roughly 512KB each) and server resources and instances can be monitored for worker thread starvation using the ISAM statistics, See “Detecting Worker Thread Starvation” in the ISAM Knowledge Center.
#---------------------- # THREADS AND CONNECTIONS #---------------------- # Number of WebSEAL worker threads # The number of configured worker threads specifies the number of # concurrent incoming requests that can be serviced by this server # instance. Choosing the optimal number depends on the quantity # and type of traffic on your network. Modifying this value should # be done carefully to ensure optimal performance. Please consult # the WebSEAL Administration Guide for further information. worker-threads = 500
LDAP authentications made using compare are generally considered to be more performant, this is the default value when using IBM Security Directory Server (or TDS), just adding this here for completeness.
[ldap] # Enables or disables authentication using password comparison. # When disabled, authentication using LDAP bind is performed. # For those LDAP servers that allow it, a compare operation might # perform faster than a bind operation. auth-using-compare = yes
This setting can reduce the impact one user can have on an environment, to give you an example, Firefox won’t actually attempt to make more than 6 concurrent connections to a server, such that enforcing a limit of 10 is not unrealistic.
# The maximum number of concurrent threads which can be consumed # by a single user session. Once the thread limit for the user session has # been reached the request will not be processed by WebSEAL and an error # will be returned to the client. # # If no value is specified for this configuration item there will be no # limit to the number of concurrent threads that a user session can # consume. # # concurrent-session-threads-hard-limit = 10
Between the browser and ISAM, the default behaviour is to not compress any of the response data. This can be modified using the parameters below. I like to compress most text based responses, including CSS, JS, JSON and HTML. And in many cases, you might find value in compressing other mime types depending on what traffic your ISAM server is proxying.
If you set the threshold to compression too low, it *could* have adverse affects on performance due to wasted effort to compress small or already compressed data types, but in general, network effects are greater than the effect on CPU time, so I’m usually pretty aggressive here.
[compress-mime-types] #---------------------- # HTTP COMPRESSION MIME-TYPE CONFIGURATION #---------------------- # This stanza allows HTTP compression to be enabled or disabled based # on the mime-type of the response and the size of the returned document. # Order is important. The first entry that matches a returned document # will be used for that document. # # Syntax: # <MIME-type> = <Min-Doc-Size>[:<Compress-Level>] image/* = -1 text/html = 1000 */* = 1000
ISAM will disable compression between ISAM and the backend server by supressing this header. It does this when you need to perform filtering on the responses for URL rewriting or snippet injection. If you are just using transparent path junctions or virtual host junctions and aren’t injecting snippets, you may want to turn this off to reduce the bandwidth required between ISAM and the backend server.
[filter-request-headers] # # HTTP headers to filter from the client request before sending to the # back-end web server. Note that this list is in addition to headers # that WebSEAL will always filter, eg iv-user, iv-groups. # # Format is: # header = <header-name> # # The header name is case insensitive. # # The addition of "accept-encoding" to this list will prevent junctioned # servers from returning compressed data to WebSEAL. WebSEAL cannot # filter compressed data. header = accept-encoding
Consider your server throughput, is the default 4096 max active sessions sufficient at any one time?
[session] #---------------------- # SESSION CACHE SETTINGS #---------------------- # The maximum number of concurrent entries in the credential cache # This corresponds to the number of concurrent logins. The value # WebSEAL actually uses might be slightly more than what is specified here. # Refer to the WebSEAL Administration Guide for details. To customise this # value for authenticated or unauthenticated sessions simply add an # additional configuration entry, prefixed by 'auth' or 'unauth', e.g. # unauth-max-entries = 1024 max-entries = 4096
OAuth Specific Configuration Guidelines
First up – I strongly recommend you have a dedicated instance of the ISAM reverse Proxy for OAuth Auth related access. This means you can tweak the security and configuration settings to meet the stricter requirements of an API request vs web requests.
ALWAYS use OAuth Auth on HTTPS only! (Yes, I’m hammering this HTTPS thing home, I know).
[oauth] # Enable authentication using Open Authorization (OAuth) mechanism. # One of <http, https, both, none> # # The OAuth authentication mechanism should be considered only as part of a # Mobile scenario, where a session can be established based on the Bearer # token in the Authorization Header. oauth-auth = https
Don’t use OAuth Auth, and the OAuth EAS at the same time. This was the default after running isamcfg up until the most recent release. So you may have been doing this without realising.
[oauth-eas] # Should the EAS be enabled? eas-enabled = false
For caching, of the validation at a ‘session level’, be sure to use OAuth Sessions. I’ve talked about this in a bit more detail elsewhere on this site. https://philipnye.com/posts/isam-for-web-and-mobile-oauth-authentication-and-sessions/
[session-http-headers] #---------------------- # HTTP HEADER SESSION KEYS #---------------------- # # List any HTTP headers which will contain a session key on a per-transport # basis. The same header can be listed for both transports if desired. # # Only the first matching header found in a request will be used. # # If ssl-id-sessions = yes, then this stanza will be ignored. # The exception to this is if MPA support is enabled. # # WebSEAL will first look for a session cookie before continuing to look # for HTTP headers from this list. # # The use of http headers as session keys is affected by the setting of # require-mpa, see the comments above the require-mpa entry for more # information. # # This list should contain no more than 20 entries per transport. # Do not include the colon (:) # # Format is one of: # <header> = http # <header> = https Authorization = https
Just be sure to disable the Multiplexing Proxy Agent requirement (MPA) setting.
# The use of an HTTP header as a session identifier or as an authentication # token carries a measure of risk that the header can be spoofed or stolen. # It is strongly recommended that headers only be accepted when proxied # through an authenticated channel. A 'yes' setting means that HTTP headers # will not be valid session keys or authentication tokens unless received via # an MPA. Please see the WebSEAL Administration Guide for more details # regarding MPAs. require-mpa = no
OAuth Sessions didn’t used to play well with the Distributed Session Cache. Be sure not to enable the DSC on any instance using OAuth-Auth. You’ll very quickly overflow your session cache otherwise.
There is a new setting in v9.0.7, which allows this to work more appropriately with DSC enabled now, see
Reverse Proxy Sessions
The reverse proxy can now store non-cookie based sessions in a local session cache when the distributed session cache is enabled. See dsess-support-local-sessions.
#---------------------- # DISTRIBUTED SESSION MANAGEMENT #---------------------- # These entries together with the "dsess" stanza control how WebSEAL uses the # DSC to store and manage sessions. # Enable/disable use of the DSC. If this is set to yes the "dsess" stanza # must have information about how to communicate with the DSC. dsess-enabled = no
Are you hashing your OAuth Tokens stored in the Database? Be sure to configure this setting in the consoles “Advanced Configuration” Section:
oauth20.hashedTokenStorageEnabled = true

The algorithm for hashing is defined under “runtime.hashAlgorithm” and defaults to SHA-256.

Fun fact, you can turn this on even if you’ve been running for a while with it off, it will progressively hash all new tokens as they are generated.
Other Handy Config options
[ldap]
# This stanza entry specifies whether to store the last login
# information of a user in LDAP.
enable-last-login = yes
Great for statistical analysis of accounts that are dormant.
This can also be enabled in the Username and Password mechanism for Advanced Access Control here, setting the LDAP settings to make the Login Failures Persistent.
(Note: this only applies to ISAM users – not basic/lite users, since these are attributes that are stored in the secAuthority suffix.)

Additionally, this also applies to the username and password mechanism for OAuth, when using the default ROPC flow.
Are you using an EAI to login or a custom hosted Login page, consider using local response redirect enabled ONLY for login operations. I’ve got a full article on this feature here: Login Redirect. Please don’t use the legacy method of javascript or a Meta-Refresh in the login.html page on ISAM unless you have a good reason and want to apply some additional logic in javascript or something.
Requiring a user to be authenticated to access the logout page can be quite counter intuitive. I like to change this to yes.
#----------------------------- # ALLOW UNAUTHENTICATED LOGOUT #----------------------------- # Set this parameter to 'yes' to allow unauthenticated users to be able # to request the pkmslogout resource. If this parameter is set to 'no' # an unauthenticated user will be requested to authenticate before the # pkmslogout resource is returned. allow-unauthenticated-logout = no
ISAM can kill sessions to backend junctions with this configuration parameter.
Note: you will need to use the ISAM cookie jar (Managed cookies) for this to work properly.
#----------------------------- # BACK-END SERVER SINGLE SIGN-OFF #----------------------------- # When a user's session is terminated in WebSEAL, any sessions that may exist # on back-end application servers are not destroyed. When this item is # configured, WebSEAL will send a request to the configured URI's including # any configured headers and cookies for the junction point on which it resides. # The backend application can use this information to terminate any sessions # for that user. # # Multiple URI's can be specified by including multiple single-signoff-uri # configuration entries. # # The configured URI must reside on a standard junction. For example: # single-signoff-uri = /app/logout.asp #
Template Pages
Make your template pages align with your sites look and feel!
There are two main sets of template pages on the ISAM appliance, firstly, the WebSEAL – reverse proxy template pages, these are stored in the “Management Root” on a per proxy basis. There are the management pages – login page, etc, and then there is also just the index.html page hosted on ISAM, if you’re using a whole bunch of junctions you might not consider what happens when someone access /index.html and they get the ever so exciting ISAM version image iv30.gif (guess how long that image filename has been around.)

And the AAC/Federation Template pages that are hosted here, being sure to remove any mention of these macros: @EXCEPTION_STACK@ from a production deployment.

This list is a work in progress, put it together after a number of recent discussions where failure to configure these settings seemed to catch a few people off. If you have any other recommendations, get in contact!