1. LEARNING OBJECTIVES

By the end of this lesson, you will be able to:

  • Understand the security landscape in FinTech and common attack vectors.

  • Implement cryptographic algorithms for data protection (symmetric, asymmetric, hashing).

  • Secure API endpoints with authentication and authorization.

  • Implement secure key management and storage.

  • Apply secure coding practices to prevent common vulnerabilities.

  • Build a secure payment processing system.

  • Understand PCI-DSS compliance requirements.

  • Implement secure audit logging and monitoring.


2. SECURITY LANDSCAPE IN FINTECH

2.1 Common Attack Vectors

 
 
Attack Type Description Financial Impact
Phishing Stealing credentials via fake communications Account takeover, unauthorized transactions
Man-in-the-Middle (MITM) Intercepting communications Data theft, transaction manipulation
SQL Injection Malicious database queries Data breach, data manipulation
Cross-Site Scripting (XSS) Injecting malicious scripts Session hijacking, data theft
DDoS Overwhelming systems with traffic Service disruption, financial loss
Insider Threat Malicious or negligent employees Data breach, fraud

2.2 Security Principles

  1. Defense in Depth: Multiple layers of security.

  2. Least Privilege: Minimal access required.

  3. Zero Trust: Never trust, always verify.

  4. Secure by Design: Security built into the system.

  5. Fail Securely: Fail to a secure state.


3. CRYPTOGRAPHY – ENCRYPTION AND HASHING

3.1 Symmetric Encryption (AES)

python
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64
import os

class SymmetricEncryption:
    """
    Implements AES symmetric encryption for sensitive data.
    """
    
    def __init__(self, password=None):
        if password:
            self.key = self._derive_key(password)
        else:
            self.key = Fernet.generate_key()
        
        self.cipher = Fernet(self.key)
    
    def _derive_key(self, password):
        """Derive a key from a password using PBKDF2."""
        salt = os.urandom(16)
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=100000
        )
        key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
        return key
    
    def encrypt(self, data):
        """Encrypt data."""
        if isinstance(data, str):
            data = data.encode()
        return self.cipher.encrypt(data)
    
    def decrypt(self, encrypted_data):
        """Decrypt data."""
        return self.cipher.decrypt(encrypted_data)
    
    def encrypt_file(self, input_file, output_file):
        """Encrypt a file."""
        with open(input_file, 'rb') as f:
            data = f.read()
        encrypted = self.encrypt(data)
        with open(output_file, 'wb') as f:
            f.write(encrypted)
    
    def decrypt_file(self, input_file, output_file):
        """Decrypt a file."""
        with open(input_file, 'rb') as f:
            data = f.read()
        decrypted = self.decrypt(data)
        with open(output_file, 'wb') as f:
            f.write(decrypted)

# Usage
encryption = SymmetricEncryption("strong_password")
encrypted = encryption.encrypt("Sensitive financial data")
print(f"Encrypted: {encrypted}")
decrypted = encryption.decrypt(encrypted)
print(f"Decrypted: {decrypted.decode()}")

3.2 Asymmetric Encryption (RSA)

python
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.backends import default_backend

