How do I do session management in CICS?
From CICS Wiki
Introduction
Performing Session management in CICS means to keep track of the users that have signed onto CICS from a web client. This is also refered to as "Managing state across an HTTP request sequence" .
CICS provides two very useful programs called DFH$WBST and DFH$WBSR. These programs are designed to assist you to manage state across multiple HTTP interactions from a web client.
The web universe
But before we deal with these programs we first have to discover the laws that govern our web universe. Traditionally terminal users use to sign on to CICS and have a permanent session that was established with CICS. The user's terminal could be displayed by a CICS operator because CICS had a permanent session with the terminal using VTAM. This allowed CICS to anchor some control blocks in which was recorded which user was represented at that terminal. Although we had pseudo-conversational programs the physical network connection was still what we call stateful in that we were always connected with CICS.
But with the advent of the Web and TCP/IP connections we found ourselves with not just stateless programs but also stateless clients - namely the web browser. The rest of this article will show how we can know whether a request that arrives in CICS from a web browser is from a user that has already signed on or not. Knowing this allows us to run a transaction under the RACF authority of the signed in user.
Using the session management features
For this article we will focus on using DFH$WBST. These programs will store away state data using a token and on demand will retrieve the stored data if you can provide the token again. Almost like a safety deposit box at the bank.
The idea of managing the user session is to give the user or rather its web browser the key to the safety deposit box and then when they come back for subsequent requests, retrieve the session key and check the safety deposit box for certain information. Obviously the main danger here is that users might just send along any old key with the hope that you will open the system to them. This can easily be avoided using the concept of a wikipedia:Shared_secret.
The shared secret is normally the IP address of the user.
How does a shared secret work?
The idea of sharing a secret is that the combination of the token and the shared secret is impossible to guess.
Think of it like a userid and password. If a userid is eight characters and a password is eight characters - the sixteen characters together form the key that is necessary to enter a system. Think of the userid as the known part of the key and the password as the unknown part of the key or the shared secret part of the key.
In the case of web session management the program DFH$WBST will store the shared secret for us in a CICS storage area and the known part of the key - the session token will be returned to us. This token will be send back to the browser.
Where to store the known part of the secret
Ok so where does the web browser store the known part of the key? The answer is that it can be stored in more in than one place. Many websites store it using cookies. In this example we will store it in the URL. Remember it does not matter that we are showing this part of the key because it is the known part of the key.
We will append the key to the back of the URL as a token. It could look something like this:
http://server:port/Webmenu/index.html?session=0000001912711243
Sample code
We will use some sample code to illustrate how to use session management in CICS. The program is used as a signon program for CICS web users.
The first test that the program has to take is to see whether it is the browser session's first request to the CICS region. Normally this is tested by checking the so-called HTTP method that the browser send along.
Understanding the Method
The method is simply the way the browser invoked or retrieved the page from CICS. Normally if a page is requested for the first time - it is requested with a GET request. When the user clicks a button or enters on an already retrieved page - the method that the browser sends through is normally POST.
In our sample program if we identify a GET request we will present the signon screen. If we identify a POST request we will process the signon screen and generate a new session for the user.
| a-main section. | ... | if httpmethod(1:4) = 'POST' | perform c-process-page | if error-signal = BOOLFALSE | perform i-generate-session-key | perform l-construct-new-url | ... | perform j-redirect | end-if | end-if. | perform h-create-symbollist. | perform b-send-page. | exec cics return end-exec. | a-exit. | exit.
You don't have to understand everything you see in this code - just notice how the sign on program pivots around the main question - whether the HTTP method was GET or POST.
The Commarea for DFH$WBST
The rest of the program validates the userid and password and if found to be valid a new session is created. As mentioned the program DFH$WBST will store it away and returned the key to us. The program needs a specific commarea that can be discovered in the "IBM documentation for this program"
For our program we had a commarea like this
| 01 session-comm. | 03 filler pic x(4). | 03 sess-action pic x. | 03 sess-return pic x. | 88 session-call-succesful value x'00'. | 03 filler pic xx. | 03 sess-token pic 9(8) binary value 0. | 03 sess-user. | 05 sessuserid pic x(8). | 05 sesstcpipservice pic x(8). | 05 sessclientaddrnu pic 9(8) binary value 0. | 05 filler pic x(236).
The top of the commarea is the static or mandatory part. We will provide the action (which in this case is "C" because we want to create a new session token) and we will receive the return code and the token back into the next two fields.
The group field "sess-user" is the variable part of the commarea. Here we provide what we want to store in the safety deposit box. This is the content of our shared secret. In this case our shared secret consist of the userid that the user provided on the web screen, the TCPIPService through which the user signed on and the user's IP address which we can retrieve using a API call.
Linking to the utility program
We now link to the program using the following code:
| i-generate-session-key section.
| move 'i-generate-session-key' to error-section.
| move 'C' to sess-action.
| move userid to sessuserid.
| move length of session-comm to sesscommlen.
| exec cics link program('DFH$WBST')
| commarea(session-comm)
| length(sesscommlen)
| nohandle
| end-exec.
| if eibresp not = dfhresp(NORMAL)
| move LINK-PROGRAM to error-command
| perform z-api-error
| exec cics return end-exec
| end-if.
|
| if session-call-succesful
| compute sess-tokendisp = sess-token
| move sess-tokendisp to sess-tokenstr
| else
| display 'call was successful'
| compute error-signal = BOOLTRUE
| string
| 'DFH$WBST returned a return code of '
| sess-return
| delimited by size
| into msg
| end-if.
| i-exit.
If all this works and we get a good return code from DFH$WBST we now know that the information is stored away in the safety deposit box. Now we just need to give the key to the customer (web browser) and send out the screen.
Redirecting the browser session
For this we will generate a URL with a session key and redirect the user to it. Note that the token is a binary field so you have to perform the normal convertion actions to get the token to be in display format. Then we generate a new URL like this:
| l-construct-new-url section. | string 'http://' | host(1:hostlen) | ':' | sess-portno | '/Webmenu/index.html?' delimited by size | querystring delimited by space | into url | end-string. | string url delimited by space | 'session=' delimited by size | sess-tokenstr delimited by space | into url | end-string. | l-exit. | exit.
The last step is to redirect the browser to the new URL. This is done with the following code. The code in the main section chose to overwrite the Location HTTP header:
| move 'Location' to headerName | move 8 to nameLen | move url to headerValue | move 0 to valueLen
The following section sets the HTTP header values.
| j-redirect section. | exec cics web write httpheader(headerName) | namelength(nameLen) | value(headerValue) | valuelength(valueLen) | end-exec. | if eibresp not = dfhresp(NORMAL) | move WRITE-HEADER to error-command | perform z-api-error | exec cics return end-exec | end-if. | move REDIRECT to statuscode. | move REDIRECTTEXT to statustext. | move length of statustext to statustextlen.
The value of REDIRECT and REDIRECTTEXT is:
| 01 REDIRECT pic s9(4) binary value 301. | 01 REDIRECTTEXT pic x(80) value 'Redirecting.'.
this will be used in the next step when we send the web page:
| exec cics web send | doctoken(token) | clntcodepage(CLIENTCODEPAGE) | statuscode(statuscode) | statustext(statustext) | length(statustextlen) | end-exec
So by setting the header and overriding the HTTP status with a value of 301 - we redirect the session to the new constructed URL that now has the session key embedded in it.
Finally testing the shared secret
The new program that is invoked by the new URL can use the provided session key and DFH$WBST to retrieve the shared secret which is the user's IP address. If this address is the same as the ip address that invoked the program - the user is assumed to be the userid which is also stored as part of the session information.
How to use the stored userid
Programs can choose to implement their own security by using the stored userid to test whether the user has access to certain resources. This makes for a lot of extra security related code. The better option is to use this with a security analyzer which will be shown next.
Using the session in an analyzer
For our analyzer we used the sample CICS program DFHWBAOX
In the analyzer program we had to extract the query string which is where the session key is stored:
| exec cics web extract | querystring(querystring) | querystrlen(querystrlen) | nohandle | end-exec. | if eibresp = dfhresp(NORMAL) and querystrlen > 0 | perform check-session | end-if.
Next we retrieve the session information and then compare the stored IP address with the web client's IP address. If this is the same we request CICS to run the transaction under the authority of the stored userid:
| check-session section.
| move spaces to WBRA-USERID.
| move low-values to session-comm.
| move spaces to sess-tokenstr.
| if querystring(1:8) = 'session='
| move querystring(9:) to sess-tokenstr
| end-if
| if sess-tokenstr > spaces and sess-tokenstr is numeric
| move sess-tokenstr to sess-tokendisp
| compute sess-token = sess-tokendisp
| compute sess-tokendisp2 = sess-token
| perform retrieve-session
| if eibresp = dfhresp(NORMAL) and session-call-succesful
| if sessclientaddrnu = WBRA-CLIENT-IP-ADDRESS
| move sessuserid to WBRA-USERID
| else
| move URP-EXCEPTION to wbra-response
| move 0 to wbra-reason
| EXEC CICS RETURN END-EXEC
| end-if
| end-if
| end-if.
| check-exit.
| exit.
|
| retrieve-session section.
| move 'R' to sess-action.
| move length of session-comm to sesscommlen.
| exec cics link program('DFH$WBST')
| commarea(session-comm)
| length(sesscommlen)
| nohandle
| end-exec.
| retrieve-exit.
| exit.
It is important to realize what it means if the IP addresses are not the same. Except for any weird network condition this might mean that the user attempted to hack into the system. It would be appropriate at this stage to notify administrators and to log all the information you have about the user.
However if the IP address is correct we have now set the userid under which CICS will run the transaction and therefore all resources we access in the application program will automatically be checked for access by CICS.
Lastly remember to set the analyzer in the appropriate TCPIPService to the analyzer program you created. The program source for the login and analyzer is available here:
Admin 10:26, 8 January 2008 (CST)
