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:
- 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.
- 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.
- 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.
- 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
-
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? -
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?
-
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?
-
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?
-
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.