class AsymmetricEncryption:
    """
    Implements RSA asymmetric encryption.
    """
    
    def __init__(self):
        self.private_key = None
        self.public_key = None
    
    def generate_keys(self, key_size=2048):
        """Generate RSA key pair."""
        self.private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=key_size,
            backend=default_backend()
        )
        self.public_key = self.private_key.public_key()
        return self.public_key
    
    def encrypt(self, data, public_key=None):
        """Encrypt with public key."""
        if public_key is None:
            public_key = self.public_key
        
        if isinstance(data, str):
            data = data.encode()
        
        encrypted = public_key.encrypt(
            data,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=hashes.SHA256()),
                algorithm=hashes.SHA256(),
                label=None
            )
        )
        return encrypted
    
    def decrypt(self, encrypted_data):
        """Decrypt with private key."""
        decrypted = self.private_key.decrypt(
            encrypted_data,
            padding.OAEP(
                mgf=padding.MGF1(algorithm=hashes.SHA256()),
                algorithm=hashes.SHA256(),
                label=None
            )
        )
        return decrypted
    
    def sign(self, data):
        """Sign data with private key."""
        if isinstance(data, str):
            data = data.encode()
        
        signature = self.private_key.sign(
            data,
            padding.PSS(
                mgf=padding.MGF1(hashes.SHA256()),
                salt_length=padding.PSS.MAX_LENGTH
            ),
            hashes.SHA256()
        )
        return signature
    
    def verify(self, data, signature, public_key=None):
        """Verify signature with public key."""
        if public_key is None:
            public_key = self.public_key
        
        if isinstance(data, str):
            data = data.encode()
        
        try:
            public_key.verify(
                signature,
                data,
                padding.PSS(
                    mgf=padding.MGF1(hashes.SHA256()),
                    salt_length=padding.PSS.MAX_LENGTH
                ),
                hashes.SHA256()
            )
            return True
        except Exception:
            return False
    
    def save_keys(self, private_key_file, public_key_file, password=None):
        """Save keys to files."""
        # Save private key
        if password:
            encryption_algorithm = serialization.BestAvailableEncryption(password.encode())
        else:
            encryption_algorithm = serialization.NoEncryption()
        
        private_pem = self.private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=encryption_algorithm
        )
        
        with open(private_key_file, 'wb') as f:
            f.write(private_pem)
        
        # Save public key
        public_pem = self.public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo
        )
        
        with open(public_key_file, 'wb') as f:
            f.write(public_pem)
    
    def load_keys(self, private_key_file, public_key_file, password=None):
        """Load keys from files."""
        # Load private key
        with open(private_key_file, 'rb') as f:
            private_data = f.read()
        
        if password:
            self.private_key = serialization.load_pem_private_key(
                private_data,
                password=password.encode(),
                backend=default_backend()
            )
        else:
            self.private_key = serialization.load_pem_private_key(
                private_data,
                password=None,
                backend=default_backend()
            )
        
        # Load public key
        with open(public_key_file, 'rb') as f:
            public_data = f.read()
        
        self.public_key = serialization.load_pem_public_key(
            public_data,
            backend=default_backend()
        )

# Usage
rsa_crypto = AsymmetricEncryption()
rsa_crypto.generate_keys()

# Encrypt with public key
message = "Sensitive transaction data"
encrypted = rsa_crypto.encrypt(message)
print(f"Encrypted: {encrypted[:50]}...")

# Decrypt with private key
decrypted = rsa_crypto.decrypt(encrypted)
print(f"Decrypted: {decrypted.decode()}")

# Sign and verify
signature = rsa_crypto.sign(message)
is_valid = rsa_crypto.verify(message, signature)
print(f"Signature valid: {is_valid}")

3.3 Hashing (SHA-256)

python
import hashlib
import hmac

def hash_data(data):
    """Hash data using SHA-256."""
    if isinstance(data, str):
        data = data.encode()
    return hashlib.sha256(data).hexdigest()

def hash_with_salt(data, salt=None):
    """Hash data with a salt."""
    if salt is None:
        salt = os.urandom(32)
    if isinstance(data, str):
        data = data.encode()
    
    salted_data = data + salt
    return hashlib.sha256(salted_data).hexdigest(), salt

def verify_hash(data, hashed_data, salt):
    """Verify a hashed password."""
    computed_hash, _ = hash_with_salt(data, salt)
    return hmac.compare_digest(computed_hash, hashed_data)

# Usage
password = "secure_password"
hashed, salt = hash_with_salt(password)
print(f"Hashed: {hashed}")
print(f"Salt: {salt.hex()}")

# Verify
is_valid = verify_hash(password, hashed, salt)
print(f"Valid: {is_valid}")

4. SECURE API IMPLEMENTATION

4.1 API Authentication with JWT

python
import jwt
import time
from functools import wraps
from flask import Flask, request, jsonify

app = Flask(__name__)
SECRET_KEY = os.environ.get('JWT_SECRET_KEY', 'your-secret-key')
TOKEN_EXPIRY = 3600  # 1 hour

