Back to blog
Engineering

Payment Security: Encryption vs. Hashing

The difference between encryption and hashing, and how pktHash protects your transactions from amount tampering via browser dev tools.

Hecto Financial Engineering
2025-01-18
6 min read
#security#encryption#pktHash#hash
Payment Security: Encryption vs. Hashing 썸네일 이미지

"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.

EncryptionHash
PurposeConfidentiality (hide data)Integrity (detect tampering)
ReversibleYes (two-way)No (one-way)
AnalogyA locked safeA tamper-evident seal
Applied toCard numbers, account numbers, PIIOrder details, payment amounts
Algorithm used by Hecto FinancialAES-256/ECB/PKCS5PaddingSHA-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:

  1. 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.
  2. 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.

NOTE

Field Concatenation Order (PG credit card payment)

Merchant ID (mchtId) + Payment method (method) + Merchant order number (mchtTrdNo) + Transaction date (trdDt) + Transaction time (trdTm) + Transaction amount, plaintext (trdAmt) + Hash key
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);
NOTE

How Verification Works

The PG generates a pktHash using the same algorithm and field order. Your server generates its own hash from the received parameters and compares the two. A mismatch means the data was tampered with in transit.

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?