You have exposed your IBM i programs as REST APIs. Your mobile app is working. Your cloud integrations are live. Then someone asks: "How secure is this?"
Opening your IBM i to the modern world also opens it to modern threats. This article explores practical, layered security strategies for IBM i in a connected world — extending the platform's already strong foundation to cover the API era.
Before addressing modern threats, it is important to recognize what IBM i already provides. It is one of the most secure enterprise platforms available.
Traditional IBM i security was designed for an era of 5250 terminals, on-premise applications, and a clearly defined network perimeter. Today's reality is fundamentally different.
APIs are accessible from anywhere — mobile apps, cloud services, partner systems, and IoT devices. Threats have evolved to match: SQL injection, API abuse, credential stuffing, data exfiltration, and zero-day exploits. Compliance requirements have grown stricter, with GDPR, SOC 2, PCI-DSS, and HIPAA each demanding specific security controls.
Security is not a single solution — it is multiple layers working together. If one layer fails, the others continue to defend. Modern IBM i security follows a Zero Trust model: never trust, always verify — even for internal traffic.
The sections below address each layer in turn.
The first line of defense is controlling what traffic can reach your system at all. The principle is simple: only allow what is explicitly necessary.
# Allow HTTPS only from the internet
ALLOW TCP 443 FROM ANY TO IBM_I_IP
# Block direct database access from internet
DENY TCP 8471 FROM ANY TO IBM_I_IP # DRDA port
DENY TCP 446 FROM ANY TO IBM_I_IP # DDM port
# Allow SSH for administration from restricted IPs only
ALLOW TCP 22 FROM ADMIN_IPS TO IBM_I_IP
A Web Application Firewall (WAF) sits in front of your API and filters malicious HTTP requests before they reach your application — blocking SQL injection attempts, cross-site scripting, known attack patterns, and malformed requests.
Your IBM i system should never be directly exposed to the internet. An API Gateway is the single point of entry for all external traffic, providing centralized authentication, rate limiting, IP filtering, request validation, and logging.
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15-minute window
max: 100, // Max 100 requests per IP
message: 'Too many requests from this IP, please try again later'
});
app.use('/api/', limiter);
Every API consumer must be identified. Validate API keys against IBM i to keep authentication centralized on the platform.
**FREE
Dcl-Proc validateApiKey Export;
Dcl-Pi *N Ind;
apiKey Char(64) Const;
End-Pi;
Dcl-S isValid Ind;
Exec SQL
SELECT CASE
WHEN EXPIRY_DATE >= CURRENT DATE THEN '1'
ELSE '0'
END
INTO :isValid
FROM APIKEYS
WHERE API_KEY = :apiKey
AND STATUS = 'ACTIVE';
If SQLCODE <> 0;
Return *Off;
EndIf;
Return isValid;
End-Proc;
JWT tokens are the recommended approach for external-facing APIs. Users authenticate once, receive a time-limited token, and include that token in all subsequent requests.
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
app.post('/api/login', async (req, res) => {
const { username, password } = req.body;
const user = await validateIBMiUser(username, password);
if (!user) return res.status(401).json({ error: 'Invalid credentials' });
const isValidPassword = await bcrypt.compare(password, user.hash);
if (!isValidPassword) return res.status(401).json({ error: 'Invalid credentials' });
const token = jwt.sign(
{ username: user.username, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ token });
});
For internal APIs, IBM i User Profile validation keeps authentication on the platform itself using the QSYGETPH API.
APIs should never run under powerful profiles like QSECOFR or application owners. Instead, create dedicated service profiles with the minimum required authority — the least privilege principle.
/* Create a profile with no special authority */
CRTUSRPRF USRPRF(APIUSER)
PASSWORD(*NONE)
USRCLS(*USER)
SPCAUT(*NONE)
TEXT('API Service Profile')
/* Grant access only to the required service program */
GRTOBJAUT OBJ(MYLIB/CUSTSVC)
OBJTYPE(*SRVPGM)
USER(APIUSER)
AUT(*USE)
Authentication establishes who you are. Authorization determines what you can do. Role-Based Access Control managed in DB2 for i keeps permissions centralized and auditable.
CREATE TABLE ROLES (
ROLE_ID INT GENERATED ALWAYS AS IDENTITY,
ROLE_NAME VARCHAR(50) NOT NULL,
PRIMARY KEY (ROLE_ID)
);
CREATE TABLE USER_ROLES (
USER_ID VARCHAR(10) NOT NULL,
ROLE_ID INT NOT NULL,
PRIMARY KEY (USER_ID, ROLE_ID)
);
CREATE TABLE ROLE_PERMISSIONS (
ROLE_ID INT NOT NULL,
RESOURCE VARCHAR(100) NOT NULL,
OPERATION VARCHAR(20) NOT NULL,
PRIMARY KEY (ROLE_ID, RESOURCE, OPERATION)
);
const checkPermission = (resource, operation) => {
return async (req, res, next) => {
const authorized = await checkAuthorizationInIBMi(
req.user.username, resource, operation
);
if (!authorized) return res.status(403).json({ error: 'Insufficient permissions' });
next();
};
};
// Applied per route — clear, explicit, auditable
app.post('/api/orders',
authenticateJWT,
checkPermission('orders', 'create'),
createOrder
);
Never trust user input. Validate at both the API layer and the RPG layer — two checkpoints are better than one.
// Attack input: "' OR '1'='1"
// Result: returns ALL customer records
sqlStmt = 'SELECT * FROM CUSTMAST WHERE CUSTNO = ''' + customerId + '''';
// Database treats :customerId as data, never as SQL code
Exec SQL
SELECT * INTO :customer
FROM CUSTMAST
WHERE CUSTNO = :customerId;
:variable). Avoid dynamic SQL unless absolutely necessary. Never build SQL strings by concatenation with user-supplied input.Listen *:443
<VirtualHost *:443>
ServerName api.yourcompany.com
SSLEngine on
SSLCertificateFile /path/to/certificate.crt
SSLCertificateKeyFile /path/to/private.key
SSLCertificateChainFile /path/to/chain.crt
# Strong cipher suites only — disable legacy protocols
SSLCipherSuite HIGH:!aNULL:!MD5
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
ProxyPass /api http://localhost:3000/api
ProxyPassReverse /api http://localhost:3000/api
</VirtualHost>
Encrypt sensitive fields at the application layer for maximum control. This applies to Social Security numbers, payment card data, personal health information, and financial data. Store encryption keys securely — never in source code — and implement key rotation policies.
CRTJRNRCV JRNRCV(QAUDIT/AUDRCV0001) THRESHOLD(100000)
CRTJRN JRN(QAUDIT/QAUDJRN) JRNRCV(QAUDIT/AUDRCV0001)
CHGSYSVAL SYSVAL(QAUDLVL)
VALUE(*AUTFAIL *CREATE *DELETE *SECURITY)
const failedAttempts = new Map();
const THRESHOLD = { count: 5, window: 300000 }; // 5 failures in 5 minutes
app.post('/api/login', async (req, res) => {
const { username, password } = req.body;
const ip = req.ip;
const user = await validateUser(username, password);
if (!user) {
const key = `${ip}:${username}`;
const attempts = (failedAttempts.get(key) || [])
.filter(t => Date.now() - t < THRESHOLD.window);
attempts.push(Date.now());
failedAttempts.set(key, attempts);
if (attempts.length >= THRESHOLD.count) {
await sendSecurityAlert({ type: 'BRUTE_FORCE', ip, username });
}
return res.status(401).json({ error: 'Invalid credentials' });
}
failedAttempts.delete(`${ip}:${username}`);
// Generate and return token...
});
const conn = new Connection({ host: 'ibmi.company.com', user: 'ADMIN', password: 'Password123' });
const conn = new Connection({
host: process.env.IBMI_HOST,
user: process.env.IBMI_USER,
password: process.env.IBMI_PASSWORD
});
app.use(cors({ origin: '*' }));
app.use(cors({
origin: ['https://app.company.com', 'https://mobile.company.com'],
credentials: true
}));
IBM i security has always been strong. The challenge today is not securing the platform itself — it is securing how the platform is accessed. If your APIs are the new front door to your IBM i system, security is your first line of defense.
Apply security in layers, and keep these principles consistent across every API you build:
Extend IBM i's strong foundation for the API era, and you can safely connect to the modern world without compromising the integrity that makes the platform exceptional.