Exchange Security Engineer
Security specialist for the Exchange Platform - a financial application handling digital currency transactions.
Security Architecture
Authentication Flow
┌──────────┐ ┌─────────────┐ ┌────────────┐
│ Client │────▶│ /auth/* │────▶│ JWT Token │
│ │◀────│ endpoints │◀────│ (RS256) │
└──────────┘ └─────────────┘ └────────────┘
│ │
│ ┌────────────────────────────────┘
│ │ Access Token (short-lived)
│ │ Refresh Token (long-lived)
▼ ▼
┌─────────────────────────────────────────────┐
│ Protected Endpoints (/api/*) │
│ JWT Validation + Role Checks │
└─────────────────────────────────────────────┘
JWT Configuration
- Algorithm: RS256 (RSA asymmetric)
- Key Size: 2048 bits minimum
- Access Token TTL: 15-60 minutes
- Refresh Token TTL: 30 days
- Token Type: Bearer
Claims Structure
json1{ 2 "sub": "123", // User ID 3 "email": "user@test.com", 4 "nickname": "user1", // Optional 5 "roles": ["USER"], 6 "iat": 1708450000, 7 "exp": 1708453600, 8 "iss": "trenvus" 9}
JWT Key Management
Generating Keys
bash1# Generate private key 2openssl genrsa -out private.pem 2048 3 4# Extract public key 5openssl rsa -in private.pem -pubout -out public.pem 6 7# Base64 encode for env vars 8base64 -w 0 private.pem # JWT_PRIVATE_KEY_B64 9base64 -w 0 public.pem # JWT_PUBLIC_KEY_B64
Environment Variables
bash1JWT_PRIVATE_KEY_B64=<base64-encoded-private-key> 2JWT_PUBLIC_KEY_B64=<base64-encoded-public-key> 3JWT_ISSUER=trenvus 4JWT_ACCESS_TTL_SECONDS=3600 5JWT_REFRESH_TTL_SECONDS=2592000
Security Configuration
Public Endpoints (permitAll)
java1/auth/register // User registration 2/auth/login // User login 3/auth/test-login // Test account login 4/auth/admin-login // Admin login 5/auth/refresh // Token refresh 6/auth/logout // Logout 7/swagger-ui/** // API docs 8/v3/api-docs/** // OpenAPI spec
Protected Endpoints (authenticated)
java1/wallet // View wallet 2/wallet/deposit // Deposit funds 3/exchange/convert // Currency conversion 4/transfer/trv // P2P transfers 5/invoices/** // QR code payments 6/transactions/** // Transaction history 7/me/** // User profile
Admin Endpoints (ROLE_ADMIN)
java1/admin/users // List users 2/admin/users/{id}/wallet // Manage user wallets 3/admin/users/{id}/role // Change user roles
CORS Configuration
java1@Bean 2public CorsConfigurationSource corsConfigurationSource() { 3 var config = new CorsConfiguration(); 4 config.setAllowedOrigins(List.of( 5 "http://localhost:3000", 6 "http://localhost:5173", 7 "https://yourdomain.com" 8 )); 9 config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); 10 config.setAllowedHeaders(List.of("*")); 11 config.setAllowCredentials(true); 12 return new UrlBasedCorsConfigurationSource(); 13}
Password Security
Hashing
- Algorithm: BCrypt
- Strength: 10 (default)
- Library:
BCryptPasswordEncoder
java1@Bean 2public PasswordEncoder passwordEncoder() { 3 return new BCryptPasswordEncoder(); 4}
Storage
- Never store plaintext passwords
- Never store passwords in logs
- Password hash stored in
users.password_hash
Token Security
Refresh Token Best Practices
- Hash before storage - SHA-256
- One-time use - Rotate on refresh
- Revocation support - Store revocation timestamp
- Device binding - Optional IP/user-agent
Token Rotation
Client Server
│ Access (expired) │
│──────────────────▶│ 401 Unauthorized
│ │
│ Refresh Token │
│──────────────────▶│ Validate
│ │ Revoke old
│ New Access+Refresh│ Issue new
│◀──────────────────│
Authorization
Role-Based Access Control (RBAC)
java1public enum UserRole { 2 USER, // Standard user 3 ADMIN // Full admin access 4}
Method-Level Security
java1@PreAuthorize("hasRole('ADMIN')") 2@GetMapping("/admin/users") 3public List<User> listUsers() { ... }
Security Headers
Enable in Spring Security:
java1http.headers(headers -> headers 2 .frameOptions(HeadersConfigurer.FrameOptionsConfig::deny) 3 .xssProtection(HeadersConfigurer.XXssConfig::disable) 4 .contentSecurityPolicy(csp -> 5 csp.policyDirectives("default-src 'self'") 6 ) 7);
Common Vulnerabilities
Preventing
- SQL Injection - Use JPA/Hibernate (parameterized queries)
- XSS - Validate input, escape output
- CSRF - Disabled (stateless JWT)
- IDOR - Verify resource ownership
- Race Conditions - Optimistic locking on wallets
Input Validation
java1@NotBlank @Email String email, 2@NotBlank @Size(min=6) String password, 3@Positive BigDecimal amount
Security Checklist
- JWT keys are RSA 2048+ bits
- Keys rotated regularly
- Passwords hashed with BCrypt
- CORS origins restricted
- No sensitive data in logs
- Rate limiting enabled
- HTTPS in production
- Security headers configured
- Input validation on all endpoints
- Authorization checks on admin endpoints
Testing Security
bash1# Check JWT signature 2curl -H "Authorization: Bearer TOKEN" http://localhost:8080/me 3 4# Test CORS 5curl -H "Origin: http://evil.com" http://localhost:8080/auth/login 6 7# SQL injection attempt 8curl -X POST http://localhost:8080/auth/login \ 9 -d '{"email":"test@test.com\' OR 1=1--","password":"test"}'