@Configuration
public class ApiSecurityConfig {
// 1. API ํค ๊ด๋ฆฌ
@Service
public class ApiKeyManager {
private final ApiKeyRepository apiKeyRepository;
private final RedisTemplate<String, ApiKeyDetails> redisTemplate;
public ApiKey generateApiKey(String clientId, ApiKeySpec spec) {
String apiKey = generateSecureRandomKey();
String hashedKey = hashApiKey(apiKey);
// API ํค ์ ์ฅ
ApiKey key = ApiKey.builder()
.clientId(clientId)
.hashedKey(hashedKey)
.permissions(spec.getPermissions())
.rateLimit(spec.getRateLimit())
.expiryDate(spec.getExpiryDate())
.build();
apiKeyRepository.save(key);
cacheApiKey(apiKey, key);
return key;
}
private void cacheApiKey(String apiKey, ApiKey details) {
String cacheKey = "api_key:" + hashApiKey(apiKey);
redisTemplate.opsForValue().set(
cacheKey,
details,
Duration.ofHours(24)
);
}
public ApiKeyDetails validateApiKey(String apiKey) {
String hashedKey = hashApiKey(apiKey);
ApiKeyDetails details = getFromCache(hashedKey);
if (details == null) {
details = apiKeyRepository.findByHashedKey(hashedKey)
.orElseThrow(() -> new InvalidApiKeyException(
"Invalid API key"));
cacheApiKey(apiKey, details);
}
if (isApiKeyExpired(details)) {
throw new ApiKeyExpiredException("API key expired");
}
return details;
}
}
// 2. ์์ฒญ ์ธ์ฆ ํํฐ
@Component
public class ApiAuthenticationFilter extends OncePerRequestFilter {
private final ApiKeyManager apiKeyManager;
private final RateLimiter rateLimiter;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
try {
String apiKey = extractApiKey(request);
ApiKeyDetails keyDetails = apiKeyManager.validateApiKey(apiKey);
// ๋น์จ ์ ํ ํ์ธ
if (!rateLimiter.tryAcquire(keyDetails.getClientId())) {
throw new RateLimitExceededException(
"Rate limit exceeded");
}
// ๋ณด์ ์ปจํ
์คํธ ์ค์
SecurityContextHolder.getContext()
.setAuthentication(createAuthentication(keyDetails));
// ์์ฒญ ๋ฉํ๋ฐ์ดํฐ ๊ธฐ๋ก
auditRequest(request, keyDetails);
} catch (ApiSecurityException e) {
handleSecurityException(response, e);
return;
}
chain.doFilter(request, response);
}
private String extractApiKey(HttpServletRequest request) {
String apiKey = request.getHeader("X-API-Key");
if (apiKey == null) {
apiKey = request.getParameter("api_key");
}
if (apiKey == null) {
throw new MissingApiKeyException("API key is required");
}
return apiKey;
}
}
}
@Service
public class ApiSecurityService {
// 1. ์์ฒญ ๊ฒ์ฆ
@Component
public class RequestValidator {
private final SignatureVerifier signatureVerifier;
private final RequestSanitizer requestSanitizer;
public void validateRequest(HttpServletRequest request) {
// 1. ์์ฒญ ์๋ช
๊ฒ์ฆ
verifyRequestSignature(request);
// 2. ์
๋ ฅ ๊ฐ ๊ฒ์ฆ ๋ฐ ์ด๊ท
sanitizeRequest(request);
// 3. ์์ฒญ ํ์์คํฌํ ๊ฒ์ฆ
validateTimestamp(request);
}
private void verifyRequestSignature(HttpServletRequest request) {
String signature = request.getHeader("X-Signature");
String timestamp = request.getHeader("X-Timestamp");
String payload = extractRequestPayload(request);
if (!signatureVerifier.verify(payload, signature, timestamp)) {
throw new InvalidSignatureException(
"Invalid request signature");
}
}
private void validateTimestamp(HttpServletRequest request) {
String timestamp = request.getHeader("X-Timestamp");
long requestTime = Long.parseLong(timestamp);
long currentTime = System.currentTimeMillis();
if (Math.abs(currentTime - requestTime) >
TimeUnit.MINUTES.toMillis(5)) {
throw new RequestTimestampException(
"Request timestamp too old");
}
}
}
// 2. ์
๋ ฅ ๊ฒ์ฆ ๋ฐ ์ด๊ท
@Component
public class RequestSanitizer {
public String sanitizeInput(String input) {
// XSS ๋ฐฉ์ง
input = stripXSS(input);
// SQL Injection ๋ฐฉ์ง
input = escapeSqlCharacters(input);
// ๋ฌธ์ ์ธ์ฝ๋ฉ ์ ๊ทํ
input = normalizeString(input);
return input;
}
private String stripXSS(String input) {
// HTML ํ๊ทธ ์ ๊ฑฐ
input = input.replaceAll("<[^>]*>", "");
// ์คํฌ๋ฆฝํธ ์ด๋ฒคํธ ์ ๊ฑฐ
input = input.replaceAll("javascript:", "");
input = input.replaceAll("on\\w+\\s*=", "");
return input;
}
private String escapeSqlCharacters(String input) {
return input.replace("'", "''")
.replace("\\", "\\\\")
.replace("%", "\\%")
.replace("_", "\\_");
}
}
}
## 3. API ๋ชจ๋ํฐ๋ง ๋ฐ ๋ณด์ ๊ฐ์ฌ
```java
@Service
public class ApiMonitoringService {
// 1. ์์ฒญ ๋ชจ๋ํฐ๋ง
@Component
public class RequestMonitor {
private final MeterRegistry meterRegistry;
private final AnomalyDetector anomalyDetector;
@Async
public void monitorRequest(
ApiRequest request,
ApiResponse response) {
// ์๋ต ์๊ฐ ๊ธฐ๋ก
recordResponseTime(request, response);
// ์ค๋ฅ์จ ๋ชจ๋ํฐ๋ง
if (response.isError()) {
recordError(request, response);
}
// ๋น์ ์ ํจํด ๊ฐ์ง
if (anomalyDetector.detectAnomaly(request)) {
handleAnomaly(request);
}
}
private void recordResponseTime(
ApiRequest request,
ApiResponse response) {
meterRegistry.timer("api.response.time",
"endpoint", request.getEndpoint(),
"client", request.getClientId())
.record(response.getResponseTime());
}
}
// 2. ๋ณด์ ๊ฐ์ฌ
@Service
public class SecurityAuditService {
private final AuditEventRepository auditRepository;
private final AlertService alertService;
public void auditSecurityEvent(SecurityEvent event) {
// ๊ฐ์ฌ ๋ก๊ทธ ์ ์ฅ
AuditLog log = AuditLog.builder()
.eventType(event.getType())
.clientId(event.getClientId())
.endpoint(event.getEndpoint())
.ipAddress(event.getIpAddress())
.timestamp(Instant.now())
.details(event.getDetails())
.build();
auditRepository.save(log);
// ์ฌ๊ฐ๋์ ๋ฐ๋ฅธ ์๋ฆผ
if (event.getSeverity().isHigh()) {
alertService.sendSecurityAlert(event);
}
}
@Scheduled(fixedRate = 300000) // 5๋ถ๋ง๋ค
public void analyzeSecurityPatterns() {
List<AuditLog> recentLogs =
auditRepository.findRecentLogs(Duration.ofMinutes(5));
SecurityAnalysis analysis =
analyzeSecurityPatterns(recentLogs);
if (analysis.hasThreats()) {
handleSecurityThreats(analysis.getThreats());
}
}
}
}
์ด๋ฌํ API ๋ณด์ ์์คํ ์ ํตํด:
-
์ธ์ฆ/์ธ๊ฐ
- API ํค ๊ด๋ฆฌ
- ์์ฒญ ๊ฒ์ฆ
- ์ ๊ทผ ์ ์ด
-
์์ฒญ ๋ณด์
- ์๋ช ๊ฒ์ฆ
- ์ ๋ ฅ ๊ฒ์ฆ
- XSS/SQL Injection ๋ฐฉ์ง
-
๋ชจ๋ํฐ๋ง/๊ฐ์ฌ
- ์ค์๊ฐ ๋ชจ๋ํฐ๋ง
- ๋ณด์ ๊ฐ์ฌ
- ์ํ ๊ฐ์ง
ํนํ ์ค์ํ ๋ณด์ ๊ณ ๋ ค์ฌํญ:
- API ํค ์์ ์ฑ
- ์์ฒญ ๋ฌด๊ฒฐ์ฑ
- ์ ๋ ฅ ๋ฐ์ดํฐ ๊ฒ์ฆ
- ์ด์ ํ์ ๊ฐ์ง
๋ฉด์ ๊ด: ์ค์ ์๋น์ค์์ API ๋ณด์์ ๊ตฌํํ ๋์ ์ฑ๋ฅ๊ณผ ๋ณด์ ์ฌ์ด์ ํธ๋ ์ด๋์คํ๋ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ์๊ฒ ์ต๋๊น?
@Service
public class ApiSecurityOptimizer {
// 1. ๋ค์ธต ์บ์ฑ ์ ๋ต
@Service
public class SecurityCacheManager {
private final LoadingCache<String, ApiKeyDetails> localCache;
private final RedisTemplate<String, ApiKeyDetails> redisCache;
public SecurityCacheManager() {
this.localCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(Duration.ofMinutes(5))
.recordStats() // ์บ์ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง
.build(key -> loadFromRedis(key));
}
public ApiKeyDetails getApiKeyDetails(String apiKey) {
String cacheKey = "api_key:" + hashApiKey(apiKey);
try {
// L1 ์บ์ (๋ก์ปฌ ๋ฉ๋ชจ๋ฆฌ)
return localCache.get(cacheKey);
} catch (Exception e) {
// L2 ์บ์ (Redis)
ApiKeyDetails details = redisCache.opsForValue().get(cacheKey);
if (details != null) {
localCache.put(cacheKey, details);
return details;
}
// DB ์กฐํ
return loadFromDatabase(apiKey);
}
}
// ์บ์ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง
@Scheduled(fixedRate = 60000)
public void monitorCachePerformance() {
CacheStats stats = localCache.stats();
// ํํธ์จ์ด ๋ฎ์ ๊ฒฝ์ฐ ์บ์ ํฌ๊ธฐ ์กฐ์
if (stats.hitRate() < 0.7) {
adjustCacheSize(stats);
}
}
}
// 2. ๋ณ๋ ฌ ์ฒ๋ฆฌ ์ต์ ํ
@Service
public class ParallelSecurityProcessor {
private final ExecutorService executorService;
public CompletableFuture<SecurityValidationResult> validateRequestAsync(
HttpServletRequest request) {
return CompletableFuture.supplyAsync(() -> {
List<CompletableFuture<ValidationResult>> futures = Arrays.asList(
validateSignature(request),
validateApiKey(request),
validateRateLimit(request)
);
return futures.stream()
.map(CompletableFuture::join)
.reduce(ValidationResult::combine)
.orElseThrow();
}, executorService);
}
}
}
@Service
public class OptimizedRequestProcessor {
// 1. ๊ฒฝ๋ํ๋ ๋ณด์ ์ฒดํฌ
@Component
public class LightweightSecurityChecker {
private final BloomFilter<String> blacklistedTokens;
private final RateLimiter rateLimiter;
public ValidationResult quickSecurityCheck(HttpServletRequest request) {
// ๋ธ๋ฃธ ํํฐ๋ฅผ ์ฌ์ฉํ ๋น ๋ฅธ ๋ธ๋๋ฆฌ์คํธ ์ฒดํฌ
String token = request.getHeader("Authorization");
if (blacklistedTokens.mightContain(token)) {
return ValidationResult.failed("Potentially blacklisted token");
}
// ํ ํฐ ๊ตฌ์กฐ ๋น ๋ฅธ ๊ฒ์ฆ
if (!isValidTokenFormat(token)) {
return ValidationResult.failed("Invalid token format");
}
return ValidationResult.success();
}
}
// 2. ์ฐ์ ์์ ๊ธฐ๋ฐ ์ฒ๋ฆฌ
@Component
public class PriorityRequestHandler {
public void processRequest(HttpServletRequest request) {
SecurityLevel securityLevel = determineSecurityLevel(request);
switch (securityLevel) {
case HIGH:
applyFullSecurityChecks(request);
break;
case MEDIUM:
applyStandardSecurityChecks(request);
break;
case LOW:
applyBasicSecurityChecks(request);
break;
}
}
private SecurityLevel determineSecurityLevel(
HttpServletRequest request) {
return SecurityLevel.valueOf(
Optional.ofNullable(request.getHeader("X-Security-Level"))
.orElse(DEFAULT_SECURITY_LEVEL)
);
}
}
}
@Service
public class ResourceOptimizer {
// 1. ๋์ ์ค๋ ๋ ํ ๊ด๋ฆฌ
@Component
public class DynamicThreadPoolManager {
private final ThreadPoolExecutor executor;
private final LoadMonitor loadMonitor;
@Scheduled(fixedRate = 1000)
public void adjustThreadPool() {
SystemLoad currentLoad = loadMonitor.getCurrentLoad();
int optimalThreads = calculateOptimalThreads(currentLoad);
executor.setCorePoolSize(optimalThreads);
executor.setMaximumPoolSize(optimalThreads * 2);
}
private int calculateOptimalThreads(SystemLoad load) {
int processors = Runtime.getRuntime().availableProcessors();
double loadFactor = Math.min(load.getCpuUsage(), 0.8);
return (int) (processors * loadFactor);
}
}
// 2. ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ ์ต์ ํ
@Component
public class MemoryOptimizer {
public void optimizeRequestProcessing(HttpServletRequest request) {
// ์์ฒญ ๋ฐ์ดํฐ ์คํธ๋ฆฌ๋ฐ ์ฒ๋ฆฌ
if (isLargePayload(request)) {
processLargePayloadInChunks(request);
} else {
processNormalPayload(request);
}
}
private void processLargePayloadInChunks(HttpServletRequest request) {
try (BufferedReader reader = request.getReader()) {
StringBuilder chunk = new StringBuilder();
char[] buffer = new char[8192];
int bytesRead;
while ((bytesRead = reader.read(buffer)) != -1) {
chunk.append(buffer, 0, bytesRead);
if (chunk.length() >= CHUNK_SIZE) {
processChunk(chunk.toString());
chunk.setLength(0);
}
}
if (chunk.length() > 0) {
processChunk(chunk.toString());
}
}
}
}
}
@Service
public class PerformanceMonitor {
// 1. ์ฑ๋ฅ ๋ฉํธ๋ฆญ ์์ง
@Component
public class SecurityMetricsCollector {
private final MeterRegistry registry;
public void recordSecurityCheck(String checkType, long duration) {
registry.timer("security.check.duration",
"type", checkType)
.record(duration, TimeUnit.MILLISECONDS);
}
@Scheduled(fixedRate = 60000)
public void analyzePerformance() {
Map<String, Double> averageDurations =
calculateAverageDurations();
// ์ฑ๋ฅ ์๊ณ๊ฐ ์ด๊ณผ ๊ฒ์ฌ
averageDurations.forEach((checkType, avgDuration) -> {
if (avgDuration > getThreshold(checkType)) {
optimizeSecurityCheck(checkType);
}
});
}
}
// 2. ์๋ ์ต์ ํ
@Service
public class AutoOptimizer {
public void optimizeBasedOnMetrics() {
SecurityMetrics metrics = collectCurrentMetrics();
// ์บ์ ์ค์ ์ต์ ํ
if (metrics.getCacheHitRate() < 0.8) {
adjustCacheSettings(metrics);
}
// ์ค๋ ๋ ํ ์ต์ ํ
if (metrics.getThreadPoolUtilization() > 0.9) {
adjustThreadPool(metrics);
}
// ํ์์์ ์ค์ ์ต์ ํ
if (metrics.getTimeoutRate() > 0.01) {
adjustTimeouts(metrics);
}
}
private void adjustCacheSettings(SecurityMetrics metrics) {
int optimalSize = calculateOptimalCacheSize(
metrics.getCacheStats());
cacheManager.resize(optimalSize);
}
}
}
์ด๋ฌํ ์ฑ๋ฅ ์ต์ ํ ์ ๋ต์ ํตํด:
-
์บ์ฑ ์ต์ ํ
- ๋ค์ธต ์บ์ฑ
- ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ
- ์บ์ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง
-
์ฒ๋ฆฌ ์ต์ ํ
- ๋ณ๋ ฌ ์ฒ๋ฆฌ
- ์ฐ์ ์์ ๊ธฐ๋ฐ ์ฒ๋ฆฌ
- ๊ฒฝ๋ํ๋ ๊ฒ์ฆ
-
๋ฆฌ์์ค ๊ด๋ฆฌ
- ๋์ ์ค๋ ๋ํ
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ ์ต์ ํ
- ์ฒญํฌ ์ฒ๋ฆฌ
-
๋ชจ๋ํฐ๋ง
- ์ค์๊ฐ ์ฑ๋ฅ ์ถ์
- ์๋ ์ต์ ํ
- ํผ๋๋ฐฑ ๋ฃจํ
์ด๋ฅผ ํตํด ๋ณด์์ฑ์ ์ ์งํ๋ฉด์๋ ๋์ ์ฑ๋ฅ์ ๋ฌ์ฑํ ์ ์์ต๋๋ค.