class JWTManager:
    """Manages JWT tokens for API authentication."""
    
    def __init__(self, secret_key):
        self.secret_key = secret_key
    
    def generate_token(self, user_id, roles=None):
        """Generate a JWT token."""
        payload = {
            'user_id': user_id,
            'roles': roles or [],
            'iat': int(time.time()),
            'exp': int(time.time()) + TOKEN_EXPIRY
        }
        return jwt.encode(payload, self.secret_key, algorithm='HS256')
    
    def verify_token(self, token):
        """Verify and decode a JWT token."""
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=['HS256'])
            return payload
        except jwt.ExpiredSignatureError:
            return {'error': 'Token expired'}
        except jwt.InvalidTokenError:
            return {'error': 'Invalid token'}

jwt_manager = JWTManager(SECRET_KEY)

def require_auth(f):
    """Decorator to require authentication."""
    @wraps(f)
    def decorated(*args, **kwargs):
        auth_header = request.headers.get('Authorization')
        if not auth_header:
            return jsonify({'error': 'Missing authorization header'}), 401
        
        parts = auth_header.split()
        if len(parts) != 2 or parts[0].lower() != 'bearer':
            return jsonify({'error': 'Invalid authorization format'}), 401
        
        token = parts[1]
        payload = jwt_manager.verify_token(token)
        
        if 'error' in payload:
            return jsonify(payload), 401
        
        request.user_id = payload['user_id']
        request.user_roles = payload['roles']
        
        return f(*args, **kwargs)
    return decorated

