Case Study 1: Implementing Security for a Banking Application

Background

Heritage Bank & Trust (HBT) is a federally chartered commercial bank with $24 billion in assets, 2.1 million customer accounts, and a mainframe-based core banking platform running on z/OS. Following a regulatory examination by the Office of the Comptroller of the Currency (OCC), HBT received a Matter Requiring Attention (MRA) citing insufficient access controls in the core banking application. The specific findings were:

  1. Overly broad dataset access: Production dataset profiles used generic wildcards that granted read access to all mainframe users, including contractors and temporary employees who had no business need for customer financial data.
  2. No transaction-level security in CICS: Any user who could sign on to CICS could execute any transaction, including administrative transactions that override daily limits and fee calculations.
  3. Excessive DB2 privileges: Application plans were bound with the authority of a single service account that held SYSADM privileges, violating the principle of least privilege.
  4. No secure coding controls: The COBOL application had no input validation, no authorization checks at the program level, and no protection against common attack patterns.

The OCC gave HBT 90 days to submit a remediation plan and 180 days to implement it. This case study documents the security architecture designed and implemented by HBT's security team, covering RACF dataset protection, CICS transaction security, DB2 authorization, and secure COBOL coding practices.


Phase 1: RACF Dataset Protection

RACF (Resource Access Control Facility) is the primary security manager on z/OS. The team's first task was to define RACF profiles that restrict dataset access to authorized users and groups.

Group Structure

The team defined RACF groups that mirror the organizational structure and application roles:

HBTADMIN   - System administrators (4 people)
HBTDBA    - Database administrators (3 people)
HBTBATCH  - Batch processing service accounts (2 accounts)
HBTCICS   - CICS region service accounts (4 accounts)
HBTDEV    - Application developers (12 people)
HBTOPS    - Operations staff (8 people)
HBTAUDIT  - Internal audit (3 people)
HBTTELLER - Branch teller users (850 people)
HBTMGR    - Branch managers (320 people)
HBTWIRE   - Wire transfer operators (15 people)

Dataset Profile Definitions

RACF profiles control access to datasets through discrete and generic profiles. Generic profiles use wildcard characters to cover groups of datasets with similar access requirements:

//*================================================================*
//* RACF COMMANDS FOR DATASET PROTECTION
//* EXECUTED VIA TSO BATCH OR RACF ADMIN PANELS
//*================================================================*
//*
//* STEP 1: DEFINE HIGH-LEVEL QUALIFIER PROTECTION
//*         DENY ALL ACCESS BY DEFAULT
//*
//RACFDEF  EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  ADDSD 'HBT.**' UACC(NONE) AUDIT(ALL(READ))
  SETROPTS GENERIC(DATASET) REFRESH
