Payment Security: Encryption vs. Hashing
The difference between encryption and hashing, and how pktHash protects your transactions from amount tampering via browser dev tools.

"I changed the payment amount in DevTools and it went through."
This is a classic vulnerability found during security testing. A malicious user opens browser DevTools, edits the payment amount from 10000 to 100 in the request payload, and submits it to the PG. The payment goes through at the lower amount.
Any time data flows through a browser before reaching your server, tampering is a real threat. Hecto Financial addresses this with a two-layer security model: encryption to hide sensitive data and hashing to detect tampering. Neither alone is sufficient — you need both.
This article explains how each technique works and how to implement them correctly.
Encryption vs. Hashing: Not the Same Thing
These terms get conflated constantly in the field. They serve distinct purposes.
| Encryption | Hash | |
|---|---|---|
| Purpose | Confidentiality (hide data) | Integrity (detect tampering) |
| Reversible | Yes (two-way) | No (one-way) |
| Analogy | A locked safe | A tamper-evident seal |
| Applied to | Card numbers, account numbers, PII | Order details, payment amounts |
| Algorithm used by Hecto Financial | AES-256/ECB/PKCS5Padding | SHA-256 |
1. Encryption: Hiding the Data
Encryption transforms data into an unreadable form. Only the party that holds the key — the PG — can decrypt it back to the original value.
- Applied to: Card numbers, expiry dates, passwords, and other sensitive personal data
- Key property: Even if the encrypted payload is intercepted, it's useless without the decryption key
2. Hashing: Verifying Integrity
A hash function maps data to a fixed-length string. Change even a single character of the input, and the output hash changes completely.
- Applied to: Order numbers, payment amounts, and any field where tampering must be detected
- Key property: Hashing is one-way — there's no decryption. Its only job is to prove the data hasn't changed
Why Both Layers Are Necessary
Each technique alone has a critical gap:
- Encryption alone: An attacker can capture an encrypted string from a known transaction (e.g., the ciphertext for "100 KRW") and substitute it into a different request — a replay attack. The amount is hidden, but the wrong ciphertext is delivered.
- Hashing alone: The data is visible in plaintext. Sensitive fields like card numbers are fully exposed.
The two-layer approach closes both gaps: encryption hides the content, hashing prevents substitution.
Implementation (Node.js)
1. AES-256 Encryption (PKCS5 Padding)
Use this for sensitive fields like card numbers.
Algorithm: AES-256/ECB/PKCS5Padding
- AES-256: Advanced Encryption Standard with a 256-bit key
- ECB mode: Each block is encrypted independently
- PKCS5 padding: Pads the data to align with the 16-byte block size
const crypto = require('crypto');
function encryptAES256(text, key) {
// Key must be exactly 32 bytes
const cipher = crypto.createCipheriv('aes-256-ecb', key, null);
cipher.setAutoPadding(true); // PKCS5 padding applied automatically
let encrypted = cipher.update(text, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
// Example
const secretKey = 'pgSettle30y739r82jtd709yOfZ2yK5K'; // 32 bytes
const cardNum = '1234567812345678';
const encryptedCardNum = encryptAES256(cardNum, secretKey);
console.log('Encrypted card number:', encryptedCardNum);
2. SHA-256 Hash (pktHash) — Credit Card Payment Example
For pktHash generation, the order of fields is fixed. Do not deviate from it.
Field Concatenation Order (PG credit card payment)
function generatePktHash(mchtId, method, mchtTrdNo, trdDt, trdTm, trdAmt, hashKey) {
// Fixed concatenation order (PG credit card payment)
const rawString = mchtId + method + mchtTrdNo + trdDt + trdTm + trdAmt(plaintext) + hashKey;
// SHA-256 hash, hex-encoded (lowercase)
const hash = crypto.createHash('sha256').update(rawString, 'utf8').digest('hex');
return hash;
}
// Example — credit card payment
const mchtId = 'nx_mid_il'; // Merchant ID
const method = 'CARD'; // Payment method
const mchtTrdNo = 'ORDER20231126001'; // Merchant order number
const trdDt = '20231126'; // Transaction date (YYYYMMDD)
const trdTm = '153000'; // Transaction time (HHMMSS)
const trdAmt = '10000'; // Transaction amount (plaintext)
const hashKey = 'ST1009281328226982205'; // Hash authentication key
const pktHash = generatePktHash(mchtId, method, mchtTrdNo, trdDt, trdTm, trdAmt, hashKey);
console.log('Generated pktHash:', pktHash);
How Verification Works
Two Rules to Remember
Security in payment systems is not optional.
- Encryption (AES-256) keeps sensitive data private.
- Hashing (SHA-256) proves the data hasn't been altered.
Implement both correctly, and you've protected your customers' data and closed the door on the most common payment tampering attacks.
Need technical support?
Code Samples
HectoFinancial GitHub