def require_role(required_role):
    """Decorator to require a specific role."""
    def decorator(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            if required_role not in getattr(request, 'user_roles', []):
                return jsonify({'error': 'Insufficient permissions'}), 403
            return f(*args, **kwargs)
        return decorated
    return decorator

@app.route('/api/login', methods=['POST'])
def login():
    """Authenticate and return a JWT token."""
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')
    
    # Validate credentials (implementation would check database)
    if username == 'admin' and password == 'secure_password':
        token = jwt_manager.generate_token(
            user_id='user_001',
            roles=['admin', 'trader']
        )
        return jsonify({'token': token})
    else:
        return jsonify({'error': 'Invalid credentials'}), 401

@app.route('/api/orders', methods=['POST'])
@require_auth
@require_role('trader')
def place_order():
    """Place a trading order."""
    data = request.get_json()
    # Process order
    return jsonify({'status': 'success', 'order_id': 'ORD-001'})

4.2 Input Validation and Sanitization

python
from marshmallow import Schema, fields, validate, ValidationError

class OrderSchema(Schema):
    """Schema for validating order requests."""
    
    symbol = fields.Str(
        required=True,
        validate=validate.Length(min=1, max=10)
    )
    side = fields.Str(
        required=True,
        validate=validate.OneOf(['BUY', 'SELL'])
    )
    quantity = fields.Int(
        required=True,
        validate=validate.Range(min=1, max=1000000)
    )
    price = fields.Float(
        required=False,
        validate=validate.Range(min=0.01)
    )
    order_type = fields.Str(
        required=True,
        validate=validate.OneOf(['MARKET', 'LIMIT', 'STOP'])
    )

class OrderValidator:
    """Validates order requests."""
    
    def __init__(self):
        self.schema = OrderSchema()
    
    def validate(self, data):
        """Validate order data."""
        try:
            return self.schema.load(data)
        except ValidationError as e:
            return {'error': e.messages}

# Usage
validator = OrderValidator()
order_data = {
    'symbol': 'AAPL',
    'side': 'BUY',
    'quantity': 100,
    'price': 150.50,
    'order_type': 'LIMIT'
}
validated = validator.validate(order_data)

4.3 Rate Limiting for API Security

python
from collections import defaultdict
import time
from functools import wraps

class RateLimiter:
    """
    Implements rate limiting for API endpoints.
    """
    
    def __init__(self, rate_limit=100, time_window=60):
        self.rate_limit = rate_limit
        self.time_window = time_window
        self.requests = defaultdict(list)
    
    def is_allowed(self, client_id):
        """Check if a client is allowed to make a request."""
        now = time.time()
        client_requests = self.requests[client_id]
        
        # Remove old requests
        client_requests = [t for t in client_requests if t > now - self.time_window]
        self.requests[client_id] = client_requests
        
        if len(client_requests) >= self.rate_limit:
            return False
        
        self.requests[client_id].append(now)
        return True
    
    def limit(self, client_id_extractor=lambda: request.remote_addr):
        """Decorator for rate limiting."""
        def decorator(f):
            @wraps(f)
            def decorated(*args, **kwargs):
                client_id = client_id_extractor()
                if not self.is_allowed(client_id):
                    return jsonify({'error': 'Rate limit exceeded'}), 429
                return f(*args, **kwargs)
            return decorated
        return decorator

rate_limiter = RateLimiter(rate_limit=100, time_window=60)

@app.route('/api/trades', methods=['GET'])
@rate_limiter.limit()
@require_auth
def get_trades():
    """Get trade history."""
    return jsonify({'trades': []})

5. SECURE KEY MANAGEMENT

5.1 Hardware Security Module (HSM) Integration

python
class HSMClient:
    """
    Client for interacting with a Hardware Security Module.
    """
    
    def __init__(self, endpoint, api_key):
        self.endpoint = endpoint
        self.api_key = api_key
    
    def encrypt_data(self, key_id, data):
        """Encrypt data using HSM."""
        # Implementation would call HSM API
        pass
    
    def decrypt_data(self, key_id, encrypted_data):
        """Decrypt data using HSM."""
        # Implementation would call HSM API
        pass
    
    def sign_data(self, key_id, data):
        """Sign data using HSM."""
        # Implementation would call HSM API
        pass
    
    def verify_signature(self, key_id, data, signature):
        """Verify signature using HSM."""
        # Implementation would call HSM API
        pass

5.2 Vault Integration (HashiCorp Vault)

python
import hvac

class VaultClient:
    """
    Client for HashiCorp Vault secret management.
    """
    
    def __init__(self, url, token):
        self.client = hvac.Client(url=url, token=token)
    
    def store_secret(self, path, data):
        """Store a secret in Vault."""
        self.client.secrets.kv.v2.create_or_update_secret(
            path=path,
            secret=data
        )
    
    def read_secret(self, path):
        """Read a secret from Vault."""
        response = self.client.secrets.kv.v2.read_secret(path=path)
        return response['data']['data']
    
    def delete_secret(self, path):
        """Delete a secret from Vault."""
        self.client.secrets.kv.v2.delete_metadata_and_all_versions(path=path)
    
    def generate_password(self, length=20):
        """Generate a secure password using Vault."""
        response = self.client.secrets.transit.generate_random_bytes(
            number_of_bytes=length
        )
        return response['data']['random_bytes']

# Usage
vault = VaultClient(
    url='http://localhost:8200',
    token='hvs.xxxxxxxxxxxx'
)

# Store API key
vault.store_secret('api-keys/payment', {
    'api_key': 'sk_live_xxxxxxxxxxxx',
    'environment': 'production'
})

# Read API key
api_keys = vault.read_secret('api-keys/payment')

6. SECURE AUDIT LOGGING

6.1 Secure Audit Log Implementation

python
import json
import hashlib
import time
from datetime import datetime

class AuditLogger:
    """
    Implements secure audit logging for financial transactions.
    """
    
    def __init__(self, log_file='audit.log', encryption=None):
        self.log_file = log_file
        self.encryption = encryption
        self.hmac_key = os.urandom(32)
    
    def log_event(self, event_type, user_id, data, ip_address=None):
        """Log an audit event."""
        event = {
            'event_id': self._generate_event_id(),
            'timestamp': datetime.utcnow().isoformat(),
            'event_type': event_type,
            'user_id': user_id,
            'ip_address': ip_address,
            'data': data
        }
        
        # Add HMAC to prevent tampering
        event['hmac'] = self._compute_hmac(event)
        
        # Encrypt if needed
        if self.encryption:
            event = self.encryption.encrypt(json.dumps(event))
        
        # Write to log
        with open(self.log_file, 'a') as f:
            f.write(json.dumps(event) + '\n')
    
    def _generate_event_id(self):
        """Generate a unique event ID."""
        return hashlib.sha256(
            f"{time.time()}{os.urandom(16)}".encode()
        ).hexdigest()[:16]
    
    def _compute_hmac(self, event):
        """Compute HMAC for the event."""
        event_copy = event.copy()
        event_copy.pop('hmac', None)
        return hmac.new(
            self.hmac_key,
            json.dumps(event_copy, sort_keys=True).encode(),
            hashlib.sha256
        ).hexdigest()
    
    def verify_log(self):
        """Verify the integrity of the audit log."""
        with open(self.log_file, 'r') as f:
            for line in f:
                event = json.loads(line.strip())
                
                if self.encryption:
                    event = json.loads(self.encryption.decrypt(event))
                
                expected_hmac = self._compute_hmac(event)
                if event['hmac'] != expected_hmac:
                    return False
        return True

# Usage
audit_logger = AuditLogger()
audit_logger.log_event(
    event_type='TRADE_EXECUTED',
    user_id='user_001',
    data={'order_id': 'ORD-001', 'symbol': 'AAPL', 'quantity': 100},
    ip_address='192.168.1.100'
)

7. PCI-DSS COMPLIANCE

7.1 Payment Card Industry Data Security Standard

Key requirements for PCI-DSS:

  1. Install and maintain firewalls.

  2. Do not use vendor-supplied defaults.

  3. Protect stored cardholder data.

  4. Encrypt transmission of cardholder data.

  5. Protect all systems against malware.

  6. Develop and maintain secure systems.

  7. Restrict access to cardholder data.

  8. Identify and authenticate access.

  9. Restrict physical access.

  10. Monitor and log all access.

  11. Regularly test security systems.

  12. Maintain an information security policy.

7.2 Tokenization Implementation

python
class PaymentTokenization:
    """
    Implements tokenization for payment card data.
    """
    
    def __init__(self):
        self.tokens = {}
        self.vault = VaultClient(
            url=os.environ.get('VAULT_URL'),
            token=os.environ.get('VAULT_TOKEN')
        )
    
    def tokenize_card(self, card_number, expiry, cvv):
        """
        Tokenize a payment card.
        Returns a token that can be stored instead of card data.
        """
        # Generate a token
        token = hashlib.sha256(
            f"{card_number}{expiry}{os.urandom(16)}".encode()
        ).hexdigest()[:16]
        
        # Store card data in vault
        self.vault.store_secret(
            f'cards/{token}',
            {
                'card_number': card_number,
                'expiry': expiry,
                'cvv': cvv
            }
        )
        
        return token
    
    def detokenize_card(self, token):
        """Retrieve card data from a token."""
        return self.vault.read_secret(f'cards/{token}')
    
    def delete_token(self, token):
        """Delete a token and its associated card data."""
        self.vault.delete_secret(f'cards/{token}')

8. SECURE CODING PRACTICES

8.1 Prevention of Common Vulnerabilities

python
# 1. SQL Injection Prevention
def safe_query(connection, query, params):
    """Execute a query safely."""
    cursor = connection.cursor()
    cursor.execute(query, params)  # Use parameterized queries
    return cursor.fetchall()

# 2. Cross-Site Scripting Prevention
def escape_html(text):
    """Escape HTML characters to prevent XSS."""
    import html
    return html.escape(text)

# 3. Secure File Uploads
def validate_file_upload(file):
    """Validate uploaded files."""
    allowed_extensions = ['.pdf', '.png', '.jpg']
    max_size = 5 * 1024 * 1024  # 5MB
    
    import os
    ext = os.path.splitext(file.filename)[1].lower()
    if ext not in allowed_extensions:
        return False
    if file.content_length > max_size:
        return False
    return True

# 4. Secure Password Handling
def hash_password(password):
    """Hash a password using bcrypt."""
    import bcrypt
    return bcrypt.hashpw(password.encode(), bcrypt.gensalt())

def verify_password(password, hashed):
    """Verify a password against its hash."""
    import bcrypt
    return bcrypt.checkpw(password.encode(), hashed)

# 5. Session Management
def generate_session_token():
    """Generate a secure session token."""
    import secrets
    return secrets.token_urlsafe(32)

9. SUMMARY FOR THE FINANCE PRACTITIONER

  • Security is critical in FinTech. Multiple layers of defense are required.

  • Cryptography protects sensitive data. Use symmetric encryption for data at rest, asymmetric for key exchange, hashing for passwords.

  • API Security requires authentication (JWT), authorization (roles), input validation, and rate limiting.

  • Key Management is essential. Use HSMs or Vault for secure key storage.

  • Audit Logging provides accountability. Logs must be tamper-proof.

  • PCI-DSS compliance is mandatory for payment processing.

  • Secure Coding Practices prevent common vulnerabilities.

 
Â