/*
//*
//* STEP 2: PRODUCTION MASTER FILE PROTECTION
//*         ONLY CICS AND BATCH SERVICE ACCOUNTS CAN UPDATE
//*
//RACFMAST EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  ADDSD 'HBT.CORE.PROD.MAST.**' UACC(NONE) -
        AUDIT(ALL(UPDATE))
  PERMIT 'HBT.CORE.PROD.MAST.**' -
         ID(HBTCICS) ACCESS(UPDATE)
  PERMIT 'HBT.CORE.PROD.MAST.**' -
         ID(HBTBATCH) ACCESS(UPDATE)
  PERMIT 'HBT.CORE.PROD.MAST.**' -
         ID(HBTAUDIT) ACCESS(READ)
  PERMIT 'HBT.CORE.PROD.MAST.**' -
         ID(HBTDBA) ACCESS(READ)
/*
//*
//* STEP 3: TRANSACTION FILE PROTECTION
//*         BATCH CAN WRITE, AUDIT CAN READ
//*
//RACFTRAN EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  ADDSD 'HBT.CORE.PROD.TRAN.**' UACC(NONE) -
        AUDIT(ALL(UPDATE))
  PERMIT 'HBT.CORE.PROD.TRAN.**' -
         ID(HBTBATCH) ACCESS(UPDATE)
  PERMIT 'HBT.CORE.PROD.TRAN.**' -
         ID(HBTCICS) ACCESS(UPDATE)
  PERMIT 'HBT.CORE.PROD.TRAN.**' -
         ID(HBTAUDIT) ACCESS(READ)
/*
//*
//* STEP 4: LOAD LIBRARY PROTECTION
//*         DEVELOPERS CANNOT MODIFY PRODUCTION LOAD MODULES
//*
//RACFLOAD EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  ADDSD 'HBT.CORE.PROD.LOAD.**' UACC(NONE) -
        AUDIT(SUCCESS(UPDATE) FAILURES(READ))
  PERMIT 'HBT.CORE.PROD.LOAD.**' -
         ID(HBTCICS) ACCESS(READ)
  PERMIT 'HBT.CORE.PROD.LOAD.**' -
         ID(HBTBATCH) ACCESS(READ)
  PERMIT 'HBT.CORE.PROD.LOAD.**' -
         ID(HBTADMIN) ACCESS(UPDATE)
/*
//*
//* STEP 5: DEVELOPMENT ENVIRONMENT - BROADER ACCESS
//*
//RACFDEV  EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  ADDSD 'HBT.CORE.DEV.**' UACC(NONE)
  PERMIT 'HBT.CORE.DEV.**' -
         ID(HBTDEV) ACCESS(ALTER)
  PERMIT 'HBT.CORE.DEV.**' -
         ID(HBTDBA) ACCESS(ALTER)
/*
//*
//* STEP 6: AUDIT TRAIL PROTECTION
//*         NO ONE CAN MODIFY AUDIT LOGS (EXCEPT SYSTEM)
//*
//RACFAUDT EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
  ADDSD 'HBT.CORE.PROD.LOG.AUDIT.**' UACC(NONE) -
        AUDIT(ALL(READ))
  PERMIT 'HBT.CORE.PROD.LOG.AUDIT.**' -
         ID(HBTBATCH) ACCESS(UPDATE)
  PERMIT 'HBT.CORE.PROD.LOG.AUDIT.**' -
         ID(HBTCICS) ACCESS(UPDATE)
  PERMIT 'HBT.CORE.PROD.LOG.AUDIT.**' -
         ID(HBTAUDIT) ACCESS(READ)
/*

Key Design Principles

UACC(NONE) everywhere: The Universal Access authority is set to NONE for all production profiles. Access is granted only through explicit PERMIT commands. This implements the "deny by default" principle required by the OCC.

AUDIT on sensitive profiles: The AUDIT keyword enables SMF (System Management Facilities) recording of access attempts. AUDIT(ALL(READ)) records every read access to master files -- not just unauthorized attempts, but all access, providing a complete access log for regulatory examination.

Separation of production and development: Developers have ALTER access to development datasets but zero access to production datasets. Promoting code from development to production requires a controlled deployment process through the HBTADMIN group.


Phase 2: CICS Transaction Security

CICS transaction security operates at two levels: transaction-level security (which users can execute which transactions) and resource-level security (which resources a transaction can access).

Transaction-Level Security with RACF TCICSTRN Class

//*================================================================*
//* CICS TRANSACTION SECURITY PROFILES
//* CLASS: TCICSTRN (TRANSACTION CLASS FOR CICS)
//*================================================================*
//*
//RACFCICS EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
* ACTIVATE THE TRANSACTION CLASS
  SETROPTS CLASSACT(TCICSTRN)
  SETROPTS RACLIST(TCICSTRN)
*
* DEFINE TRANSACTION PROFILES
* PATTERN: CICSREGION.TRANSID
*
* INQUIRY TRANSACTIONS - TELLERS AND MANAGERS
  RDEFINE TCICSTRN HBTCICS1.AINQ -
          UACC(NONE) AUDIT(FAILURES(READ))
  PERMIT HBTCICS1.AINQ CLASS(TCICSTRN) -
         ID(HBTTELLER) ACCESS(READ)
  PERMIT HBTCICS1.AINQ CLASS(TCICSTRN) -
         ID(HBTMGR) ACCESS(READ)
*
* POSTING TRANSACTIONS - TELLERS AND MANAGERS
  RDEFINE TCICSTRN HBTCICS1.APOS -
          UACC(NONE) AUDIT(ALL(READ))
  PERMIT HBTCICS1.APOS CLASS(TCICSTRN) -
         ID(HBTTELLER) ACCESS(READ)
  PERMIT HBTCICS1.APOS CLASS(TCICSTRN) -
         ID(HBTMGR) ACCESS(READ)
*
* OVERRIDE TRANSACTIONS - MANAGERS ONLY
  RDEFINE TCICSTRN HBTCICS1.AOVR -
          UACC(NONE) AUDIT(ALL(READ))
  PERMIT HBTCICS1.AOVR CLASS(TCICSTRN) -
         ID(HBTMGR) ACCESS(READ)
*
* WIRE TRANSFER TRANSACTIONS - WIRE OPERATORS ONLY
  RDEFINE TCICSTRN HBTCICS1.AWIR -
          UACC(NONE) AUDIT(ALL(READ))
  PERMIT HBTCICS1.AWIR CLASS(TCICSTRN) -
         ID(HBTWIRE) ACCESS(READ)
*
* ADMIN TRANSACTIONS - SYSTEM ADMINISTRATORS ONLY
  RDEFINE TCICSTRN HBTCICS1.AADM -
          UACC(NONE) AUDIT(ALL(READ))
  PERMIT HBTCICS1.AADM CLASS(TCICSTRN) -
         ID(HBTADMIN) ACCESS(READ)
*
  SETROPTS RACLIST(TCICSTRN) REFRESH
/*

Resource-Level Security

Beyond transaction access, the team implemented resource-level security to control which CICS resources (files, queues, programs) each transaction can access:

//*================================================================*
//* CICS RESOURCE-LEVEL SECURITY
//* CLASS: FCICSFCT (FILE CONTROL TABLE)
//*================================================================*
//*
//RACFRES  EXEC PGM=IKJEFT01
//SYSTSPRT DD SYSOUT=*
//SYSTSIN  DD *
* ACTIVATE FILE RESOURCE CLASS
  SETROPTS CLASSACT(FCICSFCT)
  SETROPTS RACLIST(FCICSFCT)
*
* ACCOUNT MASTER FILE - READ FOR INQUIRY, UPDATE FOR POSTING
  RDEFINE FCICSFCT HBTCICS1.ACCTMAST -
          UACC(NONE) AUDIT(FAILURES(READ))
  PERMIT HBTCICS1.ACCTMAST CLASS(FCICSFCT) -
         ID(HBTTELLER) ACCESS(READ)
  PERMIT HBTCICS1.ACCTMAST CLASS(FCICSFCT) -
         ID(HBTMGR) ACCESS(UPDATE)
*
* AUDIT LOG FILE - WRITE ONLY (NO READ FROM CICS)
  RDEFINE FCICSFCT HBTCICS1.AUDITLOG -
          UACC(NONE) AUDIT(ALL(READ))
  PERMIT HBTCICS1.AUDITLOG CLASS(FCICSFCT) -
         ID(HBTCICS) ACCESS(UPDATE)
*
  SETROPTS RACLIST(FCICSFCT) REFRESH
/*

Phase 3: DB2 Authorization

The legacy system used a single service account with SYSADM authority for all DB2 access. The remediation plan implements granular authorization using GRANT and REVOKE.

-- ============================================================
-- DB2 AUTHORIZATION FOR HBT CORE BANKING APPLICATION
-- ============================================================

-- REVOKE SYSADM FROM THE APPLICATION SERVICE ACCOUNT
REVOKE SYSADM FROM HBTCICS1;
REVOKE SYSADM FROM HBTBATCH;

-- CREATE APPLICATION-SPECIFIC ROLES
-- (DB2 12+ SUPPORTS ROLES; EARLIER VERSIONS USE SECONDARY AUTHIDS)

-- GRANT TABLE-LEVEL ACCESS FOR CICS OPERATIONS
GRANT SELECT ON TABLE HBT.ACCOUNT_MASTER
    TO HBTCICS1;
GRANT SELECT, UPDATE ON TABLE HBT.ACCOUNT_MASTER
    TO HBTBATCH;

GRANT SELECT ON TABLE HBT.CUSTOMER_MASTER
    TO HBTCICS1;
GRANT SELECT ON TABLE HBT.CUSTOMER_MASTER
    TO HBTBATCH;

GRANT INSERT ON TABLE HBT.TRANSACTION_LOG
    TO HBTCICS1;
GRANT SELECT, INSERT ON TABLE HBT.TRANSACTION_LOG
    TO HBTBATCH;

GRANT INSERT ON TABLE HBT.AUDIT_TRAIL
    TO HBTCICS1;
GRANT INSERT ON TABLE HBT.AUDIT_TRAIL
    TO HBTBATCH;
GRANT SELECT ON TABLE HBT.AUDIT_TRAIL
    TO HBTAUDIT;

-- GRANT PLAN EXECUTION AUTHORITY
-- EACH CICS REGION GETS EXECUTE ON ITS OWN PLANS ONLY
GRANT EXECUTE ON PLAN HBTINQ01
    TO HBTCICS1;
GRANT EXECUTE ON PLAN HBTPOS01
    TO HBTCICS1;
GRANT EXECUTE ON PLAN HBTOVR01
    TO HBTCICS1;

-- BATCH PLANS
GRANT EXECUTE ON PLAN HBTEOD01
    TO HBTBATCH;
GRANT EXECUTE ON PLAN HBTINT01
    TO HBTBATCH;
GRANT EXECUTE ON PLAN HBTRPT01
    TO HBTBATCH;

-- DENY DYNAMIC SQL FOR APPLICATION ACCOUNTS
-- (PREVENTS SQL INJECTION THROUGH APPLICATION)
REVOKE CREATEIN ON SCHEMA HBT FROM HBTCICS1;
REVOKE CREATEIN ON SCHEMA HBT FROM HBTBATCH;

-- AUDIT TABLE: NO DELETE OR UPDATE (APPEND-ONLY)
REVOKE DELETE ON TABLE HBT.AUDIT_TRAIL FROM PUBLIC;
REVOKE UPDATE ON TABLE HBT.AUDIT_TRAIL FROM PUBLIC;

Key Authorization Principles

Separate read and write authority: The CICS service account gets SELECT on master tables but not UPDATE. Only the posting transaction (through a specific plan) can modify account data. This prevents a compromised inquiry transaction from modifying balances.

Plan-level execution control: Each DB2 plan is individually authorized. The CICS region can execute the inquiry plan and the posting plan, but not the end-of-day batch plan. This prevents online transactions from invoking batch logic.

No dynamic SQL: By revoking CREATEIN authority, the application accounts cannot execute dynamic SQL. All database access must go through pre-bound plans with static SQL, eliminating the primary vector for SQL injection attacks.


Phase 4: Secure COBOL Coding Practices

The most impactful security improvements were made in the COBOL application code itself. The team implemented input validation, authorization checks, and secure data handling patterns.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. HBTPOST1.
      *================================================================*
      * PROGRAM: HBTPOST1 - ACCOUNT POSTING WITH SECURITY CONTROLS
      * PURPOSE: PROCESS DEPOSITS AND WITHDRAWALS WITH FULL
      *          INPUT VALIDATION, AUTHORIZATION CHECKING,
      *          AND AUDIT LOGGING.
      *================================================================*
       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-SECURITY-CONTEXT.
           05  WS-USER-ID             PIC X(8).
           05  WS-USER-ROLE           PIC X(4).
               88  ROLE-TELLER                   VALUE 'TELL'.
               88  ROLE-MANAGER                  VALUE 'MGRS'.
               88  ROLE-WIRE-OP                  VALUE 'WIRE'.
               88  ROLE-ADMIN                    VALUE 'ADMN'.
           05  WS-BRANCH-CODE         PIC X(4).
           05  WS-AUTH-LEVEL          PIC 9(1).
               88  AUTH-INQUIRY-ONLY             VALUE 1.
               88  AUTH-STANDARD-POST            VALUE 2.
               88  AUTH-ELEVATED-POST            VALUE 3.
               88  AUTH-ADMIN-LEVEL              VALUE 9.

       01  WS-POSTING-LIMITS.
           05  WS-TELLER-LIMIT        PIC S9(11)V99 COMP-3
                                      VALUE +5000.00.
           05  WS-MANAGER-LIMIT       PIC S9(11)V99 COMP-3
                                      VALUE +50000.00.
           05  WS-WIRE-LIMIT          PIC S9(11)V99 COMP-3
                                      VALUE +1000000.00.

       01  WS-TRANSACTION-INPUT.
           05  WS-INPUT-ACCOUNT       PIC X(10).
           05  WS-INPUT-AMOUNT        PIC S9(11)V99 COMP-3.
           05  WS-INPUT-TYPE          PIC X(2).
           05  WS-INPUT-DESCRIPTION   PIC X(40).

       01  WS-VALIDATION-FLAGS.
           05  WS-VALID-INPUT         PIC X(1).
               88  INPUT-IS-VALID              VALUE 'Y'.
               88  INPUT-IS-INVALID            VALUE 'N'.
           05  WS-AUTH-RESULT         PIC X(1).
               88  AUTHORIZED                  VALUE 'Y'.
               88  NOT-AUTHORIZED              VALUE 'N'.
           05  WS-ERROR-MESSAGE       PIC X(80).

       01  WS-AUDIT-RECORD.
           05  WA-TIMESTAMP           PIC X(26).
           05  WA-USER-ID             PIC X(8).
           05  WA-TRANSACTION-TYPE    PIC X(4).
           05  WA-ACCOUNT-NUMBER      PIC X(10).
           05  WA-AMOUNT              PIC S9(11)V99 COMP-3.
           05  WA-RESULT-CODE         PIC X(4).
           05  WA-BEFORE-BALANCE      PIC S9(13)V99 COMP-3.
           05  WA-AFTER-BALANCE       PIC S9(13)V99 COMP-3.
           05  WA-BRANCH-CODE         PIC X(4).
           05  WA-TERMINAL-ID         PIC X(8).
           05  WA-ERROR-DETAIL        PIC X(80).

       PROCEDURE DIVISION.
       0000-MAIN.
           PERFORM 1000-GET-SECURITY-CONTEXT
           PERFORM 2000-VALIDATE-INPUT
           IF INPUT-IS-VALID
               PERFORM 3000-AUTHORIZE-TRANSACTION
           END-IF
           IF INPUT-IS-VALID AND AUTHORIZED
               PERFORM 4000-EXECUTE-POSTING
           END-IF
           PERFORM 5000-WRITE-AUDIT-RECORD
           PERFORM 9000-RETURN-RESULT
           STOP RUN.

       1000-GET-SECURITY-CONTEXT.
      *--------------------------------------------------------------*
      * RETRIEVE THE CICS USER ID AND DETERMINE ROLE/AUTHORITY
      * USING EXEC CICS ASSIGN AND RACF GROUP MEMBERSHIP
      *--------------------------------------------------------------*
           EXEC CICS ASSIGN
               USERID(WS-USER-ID)
           END-EXEC

           EXEC CICS INQUIRE
               TERMINAL(EIBTRMID)
               FACILITY(WS-BRANCH-CODE)
           END-EXEC

      *    DETERMINE ROLE FROM RACF GROUP MEMBERSHIP
      *    (SIMPLIFIED - PRODUCTION WOULD USE RACROUTE)
           EVALUATE TRUE
               WHEN WS-USER-ID(1:3) = 'ADM'
                   SET ROLE-ADMIN TO TRUE
                   SET AUTH-ADMIN-LEVEL TO TRUE
               WHEN WS-USER-ID(1:3) = 'WIR'
                   SET ROLE-WIRE-OP TO TRUE
                   SET AUTH-ELEVATED-POST TO TRUE
               WHEN WS-USER-ID(1:3) = 'MGR'
                   SET ROLE-MANAGER TO TRUE
                   SET AUTH-ELEVATED-POST TO TRUE
               WHEN OTHER
                   SET ROLE-TELLER TO TRUE
                   SET AUTH-STANDARD-POST TO TRUE
           END-EVALUATE.

       2000-VALIDATE-INPUT.
      *--------------------------------------------------------------*
      * VALIDATE ALL INPUT FIELDS BEFORE PROCESSING
      * DEFENSE AGAINST MALFORMED OR MALICIOUS INPUT
      *--------------------------------------------------------------*
           SET INPUT-IS-VALID TO TRUE

      *    VALIDATE ACCOUNT NUMBER - MUST BE 10 NUMERIC DIGITS
           INSPECT WS-INPUT-ACCOUNT
               TALLYING WS-TALLY-COUNT
               FOR ALL SPACES
           IF WS-TALLY-COUNT > 0
               SET INPUT-IS-INVALID TO TRUE
               MOVE 'ACCOUNT NUMBER CONTAINS SPACES'
                   TO WS-ERROR-MESSAGE
               PERFORM 5000-WRITE-AUDIT-RECORD
           END-IF

           IF INPUT-IS-VALID
               IF WS-INPUT-ACCOUNT IS NOT NUMERIC
                   SET INPUT-IS-INVALID TO TRUE
                   MOVE 'ACCOUNT NUMBER IS NOT NUMERIC'
                       TO WS-ERROR-MESSAGE
               END-IF
           END-IF

      *    VALIDATE AMOUNT - MUST BE POSITIVE AND NON-ZERO
           IF INPUT-IS-VALID
               IF WS-INPUT-AMOUNT NOT > ZERO
                   SET INPUT-IS-INVALID TO TRUE
                   MOVE 'AMOUNT MUST BE GREATER THAN ZERO'
                       TO WS-ERROR-MESSAGE
               END-IF
           END-IF

      *    VALIDATE TRANSACTION TYPE
           IF INPUT-IS-VALID
               IF WS-INPUT-TYPE NOT = 'DP'
                  AND WS-INPUT-TYPE NOT = 'WD'
                  AND WS-INPUT-TYPE NOT = 'TI'
                  AND WS-INPUT-TYPE NOT = 'TO'
                   SET INPUT-IS-INVALID TO TRUE
                   STRING 'INVALID TRANSACTION TYPE: '
                          WS-INPUT-TYPE
                          DELIMITED BY SIZE
                       INTO WS-ERROR-MESSAGE
               END-IF
           END-IF

      *    VALIDATE DESCRIPTION - NO LOW-VALUE CHARACTERS
      *    (DEFENSE AGAINST BINARY DATA INJECTION)
           IF INPUT-IS-VALID
               INSPECT WS-INPUT-DESCRIPTION
                   TALLYING WS-TALLY-COUNT
                   FOR ALL LOW-VALUES
               IF WS-TALLY-COUNT > 0
                   SET INPUT-IS-INVALID TO TRUE
                   MOVE 'DESCRIPTION CONTAINS INVALID CHARS'
                       TO WS-ERROR-MESSAGE
               END-IF
           END-IF.

       3000-AUTHORIZE-TRANSACTION.
      *--------------------------------------------------------------*
      * CHECK THAT THE USER IS AUTHORIZED FOR THIS TRANSACTION
      * AMOUNT AGAINST THEIR ROLE-BASED LIMIT
      *--------------------------------------------------------------*
           SET AUTHORIZED TO TRUE

           EVALUATE TRUE
               WHEN ROLE-TELLER
                   IF WS-INPUT-AMOUNT > WS-TELLER-LIMIT
                       SET NOT-AUTHORIZED TO TRUE
                       STRING 'AMOUNT '
                              WS-INPUT-AMOUNT
                              ' EXCEEDS TELLER LIMIT'
                              DELIMITED BY SIZE
                           INTO WS-ERROR-MESSAGE
                   END-IF

               WHEN ROLE-MANAGER
                   IF WS-INPUT-AMOUNT > WS-MANAGER-LIMIT
                       SET NOT-AUTHORIZED TO TRUE
                       STRING 'AMOUNT EXCEEDS MANAGER LIMIT'
                              DELIMITED BY SIZE
                           INTO WS-ERROR-MESSAGE
                   END-IF

               WHEN ROLE-WIRE-OP
                   IF WS-INPUT-AMOUNT > WS-WIRE-LIMIT
                       SET NOT-AUTHORIZED TO TRUE
                       MOVE 'AMOUNT EXCEEDS WIRE LIMIT'
                           TO WS-ERROR-MESSAGE
                   END-IF

               WHEN OTHER
                   SET NOT-AUTHORIZED TO TRUE
                   MOVE 'UNRECOGNIZED USER ROLE'
                       TO WS-ERROR-MESSAGE
           END-EVALUATE.

       4000-EXECUTE-POSTING.
      *--------------------------------------------------------------*
      * PERFORM THE ACTUAL ACCOUNT POSTING
      * CAPTURE BEFORE/AFTER BALANCES FOR AUDIT
      *--------------------------------------------------------------*
           EXEC CICS READ
               FILE('ACCTMAST')
               INTO(WS-ACCOUNT-REC)
               RIDFLD(WS-INPUT-ACCOUNT)
               UPDATE
           END-EXEC

      *    CAPTURE BEFORE-IMAGE FOR AUDIT
           MOVE WS-ACCT-BALANCE TO WA-BEFORE-BALANCE

      *    APPLY THE TRANSACTION
           EVALUATE WS-INPUT-TYPE
               WHEN 'DP'
                   ADD WS-INPUT-AMOUNT TO WS-ACCT-BALANCE
               WHEN 'WD'
                   IF WS-INPUT-AMOUNT > WS-ACCT-BALANCE
                       SET NOT-AUTHORIZED TO TRUE
                       MOVE 'INSUFFICIENT FUNDS'
                           TO WS-ERROR-MESSAGE
                       EXEC CICS UNLOCK
                           FILE('ACCTMAST')
                       END-EXEC
                   ELSE
                       SUBTRACT WS-INPUT-AMOUNT
                           FROM WS-ACCT-BALANCE
                   END-IF
               WHEN 'TI'
                   ADD WS-INPUT-AMOUNT TO WS-ACCT-BALANCE
               WHEN 'TO'
                   IF WS-INPUT-AMOUNT > WS-ACCT-BALANCE
                       SET NOT-AUTHORIZED TO TRUE
                       MOVE 'INSUFFICIENT FUNDS FOR TRANSFER'
                           TO WS-ERROR-MESSAGE
                       EXEC CICS UNLOCK
                           FILE('ACCTMAST')
                       END-EXEC
                   ELSE
                       SUBTRACT WS-INPUT-AMOUNT
                           FROM WS-ACCT-BALANCE
                   END-IF
           END-EVALUATE

           IF AUTHORIZED
               MOVE WS-ACCT-BALANCE TO WA-AFTER-BALANCE
               EXEC CICS REWRITE
                   FILE('ACCTMAST')
                   FROM(WS-ACCOUNT-REC)
               END-EXEC
               MOVE 'SUCC' TO WA-RESULT-CODE
           END-IF.

       5000-WRITE-AUDIT-RECORD.
      *--------------------------------------------------------------*
      * WRITE AUDIT RECORD FOR EVERY TRANSACTION ATTEMPT
      * REGARDLESS OF WHETHER IT SUCCEEDED OR FAILED
      *--------------------------------------------------------------*
           EXEC CICS ASKTIME
               ABSTIME(WS-ABSTIME)
           END-EXEC
           EXEC CICS FORMATTIME
               ABSTIME(WS-ABSTIME)
               YYYYMMDD(WA-TIMESTAMP)
               TIME(WA-TIMESTAMP(9:8))
               TIMESEP(':')
           END-EXEC

           MOVE WS-USER-ID       TO WA-USER-ID
           MOVE WS-INPUT-TYPE    TO WA-TRANSACTION-TYPE
           MOVE WS-INPUT-ACCOUNT TO WA-ACCOUNT-NUMBER
           MOVE WS-INPUT-AMOUNT  TO WA-AMOUNT
           MOVE WS-BRANCH-CODE   TO WA-BRANCH-CODE
           MOVE EIBTRMID         TO WA-TERMINAL-ID

           IF NOT AUTHORIZED OR INPUT-IS-INVALID
               MOVE 'FAIL' TO WA-RESULT-CODE
               MOVE WS-ERROR-MESSAGE TO WA-ERROR-DETAIL
           END-IF

           EXEC CICS WRITE
               FILE('AUDITLOG')
               FROM(WS-AUDIT-RECORD)
               RIDFLD(WS-AUDIT-RBA)
           END-EXEC.

       9000-RETURN-RESULT.
           IF AUTHORIZED AND INPUT-IS-VALID
               EXEC CICS SEND
                   FROM(WS-SUCCESS-MSG)
                   LENGTH(LENGTH OF WS-SUCCESS-MSG)
               END-EXEC
           ELSE
               EXEC CICS SEND
                   FROM(WS-ERROR-MESSAGE)
                   LENGTH(LENGTH OF WS-ERROR-MESSAGE)
               END-EXEC
           END-IF

           EXEC CICS RETURN END-EXEC.

Secure Coding Patterns Demonstrated

Input validation before processing: Every input field is validated before any business logic executes. The account number is checked for numeric content. The amount is verified as positive and non-zero. The transaction type is compared against a whitelist of valid values. The description is scanned for binary data (LOW-VALUES) that could indicate an injection attempt.

Role-based authorization in code: Even though CICS transaction security controls who can invoke the transaction, the COBOL program implements an additional layer of authorization based on the user's role and the transaction amount. This defense-in-depth approach means that even if the CICS security configuration is misconfigured, the application enforces its own limits.

Before/after image capture: Every posting operation records the account balance before and after the transaction. This provides a complete audit trail that auditors can use to verify the integrity of every balance change.

Audit on failure as well as success: The audit record is written for every transaction attempt, not just successful ones. Failed transactions (invalid input, insufficient funds, authorization denied) generate audit records with the failure reason. This is critical for detecting attack patterns -- a series of failed attempts with invalid account numbers might indicate an enumeration attack.

No sensitive data in error messages: The error messages returned to the user contain functional descriptions ("AMOUNT EXCEEDS TELLER LIMIT") but never reveal internal details such as the actual limit value, database error codes, or system internals that could be exploited.


Lessons Learned

1. Security Is Layered, Not Singular

The OCC examination revealed that HBT had relied on a single security mechanism (CICS signon) to protect the entire application. The remediation implemented four independent layers: RACF dataset profiles, CICS transaction security, DB2 authorization, and application-level validation. An attacker would need to breach all four layers to compromise the system.

2. UACC(NONE) Is the Only Safe Default

The legacy RACF configuration used UACC(READ) on many profiles "for convenience." This meant that every user on the system, including contractors who had been terminated but whose IDs had not yet been revoked, could read customer financial data. The new architecture starts with UACC(NONE) universally and adds explicit PERMIT statements only for users who have a documented business need.

3. Revoking SYSADM from Application Accounts Is Non-Trivial

When the team revoked SYSADM from the CICS and batch service accounts, 47 batch jobs failed on the first night. Each job had to be analyzed to determine the minimum DB2 privileges required, and individual GRANT statements had to be created. This process took three weeks. The lesson: excessive privileges mask missing authorizations, and remediation is always harder than getting it right the first time.

4. Audit Logging Must Be Tamper-Evident

The audit log file is written through a CICS WRITE operation to an ESDS (entry-sequenced dataset). ESDS records cannot be updated or deleted in place -- they can only be appended. Combined with the RACF profile that grants only UPDATE (write/append) access to the CICS service account and READ access only to the audit team, this creates a tamper-evident trail that satisfies the OCC examination requirements.


Discussion Questions

  1. The RACF profile HBT.CORE.PROD.MAST.** uses a double wildcard. What datasets would this match, and what would it not match? How would you construct a profile that protects only the account master without protecting the customer master?

  2. The DB2 authorization grants SELECT on ACCOUNT_MASTER to the CICS service account, but the posting program needs to UPDATE the account balance. How is this apparent contradiction resolved through the separation of DB2 plan authorization and table authorization?

  3. The COBOL program validates that the account number is numeric, but it does not verify that the account exists before attempting the CICS READ. What happens if the account does not exist? How should the program handle this situation securely?

  4. The role determination logic in paragraph 1000 uses the first three characters of the user ID to infer the role. Why is this approach insecure in a production environment, and what alternative mechanism should be used?

  5. If a teller's terminal is left unattended and another person uses it, the audit trail will record the teller's user ID, not the actual person. What additional security controls could mitigate this risk?


Connection to Chapter Concepts

This case study integrates several key concepts from Chapter 31:

  • RACF profiles and access control (Section: RACF -- Resource Access Control Facility): Generic dataset profiles, UACC, PERMIT, and AUDIT demonstrate the core RACF mechanisms for z/OS dataset protection.

  • CICS security (Section: CICS Transaction and Resource Security): Transaction-level security through TCICSTRN and resource-level security through FCICSFCT show how CICS applications are secured at multiple levels.

  • DB2 authorization (Section: DB2 Security and GRANT/REVOKE): Table-level GRANT, plan-level EXECUTE authority, and the revocation of excessive privileges demonstrate the principle of least privilege in the database layer.

  • Secure COBOL coding (Section: Secure Coding Practices for z/OS): Input validation, role-based authorization, before/after image capture, and audit logging demonstrate application-level security controls that complement infrastructure security.