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
-
Defense in Depth:Â Multiple layers of security.
-
Least Privilege:Â Minimal access required.
-
Zero Trust:Â Never trust, always verify.
-
Secure by Design:Â Security built into the system.
-
Fail Securely:Â Fail to a secure state.
3. CRYPTOGRAPHY – ENCRYPTION AND HASHING
3.1 Symmetric Encryption (AES)
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)
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)
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
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
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
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
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)
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
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:
-
Install and maintain firewalls.
-
Do not use vendor-supplied defaults.
-
Protect stored cardholder data.
-
Encrypt transmission of cardholder data.
-
Protect all systems against malware.
-
Develop and maintain secure systems.
-
Restrict access to cardholder data.
-
Identify and authenticate access.
-
Restrict physical access.
-
Monitor and log all access.
-
Regularly test security systems.
-
Maintain an information security policy.
7.2 Tokenization Implementation
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
# 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.