Initial commit: Complete Software Architecture course with 11 modules
- Added comprehensive course structure with 11 modules - Included theoretical content, practical examples, and tasks - Multi-language code examples (Java, Rust, Node.js, Python) - Detailed analysis of architectural approaches and trade-offs - Complete caching strategies documentation - Architecture examples from real-world systems (Twitter, Netflix, Uber) - Structured navigation and learning path - Ready for educational use
This commit is contained in:
commit
15e744da49
107 files changed
+61762
No files matched your search
@@ -0,0 +1,723 @@
|
||||
# Уязвимости и межсервисное взаимодействие
|
||||
|
||||
## Навигация
|
||||
- [🏠 Главная страница курса](../../../README.md)
|
||||
- [📚 Модуль 6](../README.md)
|
||||
|
||||
---
|
||||
|
||||
## Типы уязвимостей в микросервисной архитектуре
|
||||
|
||||
### 1. Уязвимости межсервисного взаимодействия
|
||||
|
||||
**Проблемы:**
|
||||
- Отсутствие аутентификации между сервисами
|
||||
- Передача чувствительных данных в открытом виде
|
||||
- Отсутствие валидации токенов
|
||||
- Man-in-the-middle атаки
|
||||
- Replay атаки
|
||||
|
||||
**Решения:**
|
||||
- Mutual TLS (mTLS)
|
||||
- Service-to-Service токены
|
||||
- API Gateway с аутентификацией
|
||||
- Token Validation Middleware (TVM)
|
||||
|
||||
### 2. TVM (Token Validation Middleware)
|
||||
|
||||
**TVM** — промежуточное ПО для валидации токенов между микросервисами.
|
||||
|
||||
**Принципы работы:**
|
||||
1. Сервис получает токен от клиента
|
||||
2. TVM валидирует токен
|
||||
3. TVM извлекает информацию о пользователе/сервисе
|
||||
4. TVM передает валидные данные в целевой сервис
|
||||
|
||||
**Технический пример - TVM Implementation:**
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class TokenValidationMiddleware {
|
||||
|
||||
@Autowired
|
||||
private JwtTokenService jwtTokenService;
|
||||
|
||||
@Autowired
|
||||
private ServiceRegistry serviceRegistry;
|
||||
|
||||
public ServiceTokenValidationResult validateServiceToken(String token, String targetService) {
|
||||
try {
|
||||
// Валидация токена
|
||||
Claims claims = jwtTokenService.validateToken(token);
|
||||
|
||||
// Проверка прав доступа к сервису
|
||||
if (!hasAccessToService(claims, targetService)) {
|
||||
return ServiceTokenValidationResult.denied("No access to service: " + targetService);
|
||||
}
|
||||
|
||||
// Проверка срока действия токена
|
||||
if (isTokenExpired(claims)) {
|
||||
return ServiceTokenValidationResult.denied("Token expired");
|
||||
}
|
||||
|
||||
// Извлечение информации о сервисе
|
||||
ServiceInfo serviceInfo = extractServiceInfo(claims);
|
||||
|
||||
return ServiceTokenValidationResult.success(serviceInfo);
|
||||
|
||||
} catch (JwtException e) {
|
||||
return ServiceTokenValidationResult.denied("Invalid token: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasAccessToService(Claims claims, String targetService) {
|
||||
List<String> allowedServices = claims.get("allowed_services", List.class);
|
||||
return allowedServices != null && allowedServices.contains(targetService);
|
||||
}
|
||||
|
||||
private boolean isTokenExpired(Claims claims) {
|
||||
Date expiration = claims.getExpiration();
|
||||
return expiration.before(new Date());
|
||||
}
|
||||
|
||||
private ServiceInfo extractServiceInfo(Claims claims) {
|
||||
return ServiceInfo.builder()
|
||||
.serviceId(claims.getSubject())
|
||||
.serviceName(claims.get("service_name", String.class))
|
||||
.permissions(claims.get("permissions", List.class))
|
||||
.issuedAt(claims.getIssuedAt())
|
||||
.expiresAt(claims.getExpiration())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
// Service Token Validation Result
|
||||
@Data
|
||||
@Builder
|
||||
public class ServiceTokenValidationResult {
|
||||
private boolean valid;
|
||||
private String errorMessage;
|
||||
private ServiceInfo serviceInfo;
|
||||
|
||||
public static ServiceTokenValidationResult success(ServiceInfo serviceInfo) {
|
||||
return ServiceTokenValidationResult.builder()
|
||||
.valid(true)
|
||||
.serviceInfo(serviceInfo)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static ServiceTokenValidationResult denied(String errorMessage) {
|
||||
return ServiceTokenValidationResult.builder()
|
||||
.valid(false)
|
||||
.errorMessage(errorMessage)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
// Service Info
|
||||
@Data
|
||||
@Builder
|
||||
public class ServiceInfo {
|
||||
private String serviceId;
|
||||
private String serviceName;
|
||||
private List<String> permissions;
|
||||
private Date issuedAt;
|
||||
private Date expiresAt;
|
||||
}
|
||||
```
|
||||
|
||||
**Rust примеры - TVM с Actix Web:**
|
||||
|
||||
```rust
|
||||
// Token Validation Middleware для Actix Web
|
||||
use actix_web::{web, HttpRequest, HttpResponse, Error, middleware::Next};
|
||||
use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct ServiceClaims {
|
||||
sub: String, // service_id
|
||||
service_name: String,
|
||||
allowed_services: Vec<String>,
|
||||
permissions: Vec<String>,
|
||||
iat: u64, // issued_at
|
||||
exp: u64, // expires_at
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServiceInfo {
|
||||
pub service_id: String,
|
||||
pub service_name: String,
|
||||
pub permissions: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct TokenValidationMiddleware {
|
||||
decoding_key: DecodingKey,
|
||||
validation: Validation,
|
||||
}
|
||||
|
||||
impl TokenValidationMiddleware {
|
||||
pub fn new(secret: &str) -> Self {
|
||||
let mut validation = Validation::new(Algorithm::HS256);
|
||||
validation.set_issuer(&["microservices-platform"]);
|
||||
|
||||
Self {
|
||||
decoding_key: DecodingKey::from_secret(secret.as_ref()),
|
||||
validation,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_service_token(
|
||||
&self,
|
||||
token: &str,
|
||||
target_service: &str,
|
||||
) -> Result<ServiceInfo, String> {
|
||||
// Декодирование токена
|
||||
let token_data = decode::<ServiceClaims>(token, &self.decoding_key, &self.validation)
|
||||
.map_err(|e| format!("Token validation failed: {}", e))?;
|
||||
|
||||
let claims = token_data.claims;
|
||||
|
||||
// Проверка срока действия
|
||||
let now = chrono::Utc::now().timestamp() as u64;
|
||||
if claims.exp < now {
|
||||
return Err("Token expired".to_string());
|
||||
}
|
||||
|
||||
// Проверка прав доступа к сервису
|
||||
if !claims.allowed_services.contains(&target_service.to_string()) {
|
||||
return Err(format!("No access to service: {}", target_service));
|
||||
}
|
||||
|
||||
Ok(ServiceInfo {
|
||||
service_id: claims.sub,
|
||||
service_name: claims.service_name,
|
||||
permissions: claims.permissions,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware для Actix Web
|
||||
pub async fn token_validation_middleware(
|
||||
req: HttpRequest,
|
||||
next: Next,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
// Извлечение токена из заголовка
|
||||
let token = req.headers()
|
||||
.get("Authorization")
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.and_then(|s| s.strip_prefix("Bearer "));
|
||||
|
||||
match token {
|
||||
Some(token) => {
|
||||
// Валидация токена
|
||||
let middleware = TokenValidationMiddleware::new("your-secret-key");
|
||||
let target_service = req.path().split('/').nth(1).unwrap_or("unknown");
|
||||
|
||||
match middleware.validate_service_token(token, target_service) {
|
||||
Ok(service_info) => {
|
||||
// Добавление информации о сервисе в контекст запроса
|
||||
req.extensions_mut().insert(service_info);
|
||||
next.run(req).await
|
||||
}
|
||||
Err(e) => Ok(HttpResponse::Unauthorized()
|
||||
.json(serde_json::json!({
|
||||
"error": "Token validation failed",
|
||||
"message": e
|
||||
})))
|
||||
}
|
||||
}
|
||||
None => Ok(HttpResponse::Unauthorized()
|
||||
.json(serde_json::json!({
|
||||
"error": "Missing authorization token"
|
||||
})))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Python примеры - TVM с FastAPI:**
|
||||
|
||||
```python
|
||||
# Token Validation Middleware для FastAPI
|
||||
from fastapi import FastAPI, HTTPException, Depends, Request
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from jose import JWTError, jwt
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional, List
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ServiceClaims(BaseModel):
|
||||
sub: str
|
||||
service_name: str
|
||||
allowed_services: List[str]
|
||||
permissions: List[str]
|
||||
iat: int
|
||||
exp: int
|
||||
|
||||
class ServiceInfo(BaseModel):
|
||||
service_id: str
|
||||
service_name: str
|
||||
permissions: List[str]
|
||||
|
||||
class TokenValidationMiddleware:
|
||||
def __init__(self, secret_key: str):
|
||||
self.secret_key = secret_key
|
||||
self.algorithm = "HS256"
|
||||
|
||||
def validate_service_token(self, token: str, target_service: str) -> ServiceInfo:
|
||||
try:
|
||||
# Декодирование токена
|
||||
payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
|
||||
|
||||
# Проверка срока действия
|
||||
if datetime.utcnow().timestamp() > payload.get("exp", 0):
|
||||
raise HTTPException(status_code=401, detail="Token expired")
|
||||
|
||||
# Проверка прав доступа к сервису
|
||||
allowed_services = payload.get("allowed_services", [])
|
||||
if target_service not in allowed_services:
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail=f"No access to service: {target_service}"
|
||||
)
|
||||
|
||||
return ServiceInfo(
|
||||
service_id=payload.get("sub"),
|
||||
service_name=payload.get("service_name"),
|
||||
permissions=payload.get("permissions", [])
|
||||
)
|
||||
|
||||
except JWTError as e:
|
||||
raise HTTPException(status_code=401, detail=f"Invalid token: {str(e)}")
|
||||
|
||||
# Dependency для FastAPI
|
||||
security = HTTPBearer()
|
||||
middleware = TokenValidationMiddleware("your-secret-key")
|
||||
|
||||
async def get_service_info(
|
||||
credentials: HTTPAuthorizationCredentials = Depends(security),
|
||||
request: Request = None
|
||||
) -> ServiceInfo:
|
||||
token = credentials.credentials
|
||||
target_service = request.url.path.split('/')[1] if request else "unknown"
|
||||
|
||||
return middleware.validate_service_token(token, target_service)
|
||||
|
||||
# Использование в FastAPI
|
||||
app = FastAPI()
|
||||
|
||||
@app.get("/api/orders")
|
||||
async def get_orders(service_info: ServiceInfo = Depends(get_service_info)):
|
||||
# Проверка прав доступа
|
||||
if "read:orders" not in service_info.permissions:
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
|
||||
return {"message": "Orders retrieved", "service": service_info.service_name}
|
||||
```
|
||||
|
||||
**Node.js примеры - TVM с Express:**
|
||||
|
||||
```typescript
|
||||
// Token Validation Middleware для Express
|
||||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { Request as JWTRequest } from 'express-jwt';
|
||||
|
||||
interface ServiceClaims {
|
||||
sub: string;
|
||||
service_name: string;
|
||||
allowed_services: string[];
|
||||
permissions: string[];
|
||||
iat: number;
|
||||
exp: number;
|
||||
}
|
||||
|
||||
interface ServiceInfo {
|
||||
service_id: string;
|
||||
service_name: string;
|
||||
permissions: string[];
|
||||
}
|
||||
|
||||
class TokenValidationMiddleware {
|
||||
private secretKey: string;
|
||||
private algorithm: string = 'HS256';
|
||||
|
||||
constructor(secretKey: string) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
validateServiceToken(token: string, targetService: string): ServiceInfo {
|
||||
try {
|
||||
// Декодирование токена
|
||||
const payload = jwt.verify(token, this.secretKey, {
|
||||
algorithms: [this.algorithm]
|
||||
}) as ServiceClaims;
|
||||
|
||||
// Проверка срока действия
|
||||
if (Date.now() / 1000 > payload.exp) {
|
||||
throw new Error('Token expired');
|
||||
}
|
||||
|
||||
// Проверка прав доступа к сервису
|
||||
if (!payload.allowed_services.includes(targetService)) {
|
||||
throw new Error(`No access to service: ${targetService}`);
|
||||
}
|
||||
|
||||
return {
|
||||
service_id: payload.sub,
|
||||
service_name: payload.service_name,
|
||||
permissions: payload.permissions
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(`Token validation failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware для Express
|
||||
const middleware = new TokenValidationMiddleware('your-secret-key');
|
||||
|
||||
export const tokenValidationMiddleware = (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.status(401).json({ error: 'Missing authorization token' });
|
||||
}
|
||||
|
||||
const token = authHeader.substring(7);
|
||||
const targetService = req.path.split('/')[2] || 'unknown';
|
||||
|
||||
const serviceInfo = middleware.validateServiceToken(token, targetService);
|
||||
|
||||
// Добавление информации о сервисе в контекст запроса
|
||||
(req as any).serviceInfo = serviceInfo;
|
||||
|
||||
next();
|
||||
} catch (error) {
|
||||
res.status(401).json({
|
||||
error: 'Token validation failed',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Использование в Express
|
||||
app.use('/api', tokenValidationMiddleware);
|
||||
|
||||
app.get('/api/orders', (req: Request, res: Response) => {
|
||||
const serviceInfo = (req as any).serviceInfo;
|
||||
|
||||
// Проверка прав доступа
|
||||
if (!serviceInfo.permissions.includes('read:orders')) {
|
||||
return res.status(403).json({ error: 'Insufficient permissions' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
message: 'Orders retrieved',
|
||||
service: serviceInfo.service_name
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Mutual TLS (mTLS)
|
||||
|
||||
**mTLS** обеспечивает взаимную аутентификацию между сервисами.
|
||||
|
||||
**Java пример - Spring Boot с mTLS:**
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class MTlsSecurityConfig {
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
return http
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/api/public/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.x509(x509 -> x509
|
||||
.subjectPrincipalRegex("CN=(.*?)(?:,|$)")
|
||||
.userDetailsService(userDetailsService())
|
||||
)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UserDetailsService userDetailsService() {
|
||||
return username -> {
|
||||
// Валидация сертификата клиента
|
||||
if (isValidServiceCertificate(username)) {
|
||||
return User.builder()
|
||||
.username(username)
|
||||
.password("")
|
||||
.authorities("ROLE_SERVICE")
|
||||
.build();
|
||||
}
|
||||
throw new UsernameNotFoundException("Invalid service certificate");
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isValidServiceCertificate(String subject) {
|
||||
// Логика валидации сертификата
|
||||
return subject.startsWith("service-");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Service Mesh Security
|
||||
|
||||
**Istio Security Configuration:**
|
||||
|
||||
```yaml
|
||||
# PeerAuthentication для mTLS
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: default
|
||||
namespace: production
|
||||
spec:
|
||||
mtls:
|
||||
mode: STRICT
|
||||
|
||||
# AuthorizationPolicy для контроля доступа
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: AuthorizationPolicy
|
||||
metadata:
|
||||
name: service-access
|
||||
namespace: production
|
||||
spec:
|
||||
rules:
|
||||
- from:
|
||||
- source:
|
||||
principals: ["cluster.local/ns/production/sa/order-service"]
|
||||
to:
|
||||
- operation:
|
||||
methods: ["GET", "POST"]
|
||||
paths: ["/api/orders/*"]
|
||||
```
|
||||
|
||||
### 5. API Gateway Security
|
||||
|
||||
**Kong Gateway Configuration:**
|
||||
|
||||
```yaml
|
||||
# Kong API Gateway с JWT аутентификацией
|
||||
apiVersion: configuration.konghq.com/v1
|
||||
kind: KongPlugin
|
||||
metadata:
|
||||
name: jwt-auth
|
||||
config:
|
||||
key_claim_name: iss
|
||||
secret_is_base64: false
|
||||
run_on_preflight: true
|
||||
---
|
||||
apiVersion: configuration.konghq.com/v1
|
||||
kind: KongIngress
|
||||
metadata:
|
||||
name: api-ingress
|
||||
upstream:
|
||||
healthchecks:
|
||||
active:
|
||||
healthy:
|
||||
interval: 30
|
||||
successes: 3
|
||||
unhealthy:
|
||||
interval: 10
|
||||
http_failures: 3
|
||||
```
|
||||
|
||||
### 6. Security Headers для микросервисов
|
||||
|
||||
**Java пример - Security Headers:**
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class SecurityHeadersFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
|
||||
// Security Headers
|
||||
httpResponse.setHeader("X-Content-Type-Options", "nosniff");
|
||||
httpResponse.setHeader("X-Frame-Options", "DENY");
|
||||
httpResponse.setHeader("X-XSS-Protection", "1; mode=block");
|
||||
httpResponse.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
|
||||
httpResponse.setHeader("Content-Security-Policy", "default-src 'self'");
|
||||
httpResponse.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
|
||||
httpResponse.setHeader("Permissions-Policy", "geolocation=(), microphone=(), camera=()");
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Rate Limiting для межсервисного взаимодействия
|
||||
|
||||
**Redis-based Rate Limiting:**
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class InterServiceRateLimitingService {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
public boolean isAllowed(String serviceId, String targetService, int maxRequests, Duration window) {
|
||||
String key = String.format("rate_limit:%s:%s", serviceId, targetService);
|
||||
|
||||
// Sliding window rate limiting
|
||||
long now = System.currentTimeMillis();
|
||||
long windowStart = now - window.toMillis();
|
||||
|
||||
// Удаление старых записей
|
||||
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
|
||||
|
||||
// Подсчет текущих запросов
|
||||
Long currentCount = redisTemplate.opsForZSet().count(key, windowStart, now);
|
||||
|
||||
if (currentCount >= maxRequests) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Добавление нового запроса
|
||||
redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now);
|
||||
redisTemplate.expire(key, window);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Audit Logging для межсервисного взаимодействия
|
||||
|
||||
**Structured Audit Logging:**
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class InterServiceAuditLogger {
|
||||
|
||||
private static final Logger auditLogger = LoggerFactory.getLogger("AUDIT");
|
||||
|
||||
public void logServiceCall(String sourceService, String targetService,
|
||||
String operation, boolean success, String details) {
|
||||
|
||||
Map<String, Object> auditData = Map.of(
|
||||
"timestamp", Instant.now().toString(),
|
||||
"source_service", sourceService,
|
||||
"target_service", targetService,
|
||||
"operation", operation,
|
||||
"success", success,
|
||||
"details", details,
|
||||
"correlation_id", MDC.get("correlationId")
|
||||
);
|
||||
|
||||
auditLogger.info("Inter-service call: {}", auditData);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 9. Circuit Breaker для безопасности
|
||||
|
||||
**Resilience4j Circuit Breaker:**
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class SecureServiceClient {
|
||||
|
||||
private final CircuitBreaker circuitBreaker;
|
||||
private final Retry retry;
|
||||
|
||||
public SecureServiceClient() {
|
||||
this.circuitBreaker = CircuitBreaker.ofDefaults("secure-service");
|
||||
this.retry = Retry.ofDefaults("secure-service");
|
||||
}
|
||||
|
||||
@CircuitBreaker(name = "secure-service")
|
||||
@Retry(name = "secure-service")
|
||||
public ServiceResponse callSecureService(String serviceUrl, String token) {
|
||||
try {
|
||||
// Вызов защищенного сервиса
|
||||
return restTemplate.exchange(
|
||||
serviceUrl,
|
||||
HttpMethod.GET,
|
||||
createSecureRequest(token),
|
||||
ServiceResponse.class
|
||||
).getBody();
|
||||
} catch (Exception e) {
|
||||
// Логирование безопасности
|
||||
securityLogger.warn("Secure service call failed: {}", e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private HttpEntity<?> createSecureRequest(String token) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setBearerAuth(token);
|
||||
headers.set("X-Service-Name", "order-service");
|
||||
headers.set("X-Request-ID", UUID.randomUUID().toString());
|
||||
|
||||
return new HttpEntity<>(headers);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 10. Security Monitoring и Alerting
|
||||
|
||||
**Security Metrics:**
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class SecurityMetrics {
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final Counter authenticationFailures;
|
||||
private final Counter authorizationFailures;
|
||||
private final Timer serviceCallDuration;
|
||||
|
||||
public SecurityMetrics(MeterRegistry meterRegistry) {
|
||||
this.meterRegistry = meterRegistry;
|
||||
this.authenticationFailures = Counter.builder("security.auth.failures")
|
||||
.description("Authentication failures")
|
||||
.register(meterRegistry);
|
||||
this.authorizationFailures = Counter.builder("security.authz.failures")
|
||||
.description("Authorization failures")
|
||||
.register(meterRegistry);
|
||||
this.serviceCallDuration = Timer.builder("security.service.call.duration")
|
||||
.description("Secure service call duration")
|
||||
.register(meterRegistry);
|
||||
}
|
||||
|
||||
public void recordAuthenticationFailure(String reason) {
|
||||
authenticationFailures.increment(Tags.of("reason", reason));
|
||||
}
|
||||
|
||||
public void recordAuthorizationFailure(String resource) {
|
||||
authorizationFailures.increment(Tags.of("resource", resource));
|
||||
}
|
||||
|
||||
public Timer.Sample startServiceCallTimer() {
|
||||
return Timer.start(meterRegistry);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Заключение
|
||||
|
||||
Безопасность межсервисного взаимодействия требует комплексного подхода:
|
||||
|
||||
1. **Аутентификация** - проверка подлинности сервисов
|
||||
2. **Авторизация** - контроль доступа к ресурсам
|
||||
3. **Шифрование** - защита данных в транзите
|
||||
4. **Мониторинг** - отслеживание подозрительной активности
|
||||
5. **Аудит** - логирование всех операций
|
||||
6. **Rate Limiting** - защита от злоупотреблений
|
||||
7. **Circuit Breaker** - устойчивость к сбоям
|
||||
|
||||
TVM и mTLS являются ключевыми технологиями для обеспечения безопасности в микросервисной архитектуре.
|
||||
@@ -0,0 +1,766 @@
|
||||
# Шифрование и управление ключами
|
||||
|
||||
## Навигация
|
||||
- [🏠 Главная страница курса](../../../README.md)
|
||||
- [📚 Модуль 6](../README.md)
|
||||
|
||||
---
|
||||
|
||||
## Типы шифрования
|
||||
|
||||
### 1. Symmetric Encryption (Симметричное шифрование)
|
||||
|
||||
**Симметричное шифрование** использует один ключ для шифрования и расшифровки данных.
|
||||
|
||||
**Преимущества:**
|
||||
- Быстрое выполнение
|
||||
- Подходит для больших объемов данных
|
||||
- Простота реализации
|
||||
|
||||
**Недостатки:**
|
||||
- Проблема распределения ключей
|
||||
- Сложность масштабирования
|
||||
|
||||
**Технический пример - Advanced Encryption Service:**
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class SymmetricEncryptionService {
|
||||
|
||||
private final SecretKey secretKey;
|
||||
private final String algorithm = "AES/GCM/NoPadding";
|
||||
private final int GCM_IV_LENGTH = 12;
|
||||
private final int GCM_TAG_LENGTH = 16;
|
||||
|
||||
@Value("${encryption.key}")
|
||||
private String base64Key;
|
||||
|
||||
public SymmetricEncryptionService() throws Exception {
|
||||
// Генерируем ключ из конфигурации или создаем новый
|
||||
if (base64Key != null && !base64Key.isEmpty()) {
|
||||
byte[] keyBytes = Base64.getDecoder().decode(base64Key);
|
||||
this.secretKey = new SecretKeySpec(keyBytes, "AES");
|
||||
} else {
|
||||
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
|
||||
keyGenerator.init(256);
|
||||
this.secretKey = keyGenerator.generateKey();
|
||||
}
|
||||
}
|
||||
|
||||
public EncryptedData encrypt(String plainText) throws Exception {
|
||||
if (plainText == null || plainText.isEmpty()) {
|
||||
throw new IllegalArgumentException("Plain text cannot be null or empty");
|
||||
}
|
||||
|
||||
// Генерируем случайный IV
|
||||
byte[] iv = new byte[GCM_IV_LENGTH];
|
||||
SecureRandom.getInstanceStrong().nextBytes(iv);
|
||||
|
||||
// Настраиваем шифр
|
||||
Cipher cipher = Cipher.getInstance(algorithm);
|
||||
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
|
||||
|
||||
// Шифруем данные
|
||||
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// Объединяем IV и зашифрованные данные
|
||||
byte[] encryptedWithIv = new byte[GCM_IV_LENGTH + encryptedBytes.length];
|
||||
System.arraycopy(iv, 0, encryptedWithIv, 0, GCM_IV_LENGTH);
|
||||
System.arraycopy(encryptedBytes, 0, encryptedWithIv, GCM_IV_LENGTH, encryptedBytes.length);
|
||||
|
||||
return new EncryptedData(
|
||||
Base64.getEncoder().encodeToString(encryptedWithIv),
|
||||
System.currentTimeMillis()
|
||||
);
|
||||
}
|
||||
|
||||
public String decrypt(EncryptedData encryptedData) throws Exception {
|
||||
if (encryptedData == null || encryptedData.getData() == null) {
|
||||
throw new IllegalArgumentException("Encrypted data cannot be null");
|
||||
}
|
||||
|
||||
byte[] encryptedWithIv = Base64.getDecoder().decode(encryptedData.getData());
|
||||
|
||||
if (encryptedWithIv.length < GCM_IV_LENGTH) {
|
||||
throw new IllegalArgumentException("Invalid encrypted data length");
|
||||
}
|
||||
|
||||
// Извлекаем IV
|
||||
byte[] iv = new byte[GCM_IV_LENGTH];
|
||||
System.arraycopy(encryptedWithIv, 0, iv, 0, GCM_IV_LENGTH);
|
||||
|
||||
// Извлекаем зашифрованные данные
|
||||
byte[] encryptedBytes = new byte[encryptedWithIv.length - GCM_IV_LENGTH];
|
||||
System.arraycopy(encryptedWithIv, GCM_IV_LENGTH, encryptedBytes, 0, encryptedBytes.length);
|
||||
|
||||
// Настраиваем шифр для расшифровки
|
||||
Cipher cipher = Cipher.getInstance(algorithm);
|
||||
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
|
||||
|
||||
// Расшифровываем данные
|
||||
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
|
||||
return new String(decryptedBytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public String encryptSensitiveField(String value) throws Exception {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return encrypt(value).getData();
|
||||
}
|
||||
|
||||
public String decryptSensitiveField(String encryptedValue) throws Exception {
|
||||
if (encryptedValue == null) {
|
||||
return null;
|
||||
}
|
||||
return decrypt(new EncryptedData(encryptedValue, 0L));
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypted Data Wrapper
|
||||
public class EncryptedData {
|
||||
private final String data;
|
||||
private final long timestamp;
|
||||
|
||||
public EncryptedData(String data, long timestamp) {
|
||||
this.data = data;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// Field-level Encryption
|
||||
@Entity
|
||||
public class User {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@Convert(converter = EncryptedStringConverter.class)
|
||||
private String email;
|
||||
|
||||
@Convert(converter = EncryptedStringConverter.class)
|
||||
private String phoneNumber;
|
||||
|
||||
@Convert(converter = EncryptedStringConverter.class)
|
||||
private String ssn;
|
||||
|
||||
// Getters and setters
|
||||
}
|
||||
|
||||
// Custom Converter for JPA
|
||||
@Converter
|
||||
public class EncryptedStringConverter implements AttributeConverter<String, String> {
|
||||
|
||||
@Autowired
|
||||
private SymmetricEncryptionService encryptionService;
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(String attribute) {
|
||||
if (attribute == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return encryptionService.encryptSensitiveField(attribute);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to encrypt field", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToEntityAttribute(String dbData) {
|
||||
if (dbData == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return encryptionService.decryptSensitiveField(dbData);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to decrypt field", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Rust примеры - Симметричное шифрование:**
|
||||
|
||||
```rust
|
||||
use aes_gcm::{Aes256Gcm, Key, Nonce};
|
||||
use aes_gcm::aead::{Aead, NewAead};
|
||||
use rand::Rng;
|
||||
|
||||
pub struct SymmetricEncryptionService {
|
||||
cipher: Aes256Gcm,
|
||||
}
|
||||
|
||||
impl SymmetricEncryptionService {
|
||||
pub fn new(key: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let key = Key::from_slice(key);
|
||||
let cipher = Aes256Gcm::new(key);
|
||||
|
||||
Ok(Self { cipher })
|
||||
}
|
||||
|
||||
pub fn encrypt(&self, plaintext: &str) -> Result<EncryptedData, Box<dyn std::error::Error>> {
|
||||
let nonce = self.generate_nonce();
|
||||
let ciphertext = self.cipher.encrypt(&nonce, plaintext.as_bytes())?;
|
||||
|
||||
Ok(EncryptedData {
|
||||
data: base64::encode(ciphertext),
|
||||
nonce: base64::encode(nonce),
|
||||
timestamp: std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)?
|
||||
.as_secs(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn decrypt(&self, encrypted_data: &EncryptedData) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let nonce = base64::decode(&encrypted_data.nonce)?;
|
||||
let ciphertext = base64::decode(&encrypted_data.data)?;
|
||||
|
||||
let plaintext = self.cipher.decrypt(&nonce.into(), ciphertext.as_ref())?;
|
||||
Ok(String::from_utf8(plaintext)?)
|
||||
}
|
||||
|
||||
fn generate_nonce(&self) -> Nonce {
|
||||
let mut nonce = [0u8; 12];
|
||||
rand::thread_rng().fill(&mut nonce);
|
||||
nonce.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncryptedData {
|
||||
pub data: String,
|
||||
pub nonce: String,
|
||||
pub timestamp: u64,
|
||||
}
|
||||
```
|
||||
|
||||
**Python примеры - Симметричное шифрование:**
|
||||
|
||||
```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 SymmetricEncryptionService:
|
||||
def __init__(self, password: str):
|
||||
# Генерируем ключ из пароля
|
||||
salt = os.urandom(16)
|
||||
kdf = PBKDF2HMAC(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=32,
|
||||
salt=salt,
|
||||
iterations=100000,
|
||||
)
|
||||
key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
|
||||
self.cipher = Fernet(key)
|
||||
|
||||
def encrypt(self, plaintext: str) -> EncryptedData:
|
||||
if not plaintext:
|
||||
raise ValueError("Plaintext cannot be empty")
|
||||
|
||||
encrypted_data = self.cipher.encrypt(plaintext.encode())
|
||||
|
||||
return EncryptedData(
|
||||
data=base64.b64encode(encrypted_data).decode(),
|
||||
timestamp=int(time.time())
|
||||
)
|
||||
|
||||
def decrypt(self, encrypted_data: EncryptedData) -> str:
|
||||
if not encrypted_data.data:
|
||||
raise ValueError("Encrypted data cannot be empty")
|
||||
|
||||
encrypted_bytes = base64.b64decode(encrypted_data.data)
|
||||
decrypted_bytes = self.cipher.decrypt(encrypted_bytes)
|
||||
|
||||
return decrypted_bytes.decode()
|
||||
|
||||
@dataclass
|
||||
class EncryptedData:
|
||||
data: str
|
||||
timestamp: int
|
||||
```
|
||||
|
||||
**Node.js примеры - Симметричное шифрование:**
|
||||
|
||||
```typescript
|
||||
import crypto from 'crypto';
|
||||
|
||||
interface EncryptedData {
|
||||
data: string;
|
||||
iv: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
class SymmetricEncryptionService {
|
||||
private algorithm = 'aes-256-gcm';
|
||||
private keyLength = 32;
|
||||
private ivLength = 16;
|
||||
private tagLength = 16;
|
||||
|
||||
constructor(private key: Buffer) {
|
||||
if (key.length !== this.keyLength) {
|
||||
throw new Error(`Key must be ${this.keyLength} bytes long`);
|
||||
}
|
||||
}
|
||||
|
||||
encrypt(plaintext: string): EncryptedData {
|
||||
if (!plaintext) {
|
||||
throw new Error('Plaintext cannot be empty');
|
||||
}
|
||||
|
||||
const iv = crypto.randomBytes(this.ivLength);
|
||||
const cipher = crypto.createCipher(this.algorithm, this.key);
|
||||
cipher.setAAD(iv);
|
||||
|
||||
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
|
||||
const tag = cipher.getAuthTag();
|
||||
|
||||
return {
|
||||
data: encrypted,
|
||||
iv: iv.toString('hex'),
|
||||
timestamp: Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
decrypt(encryptedData: EncryptedData): string {
|
||||
if (!encryptedData.data || !encryptedData.iv) {
|
||||
throw new Error('Invalid encrypted data');
|
||||
}
|
||||
|
||||
const iv = Buffer.from(encryptedData.iv, 'hex');
|
||||
const decipher = crypto.createDecipher(this.algorithm, this.key);
|
||||
decipher.setAAD(iv);
|
||||
|
||||
let decrypted = decipher.update(encryptedData.data, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Asymmetric Encryption (Асимметричное шифрование)
|
||||
|
||||
**Асимметричное шифрование** использует пару ключей: публичный для шифрования и приватный для расшифровки.
|
||||
|
||||
**Преимущества:**
|
||||
- Безопасное распределение ключей
|
||||
- Цифровые подписи
|
||||
- Масштабируемость
|
||||
|
||||
**Недостатки:**
|
||||
- Медленнее симметричного шифрования
|
||||
- Сложность реализации
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class AsymmetricEncryptionService {
|
||||
private final KeyPair keyPair;
|
||||
|
||||
public AsymmetricEncryptionService() throws Exception {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
|
||||
keyPairGenerator.initialize(2048);
|
||||
this.keyPair = keyPairGenerator.generateKeyPair();
|
||||
}
|
||||
|
||||
public String encrypt(String plainText, PublicKey publicKey) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
||||
|
||||
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes());
|
||||
return Base64.getEncoder().encodeToString(encryptedBytes);
|
||||
}
|
||||
|
||||
public String decrypt(String encryptedText, PrivateKey privateKey) throws Exception {
|
||||
Cipher cipher = Cipher.getInstance("RSA");
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
||||
|
||||
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
|
||||
return new String(decryptedBytes);
|
||||
}
|
||||
|
||||
public String sign(String data, PrivateKey privateKey) throws Exception {
|
||||
Signature signature = Signature.getInstance("SHA256withRSA");
|
||||
signature.initSign(privateKey);
|
||||
signature.update(data.getBytes());
|
||||
|
||||
byte[] signatureBytes = signature.sign();
|
||||
return Base64.getEncoder().encodeToString(signatureBytes);
|
||||
}
|
||||
|
||||
public boolean verify(String data, String signature, PublicKey publicKey) throws Exception {
|
||||
Signature sig = Signature.getInstance("SHA256withRSA");
|
||||
sig.initVerify(publicKey);
|
||||
sig.update(data.getBytes());
|
||||
|
||||
return sig.verify(Base64.getDecoder().decode(signature));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Hashing (Хеширование)
|
||||
|
||||
**Хеширование** — одностороннее преобразование данных в фиксированную длину.
|
||||
|
||||
**Применения:**
|
||||
- Хранение паролей
|
||||
- Проверка целостности данных
|
||||
- Цифровые подписи
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class HashingService {
|
||||
|
||||
private final int BCRYPT_ROUNDS = 12;
|
||||
private final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256";
|
||||
private final int PBKDF2_ITERATIONS = 100000;
|
||||
private final int PBKDF2_KEY_LENGTH = 256;
|
||||
|
||||
public String hashPassword(String password) {
|
||||
if (password == null || password.isEmpty()) {
|
||||
throw new IllegalArgumentException("Password cannot be null or empty");
|
||||
}
|
||||
return BCrypt.hashpw(password, BCrypt.gensalt(BCRYPT_ROUNDS));
|
||||
}
|
||||
|
||||
public boolean verifyPassword(String password, String hash) {
|
||||
if (password == null || hash == null) {
|
||||
return false;
|
||||
}
|
||||
return BCrypt.checkpw(password, hash);
|
||||
}
|
||||
|
||||
public String generatePBKDF2Hash(String password, String salt) throws Exception {
|
||||
if (password == null || salt == null) {
|
||||
throw new IllegalArgumentException("Password and salt cannot be null");
|
||||
}
|
||||
|
||||
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(),
|
||||
PBKDF2_ITERATIONS, PBKDF2_KEY_LENGTH);
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
|
||||
byte[] hash = skf.generateSecret(spec).getEncoded();
|
||||
|
||||
return Base64.getEncoder().encodeToString(hash);
|
||||
}
|
||||
|
||||
public String generateSalt() {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] salt = new byte[16];
|
||||
random.nextBytes(salt);
|
||||
return Base64.getEncoder().encodeToString(salt);
|
||||
}
|
||||
|
||||
public String generateHash(String input, String algorithm) throws Exception {
|
||||
if (input == null) {
|
||||
throw new IllegalArgumentException("Input cannot be null");
|
||||
}
|
||||
|
||||
MessageDigest digest = MessageDigest.getInstance(algorithm);
|
||||
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.getEncoder().encodeToString(hash);
|
||||
}
|
||||
|
||||
public String generateHMAC(String data, String key, String algorithm) throws Exception {
|
||||
if (data == null || key == null) {
|
||||
throw new IllegalArgumentException("Data and key cannot be null");
|
||||
}
|
||||
|
||||
Mac mac = Mac.getInstance(algorithm);
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm);
|
||||
mac.init(secretKeySpec);
|
||||
|
||||
byte[] hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.getEncoder().encodeToString(hmacBytes);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Управление ключами
|
||||
|
||||
### 1. Key Rotation (Ротация ключей)
|
||||
|
||||
**Ротация ключей** — регулярная смена ключей шифрования для повышения безопасности.
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class KeyManagementService {
|
||||
|
||||
@Value("${encryption.key.rotation.enabled:true}")
|
||||
private boolean keyRotationEnabled;
|
||||
|
||||
@Value("${encryption.key.rotation.interval:30}")
|
||||
private int rotationIntervalDays;
|
||||
|
||||
private final Map<String, SecretKey> keyVersions = new ConcurrentHashMap<>();
|
||||
private final AtomicInteger currentKeyVersion = new AtomicInteger(1);
|
||||
|
||||
@PostConstruct
|
||||
public void initializeKeys() throws Exception {
|
||||
// Создаем начальный ключ
|
||||
SecretKey initialKey = generateAESKey();
|
||||
keyVersions.put("v1", initialKey);
|
||||
|
||||
if (keyRotationEnabled) {
|
||||
scheduleKeyRotation();
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0 2 * * ?") // Каждый день в 2:00
|
||||
public void rotateKeys() throws Exception {
|
||||
if (!keyRotationEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Starting key rotation process");
|
||||
|
||||
// Генерируем новый ключ
|
||||
int newVersion = currentKeyVersion.incrementAndGet();
|
||||
SecretKey newKey = generateAESKey();
|
||||
keyVersions.put("v" + newVersion, newKey);
|
||||
|
||||
// Перешифровываем критически важные данные
|
||||
reencryptCriticalData(newKey);
|
||||
|
||||
// Удаляем старые ключи (оставляем последние 3 версии)
|
||||
cleanupOldKeys();
|
||||
|
||||
log.info("Key rotation completed. New version: v{}", newVersion);
|
||||
}
|
||||
|
||||
public SecretKey getCurrentKey() {
|
||||
return keyVersions.get("v" + currentKeyVersion.get());
|
||||
}
|
||||
|
||||
public SecretKey getKeyByVersion(int version) {
|
||||
return keyVersions.get("v" + version);
|
||||
}
|
||||
|
||||
private SecretKey generateAESKey() throws Exception {
|
||||
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
|
||||
keyGenerator.init(256);
|
||||
return keyGenerator.generateKey();
|
||||
}
|
||||
|
||||
private void reencryptCriticalData(SecretKey newKey) {
|
||||
// Логика перешифровки критически важных данных
|
||||
// Это может включать перешифровку данных в БД
|
||||
}
|
||||
|
||||
private void cleanupOldKeys() {
|
||||
int currentVersion = currentKeyVersion.get();
|
||||
int keepVersions = 3;
|
||||
|
||||
for (int i = 1; i <= currentVersion - keepVersions; i++) {
|
||||
keyVersions.remove("v" + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Hardware Security Module (HSM)
|
||||
|
||||
**HSM** — аппаратный модуль безопасности для безопасного хранения и управления ключами.
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class HSMService {
|
||||
private final HSMClient hsmClient;
|
||||
|
||||
public String encryptWithHSM(String data) {
|
||||
return hsmClient.encrypt(data);
|
||||
}
|
||||
|
||||
public String decryptWithHSM(String encryptedData) {
|
||||
return hsmClient.decrypt(encryptedData);
|
||||
}
|
||||
|
||||
public String signWithHSM(String data) {
|
||||
return hsmClient.sign(data);
|
||||
}
|
||||
|
||||
public boolean verifyWithHSM(String data, String signature) {
|
||||
return hsmClient.verify(data, signature);
|
||||
}
|
||||
|
||||
public String generateKeyInHSM(String keyId) {
|
||||
return hsmClient.generateKey(keyId);
|
||||
}
|
||||
|
||||
public void deleteKeyFromHSM(String keyId) {
|
||||
hsmClient.deleteKey(keyId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Key Vault Integration
|
||||
|
||||
**Интеграция с хранилищем ключей** для централизованного управления ключами.
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class KeyVaultService {
|
||||
|
||||
@Value("${azure.keyvault.url}")
|
||||
private String keyVaultUrl;
|
||||
|
||||
@Value("${azure.keyvault.client-id}")
|
||||
private String clientId;
|
||||
|
||||
@Value("${azure.keyvault.client-secret}")
|
||||
private String clientSecret;
|
||||
|
||||
private KeyVaultClient keyVaultClient;
|
||||
|
||||
@PostConstruct
|
||||
public void initialize() {
|
||||
// Инициализация клиента Key Vault
|
||||
this.keyVaultClient = new KeyVaultClient(keyVaultUrl, clientId, clientSecret);
|
||||
}
|
||||
|
||||
public String getSecret(String secretName) {
|
||||
return keyVaultClient.getSecret(secretName);
|
||||
}
|
||||
|
||||
public void setSecret(String secretName, String secretValue) {
|
||||
keyVaultClient.setSecret(secretName, secretValue);
|
||||
}
|
||||
|
||||
public void deleteSecret(String secretName) {
|
||||
keyVaultClient.deleteSecret(secretName);
|
||||
}
|
||||
|
||||
public String getKey(String keyName) {
|
||||
return keyVaultClient.getKey(keyName);
|
||||
}
|
||||
|
||||
public void createKey(String keyName, String keyType) {
|
||||
keyVaultClient.createKey(keyName, keyType);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Безопасность ключей
|
||||
|
||||
### 1. Key Storage (Хранение ключей)
|
||||
|
||||
**Принципы безопасного хранения ключей:**
|
||||
- Никогда не храните ключи в коде
|
||||
- Используйте переменные окружения
|
||||
- Шифруйте ключи при хранении
|
||||
- Разделяйте ключи по окружениям
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
public class KeyStorageConfig {
|
||||
|
||||
@Bean
|
||||
public SecretKey encryptionKey() {
|
||||
String keyString = System.getenv("ENCRYPTION_KEY");
|
||||
if (keyString == null) {
|
||||
throw new IllegalStateException("ENCRYPTION_KEY environment variable not set");
|
||||
}
|
||||
|
||||
byte[] keyBytes = Base64.getDecoder().decode(keyString);
|
||||
return new SecretKeySpec(keyBytes, "AES");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String jwtSecret() {
|
||||
String secret = System.getenv("JWT_SECRET");
|
||||
if (secret == null) {
|
||||
throw new IllegalStateException("JWT_SECRET environment variable not set");
|
||||
}
|
||||
return secret;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Key Access Control (Контроль доступа к ключам)
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class KeyAccessControlService {
|
||||
|
||||
@Autowired
|
||||
private AuditService auditService;
|
||||
|
||||
public SecretKey getKeyWithAccessControl(String keyId, String userId, String operation) {
|
||||
// Проверяем права доступа
|
||||
if (!hasKeyAccess(userId, keyId, operation)) {
|
||||
auditService.logUnauthorizedKeyAccess(userId, keyId, operation);
|
||||
throw new AccessDeniedException("Access denied to key: " + keyId);
|
||||
}
|
||||
|
||||
// Логируем доступ к ключу
|
||||
auditService.logKeyAccess(userId, keyId, operation);
|
||||
|
||||
// Возвращаем ключ
|
||||
return keyManagementService.getKey(keyId);
|
||||
}
|
||||
|
||||
private boolean hasKeyAccess(String userId, String keyId, String operation) {
|
||||
// Логика проверки прав доступа к ключу
|
||||
return userService.hasPermission(userId, "key:" + keyId + ":" + operation);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Key Monitoring (Мониторинг ключей)
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class KeyMonitoringService {
|
||||
|
||||
@Autowired
|
||||
private MetricsService metricsService;
|
||||
|
||||
@Autowired
|
||||
private AlertService alertService;
|
||||
|
||||
public void monitorKeyUsage(String keyId, String operation) {
|
||||
// Увеличиваем счетчик использования ключа
|
||||
metricsService.incrementCounter("key.usage",
|
||||
Tags.of("key_id", keyId, "operation", operation));
|
||||
|
||||
// Проверяем на подозрительную активность
|
||||
if (isSuspiciousActivity(keyId, operation)) {
|
||||
alertService.sendKeySecurityAlert(keyId, operation);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSuspiciousActivity(String keyId, String operation) {
|
||||
// Проверка на необычное использование ключа
|
||||
long usageCount = metricsService.getCounterValue("key.usage",
|
||||
Tags.of("key_id", keyId, "operation", operation));
|
||||
|
||||
return usageCount > 1000; // Подозрительно много использований
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Заключение
|
||||
|
||||
Шифрование и управление ключами являются критически важными компонентами безопасности:
|
||||
|
||||
1. **Выбор алгоритма** — AES для симметричного, RSA/ECC для асимметричного
|
||||
2. **Управление ключами** — ротация, безопасное хранение, контроль доступа
|
||||
3. **Мониторинг** — отслеживание использования и подозрительной активности
|
||||
4. **Соответствие стандартам** — PCI DSS, GDPR, SOX
|
||||
|
||||
Правильная реализация этих компонентов обеспечивает защиту данных на всех уровнях приложения.
|
||||
@@ -0,0 +1,555 @@
|
||||
# Обфускация и защита кода
|
||||
|
||||
## Навигация
|
||||
- [🏠 Главная страница курса](../../../README.md)
|
||||
- [📚 Модуль 6](../README.md)
|
||||
|
||||
---
|
||||
|
||||
## Принципы защиты исходного кода
|
||||
|
||||
### 1. Обфускация кода (Code Obfuscation)
|
||||
|
||||
**Обфускация** — процесс преобразования исходного кода в трудночитаемую форму для защиты интеллектуальной собственности и усложнения реверс-инжиниринга.
|
||||
|
||||
**Цели обфускации:**
|
||||
- Защита алгоритмов и бизнес-логики
|
||||
- Усложнение реверс-инжиниринга
|
||||
- Защита от кражи интеллектуальной собственности
|
||||
- Предотвращение модификации кода
|
||||
|
||||
**Методы обфускации:**
|
||||
|
||||
```java
|
||||
// Исходный код
|
||||
public class PaymentProcessor {
|
||||
private static final String API_KEY = "sk_live_123456789";
|
||||
private static final String SECRET_KEY = "secret_abc123";
|
||||
|
||||
public PaymentResult processPayment(PaymentRequest request) {
|
||||
if (request.getAmount() > 10000) {
|
||||
return validateHighAmountPayment(request);
|
||||
}
|
||||
return processStandardPayment(request);
|
||||
}
|
||||
|
||||
private PaymentResult validateHighAmountPayment(PaymentRequest request) {
|
||||
// Сложная логика валидации
|
||||
boolean isValid = performAdvancedValidation(request);
|
||||
if (isValid) {
|
||||
return executePayment(request);
|
||||
}
|
||||
return PaymentResult.failed("Validation failed");
|
||||
}
|
||||
}
|
||||
|
||||
// Обфускированный код
|
||||
public class a {
|
||||
private static final String b = "sk_live_123456789";
|
||||
private static final String c = "secret_abc123";
|
||||
|
||||
public d e(f g) {
|
||||
if (g.h() > 10000) {
|
||||
return i(g);
|
||||
}
|
||||
return j(g);
|
||||
}
|
||||
|
||||
private d i(f g) {
|
||||
boolean h = k(g);
|
||||
if (h) {
|
||||
return l(g);
|
||||
}
|
||||
return d.m("Validation failed");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Rust примеры - Обфускация:**
|
||||
|
||||
```rust
|
||||
// Исходный код
|
||||
pub struct PaymentProcessor {
|
||||
api_key: String,
|
||||
secret_key: String,
|
||||
}
|
||||
|
||||
impl PaymentProcessor {
|
||||
pub fn new(api_key: String, secret_key: String) -> Self {
|
||||
Self { api_key, secret_key }
|
||||
}
|
||||
|
||||
pub fn process_payment(&self, request: PaymentRequest) -> PaymentResult {
|
||||
if request.amount > 10000 {
|
||||
return self.validate_high_amount_payment(request);
|
||||
}
|
||||
self.process_standard_payment(request)
|
||||
}
|
||||
|
||||
fn validate_high_amount_payment(&self, request: PaymentRequest) -> PaymentResult {
|
||||
let is_valid = self.perform_advanced_validation(&request);
|
||||
if is_valid {
|
||||
self.execute_payment(request)
|
||||
} else {
|
||||
PaymentResult::failed("Validation failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Обфускированный код (с использованием макросов)
|
||||
macro_rules! obfuscate_string {
|
||||
($s:expr) => {
|
||||
base64::decode($s).unwrap_or_else(|_| $s.as_bytes().to_vec())
|
||||
};
|
||||
}
|
||||
|
||||
pub struct a {
|
||||
b: Vec<u8>,
|
||||
c: Vec<u8>,
|
||||
}
|
||||
|
||||
impl a {
|
||||
pub fn d(e: Vec<u8>, f: Vec<u8>) -> Self {
|
||||
Self { b: e, c: f }
|
||||
}
|
||||
|
||||
pub fn g(&self, h: i) -> j {
|
||||
if h.k > 10000 {
|
||||
return self.l(h);
|
||||
}
|
||||
self.m(h)
|
||||
}
|
||||
|
||||
fn l(&self, h: i) -> j {
|
||||
let n = self.o(&h);
|
||||
if n {
|
||||
self.p(h)
|
||||
} else {
|
||||
j::q("Validation failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Python примеры - Обфускация:**
|
||||
|
||||
```python
|
||||
# Исходный код
|
||||
class PaymentProcessor:
|
||||
def __init__(self, api_key, secret_key):
|
||||
self.api_key = api_key
|
||||
self.secret_key = secret_key
|
||||
|
||||
def process_payment(self, request):
|
||||
if request.amount > 10000:
|
||||
return self.validate_high_amount_payment(request)
|
||||
return self.process_standard_payment(request)
|
||||
|
||||
def validate_high_amount_payment(self, request):
|
||||
is_valid = self.perform_advanced_validation(request)
|
||||
if is_valid:
|
||||
return self.execute_payment(request)
|
||||
return PaymentResult.failed("Validation failed")
|
||||
|
||||
# Обфускированный код
|
||||
import base64
|
||||
import zlib
|
||||
|
||||
class a:
|
||||
def __init__(self, b, c):
|
||||
self.b = b
|
||||
self.c = c
|
||||
|
||||
def d(self, e):
|
||||
if e.f > 10000:
|
||||
return self.g(e)
|
||||
return self.h(e)
|
||||
|
||||
def g(self, e):
|
||||
i = self.j(e)
|
||||
if i:
|
||||
return self.k(e)
|
||||
return l.m("Validation failed")
|
||||
|
||||
# Дополнительная обфускация строк
|
||||
def obfuscate_string(s):
|
||||
return base64.b64encode(zlib.compress(s.encode())).decode()
|
||||
|
||||
# Использование
|
||||
API_KEY = obfuscate_string("sk_live_123456789")
|
||||
SECRET_KEY = obfuscate_string("secret_abc123")
|
||||
```
|
||||
|
||||
**Node.js примеры - Обфускация:**
|
||||
|
||||
```typescript
|
||||
// Исходный код
|
||||
class PaymentProcessor {
|
||||
private apiKey: string;
|
||||
private secretKey: string;
|
||||
|
||||
constructor(apiKey: string, secretKey: string) {
|
||||
this.apiKey = apiKey;
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
processPayment(request: PaymentRequest): PaymentResult {
|
||||
if (request.amount > 10000) {
|
||||
return this.validateHighAmountPayment(request);
|
||||
}
|
||||
return this.processStandardPayment(request);
|
||||
}
|
||||
|
||||
private validateHighAmountPayment(request: PaymentRequest): PaymentResult {
|
||||
const isValid = this.performAdvancedValidation(request);
|
||||
if (isValid) {
|
||||
return this.executePayment(request);
|
||||
}
|
||||
return PaymentResult.failed("Validation failed");
|
||||
}
|
||||
}
|
||||
|
||||
// Обфускированный код
|
||||
class a {
|
||||
private b: string;
|
||||
private c: string;
|
||||
|
||||
constructor(d: string, e: string) {
|
||||
this.b = d;
|
||||
this.c = e;
|
||||
}
|
||||
|
||||
f(g: h): i {
|
||||
if (g.j > 10000) {
|
||||
return this.k(g);
|
||||
}
|
||||
return this.l(g);
|
||||
}
|
||||
|
||||
private k(g: h): i {
|
||||
const m = this.n(g);
|
||||
if (m) {
|
||||
return this.o(g);
|
||||
}
|
||||
return i.p("Validation failed");
|
||||
}
|
||||
}
|
||||
|
||||
// Webpack конфигурация для обфускации
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
mangle: {
|
||||
properties: {
|
||||
regex: /^_/
|
||||
}
|
||||
},
|
||||
compress: {
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
pure_funcs: ['console.log']
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Минификация (Minification)
|
||||
|
||||
**Минификация** — процесс удаления ненужных символов из кода без изменения его функциональности.
|
||||
|
||||
**Методы минификации:**
|
||||
|
||||
```javascript
|
||||
// Исходный JavaScript код
|
||||
function calculateTotal(items) {
|
||||
let total = 0;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (items[i].price > 0) {
|
||||
total += items[i].price * items[i].quantity;
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// Минифицированный код
|
||||
function calculateTotal(items){let total=0;for(let i=0;i<items.length;i++){if(items[i].price>0){total+=items[i].price*items[i].quantity}}return total}
|
||||
|
||||
// CSS минификация
|
||||
/* Исходный CSS */
|
||||
.header {
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Минифицированный CSS */
|
||||
.header{background-color:#fff;padding:20px;margin-bottom:10px}
|
||||
```
|
||||
|
||||
### 3. Защита от реверс-инжиниринга
|
||||
|
||||
**Методы защиты:**
|
||||
|
||||
```java
|
||||
// Анти-отладка
|
||||
public class AntiDebugProtection {
|
||||
|
||||
public static boolean isDebuggerPresent() {
|
||||
try {
|
||||
// Проверка наличия отладчика
|
||||
System.getProperty("java.vm.info");
|
||||
return System.getProperty("java.vm.info").contains("debug");
|
||||
} catch (Exception e) {
|
||||
return true; // Предполагаем наличие отладчика при ошибке
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkDebugger() {
|
||||
if (isDebuggerPresent()) {
|
||||
System.exit(1); // Завершение при обнаружении отладчика
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка целостности кода
|
||||
public static boolean verifyCodeIntegrity() {
|
||||
try {
|
||||
String expectedHash = "a1b2c3d4e5f6";
|
||||
String actualHash = calculateClassHash();
|
||||
return expectedHash.equals(actualHash);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String calculateClassHash() {
|
||||
// Расчет хеша класса для проверки целостности
|
||||
return "calculated_hash";
|
||||
}
|
||||
}
|
||||
|
||||
// Защита от модификации
|
||||
@Component
|
||||
public class CodeProtectionService {
|
||||
|
||||
@PostConstruct
|
||||
public void initializeProtection() {
|
||||
// Проверка целостности при запуске
|
||||
if (!verifyCodeIntegrity()) {
|
||||
throw new SecurityException("Code integrity check failed");
|
||||
}
|
||||
|
||||
// Проверка на отладчик
|
||||
checkDebugger();
|
||||
|
||||
// Запуск фоновой проверки
|
||||
startBackgroundProtection();
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate = 30000) // Каждые 30 секунд
|
||||
public void backgroundProtection() {
|
||||
if (!verifyCodeIntegrity()) {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (isDebuggerPresent()) {
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Source Maps и их безопасность
|
||||
|
||||
**Проблемы Source Maps:**
|
||||
|
||||
```javascript
|
||||
// webpack.config.js - НЕБЕЗОПАСНО
|
||||
module.exports = {
|
||||
devtool: 'source-map', // Раскрывает весь исходный код
|
||||
|
||||
// БЕЗОПАСНО - только для разработки
|
||||
devtool: process.env.NODE_ENV === 'development' ? 'source-map' : false,
|
||||
|
||||
// БЕЗОПАСНО - скрытые source maps
|
||||
devtool: 'hidden-source-map',
|
||||
|
||||
// БЕЗОПАСНО - только для внутреннего использования
|
||||
devtool: 'nosources-source-map'
|
||||
};
|
||||
|
||||
// Безопасная конфигурация
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
devtool: false, // Отключение source maps в продакшене
|
||||
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
sourceMap: false, // Отключение source maps
|
||||
mangle: true,
|
||||
compress: {
|
||||
drop_console: true,
|
||||
drop_debugger: true
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
|
||||
// Условное включение source maps только для разработки
|
||||
...(process.env.NODE_ENV === 'development' && {
|
||||
devtool: 'eval-source-map'
|
||||
})
|
||||
};
|
||||
```
|
||||
|
||||
### 5. Защита конфигурационных файлов
|
||||
|
||||
```java
|
||||
// Защита конфигурации
|
||||
@Configuration
|
||||
@EnableEncryption
|
||||
public class SecureConfiguration {
|
||||
|
||||
@Value("${encrypted.database.password}")
|
||||
private String encryptedDbPassword;
|
||||
|
||||
@Value("${encrypted.api.key}")
|
||||
private String encryptedApiKey;
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
|
||||
config.setUsername("user");
|
||||
config.setPassword(decrypt(encryptedDbPassword)); // Расшифровка пароля
|
||||
return new HikariDataSource(config);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
RestTemplate template = new RestTemplate();
|
||||
template.getInterceptors().add(new ApiKeyInterceptor(decrypt(encryptedApiKey)));
|
||||
return template;
|
||||
}
|
||||
|
||||
private String decrypt(String encryptedValue) {
|
||||
// Логика расшифровки
|
||||
return encryptionService.decrypt(encryptedValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Шифрование конфигурации
|
||||
@Service
|
||||
public class ConfigurationEncryptionService {
|
||||
|
||||
public String encryptConfiguration(String value) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(
|
||||
getEncryptionKey(), "AES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
|
||||
|
||||
byte[] encrypted = cipher.doFinal(value.getBytes());
|
||||
return Base64.getEncoder().encodeToString(encrypted);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException("Failed to encrypt configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getEncryptionKey() {
|
||||
// Получение ключа шифрования из безопасного хранилища
|
||||
return keyManagementService.getEncryptionKey();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. Защита от анализа трафика
|
||||
|
||||
```java
|
||||
// Обфускация сетевого трафика
|
||||
@Service
|
||||
public class NetworkObfuscationService {
|
||||
|
||||
public String obfuscateRequest(String originalRequest) {
|
||||
// Обфускация параметров запроса
|
||||
String obfuscated = base64Encode(originalRequest);
|
||||
obfuscated = addRandomPadding(obfuscated);
|
||||
obfuscated = xorWithKey(obfuscated, getObfuscationKey());
|
||||
|
||||
return obfuscated;
|
||||
}
|
||||
|
||||
public String deobfuscateResponse(String obfuscatedResponse) {
|
||||
// Деобфускация ответа
|
||||
String deobfuscated = xorWithKey(obfuscatedResponse, getObfuscationKey());
|
||||
deobfuscated = removeRandomPadding(deobfuscated);
|
||||
deobfuscated = base64Decode(deobfuscated);
|
||||
|
||||
return deobfuscated;
|
||||
}
|
||||
|
||||
private String addRandomPadding(String data) {
|
||||
// Добавление случайных данных для усложнения анализа
|
||||
Random random = new Random();
|
||||
int paddingLength = random.nextInt(100) + 50;
|
||||
StringBuilder padding = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < paddingLength; i++) {
|
||||
padding.append((char) (random.nextInt(26) + 'a'));
|
||||
}
|
||||
|
||||
return padding.toString() + data + padding.toString();
|
||||
}
|
||||
|
||||
private String removeRandomPadding(String data) {
|
||||
// Удаление случайных данных
|
||||
// Логика удаления padding
|
||||
return data.substring(50, data.length() - 50);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Инструменты обфускации
|
||||
|
||||
### Java
|
||||
- **ProGuard** - обфускация и минификация
|
||||
- **Allatori** - коммерческая обфускация
|
||||
- **DashO** - профессиональная обфускация
|
||||
|
||||
### JavaScript/TypeScript
|
||||
- **Terser** - минификация и обфускация
|
||||
- **UglifyJS** - минификация JavaScript
|
||||
- **Webpack** - бандлинг и обфускация
|
||||
|
||||
### Python
|
||||
- **PyArmor** - обфускация Python кода
|
||||
- **Nuitka** - компиляция в нативный код
|
||||
- **cx_Freeze** - создание исполняемых файлов
|
||||
|
||||
### Rust
|
||||
- **rustc** с флагами оптимизации
|
||||
- **strip** - удаление отладочной информации
|
||||
- **upx** - сжатие исполняемых файлов
|
||||
|
||||
## Заключение
|
||||
|
||||
Обфускация и защита кода являются важными мерами безопасности:
|
||||
|
||||
1. **Обфускация** — усложнение понимания кода
|
||||
2. **Минификация** — уменьшение размера и удаление отладочной информации
|
||||
3. **Анти-отладка** — защита от анализа во время выполнения
|
||||
4. **Защита конфигурации** — шифрование чувствительных настроек
|
||||
5. **Обфускация трафика** — усложнение анализа сетевого взаимодействия
|
||||
|
||||
Правильное применение этих техник значительно повышает безопасность приложения и защищает интеллектуальную собственность.
|
||||
@@ -0,0 +1,745 @@
|
||||
# Security by Design
|
||||
|
||||
## Навигация
|
||||
- [🏠 Главная страница курса](../../../README.md)
|
||||
- [📚 Модуль 6](../README.md)
|
||||
|
||||
---
|
||||
|
||||
## Принципы Security by Design
|
||||
|
||||
### 1. Secure by Default
|
||||
- Безопасные настройки по умолчанию
|
||||
- Минимальные права доступа
|
||||
- Защита от случайных ошибок
|
||||
|
||||
### 2. Fail Secure
|
||||
- При отказе система переходит в безопасное состояние
|
||||
- Блокировка доступа при ошибках
|
||||
- Логирование всех событий безопасности
|
||||
|
||||
### 3. Security as Code
|
||||
- Безопасность как часть кода
|
||||
- Автоматизированные проверки безопасности
|
||||
- Версионирование политик безопасности
|
||||
|
||||
## Моделирование угроз
|
||||
|
||||
### STRIDE Model
|
||||
- **S**poofing (Подмена)
|
||||
- **T**ampering (Изменение)
|
||||
- **R**epudiation (Отказ)
|
||||
- **I**nformation Disclosure (Раскрытие информации)
|
||||
- **D**enial of Service (Отказ в обслуживании)
|
||||
- **E**levation of Privilege (Повышение привилегий)
|
||||
|
||||
### Пример Threat Model:
|
||||
```yaml
|
||||
# threat-model.yml
|
||||
application: "E-commerce Platform"
|
||||
components:
|
||||
- name: "User Authentication"
|
||||
threats:
|
||||
- type: "Spoofing"
|
||||
description: "User impersonation"
|
||||
mitigations:
|
||||
- "Strong password policy"
|
||||
- "Multi-factor authentication"
|
||||
|
||||
- name: "Payment Processing"
|
||||
threats:
|
||||
- type: "Tampering"
|
||||
description: "Payment data modification"
|
||||
mitigations:
|
||||
- "Data encryption"
|
||||
- "Digital signatures"
|
||||
```
|
||||
|
||||
## Архитектура безопасности
|
||||
|
||||
### 1. Zero Trust Architecture
|
||||
**Принцип:** "Никогда не доверяй, всегда проверяй"
|
||||
|
||||
```java
|
||||
@Component
|
||||
public class ZeroTrustFilter implements Filter {
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
|
||||
// проверяем каждую аутентификацию
|
||||
if (!isAuthenticated(httpRequest)) {
|
||||
throw new AuthenticationException("Authentication required");
|
||||
}
|
||||
|
||||
// проверяем каждую авторизацию
|
||||
if (!isAuthorized(httpRequest)) {
|
||||
throw new AuthorizationException("Access denied");
|
||||
}
|
||||
|
||||
// проверяем устройство
|
||||
if (!isDeviceTrusted(httpRequest)) {
|
||||
throw new SecurityException("Untrusted device");
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Microsegmentation
|
||||
**Принцип:** Разделение сети на мелкие сегменты
|
||||
|
||||
```yaml
|
||||
# network-policy.yml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: order-service-policy
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: order-service
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: api-gateway
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
egress:
|
||||
- to:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: payment-service
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
```
|
||||
|
||||
## Аутентификация и авторизация
|
||||
|
||||
### Аутентификация
|
||||
|
||||
#### 1. Password-based Authentication
|
||||
```java
|
||||
@Service
|
||||
public class AuthenticationService {
|
||||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
public AuthenticationResult authenticate(String username, String password) {
|
||||
User user = userRepository.findByUsername(username);
|
||||
|
||||
if (user == null) {
|
||||
return AuthenticationResult.failure("User not found");
|
||||
}
|
||||
|
||||
if (!passwordEncoder.matches(password, user.getPasswordHash())) {
|
||||
return AuthenticationResult.failure("Invalid password");
|
||||
}
|
||||
|
||||
if (!user.isActive()) {
|
||||
return AuthenticationResult.failure("User account is disabled");
|
||||
}
|
||||
|
||||
return AuthenticationResult.success(user);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Multi-Factor Authentication (MFA)
|
||||
```java
|
||||
@Service
|
||||
public class MFAService {
|
||||
@Autowired
|
||||
private TOTPGenerator totpGenerator;
|
||||
|
||||
@Autowired
|
||||
private SMSService smsService;
|
||||
|
||||
public void enableMFA(Long userId, String phoneNumber) {
|
||||
String secret = totpGenerator.generateSecret();
|
||||
userRepository.updateMFASecret(userId, secret);
|
||||
userRepository.updatePhoneNumber(userId, phoneNumber);
|
||||
}
|
||||
|
||||
public boolean verifyMFA(Long userId, String code) {
|
||||
User user = userRepository.findById(userId);
|
||||
String secret = user.getMFASecret();
|
||||
|
||||
return totpGenerator.verifyCode(secret, code);
|
||||
}
|
||||
|
||||
public void sendSMS(Long userId, String code) {
|
||||
User user = userRepository.findById(userId);
|
||||
smsService.sendSMS(user.getPhoneNumber(), "Your verification code: " + code);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. OAuth 2.0 / OpenID Connect
|
||||
```java
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.oauth2Login(oauth2 -> oauth2
|
||||
.userInfoEndpoint(userInfo -> userInfo
|
||||
.userService(customOAuth2UserService)
|
||||
)
|
||||
)
|
||||
.authorizeHttpRequests(authz -> authz
|
||||
.requestMatchers("/public/**").permitAll()
|
||||
.requestMatchers("/admin/**").hasRole("ADMIN")
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Авторизация
|
||||
|
||||
#### 1. Role-Based Access Control (RBAC)
|
||||
```java
|
||||
@Entity
|
||||
public class User {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
@JoinTable(
|
||||
name = "user_roles",
|
||||
joinColumns = @JoinColumn(name = "user_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "role_id")
|
||||
)
|
||||
private Set<Role> roles = new HashSet<>();
|
||||
}
|
||||
|
||||
@Entity
|
||||
public class Role {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
@JoinTable(
|
||||
name = "role_permissions",
|
||||
joinColumns = @JoinColumn(name = "role_id"),
|
||||
inverseJoinColumns = @JoinColumn(name = "permission_id")
|
||||
)
|
||||
private Set<Permission> permissions = new HashSet<>();
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Attribute-Based Access Control (ABAC)
|
||||
```java
|
||||
@Component
|
||||
public class ABACPolicyEngine {
|
||||
public boolean evaluate(Policy policy, AccessRequest request) {
|
||||
// проверяем субъект
|
||||
if (!policy.getSubject().matches(request.getSubject())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// проверяем ресурс
|
||||
if (!policy.getResource().matches(request.getResource())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// проверяем действие
|
||||
if (!policy.getAction().matches(request.getAction())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// проверяем контекст
|
||||
if (!policy.getContext().matches(request.getContext())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Пример политики
|
||||
Policy policy = Policy.builder()
|
||||
.subject("role:manager")
|
||||
.resource("department:finance")
|
||||
.action("read")
|
||||
.context("time:business_hours")
|
||||
.build();
|
||||
```
|
||||
|
||||
#### 3. JWT Tokens
|
||||
```java
|
||||
@Service
|
||||
public class JWTService {
|
||||
private final String secretKey = "mySecretKey";
|
||||
private final int jwtExpiration = 86400000; // 24 hours
|
||||
|
||||
public String generateToken(UserDetails userDetails) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("roles", userDetails.getAuthorities());
|
||||
claims.put("userId", userDetails.getUsername());
|
||||
|
||||
return createToken(claims, userDetails.getUsername());
|
||||
}
|
||||
|
||||
private String createToken(Map<String, Object> claims, String subject) {
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setSubject(subject)
|
||||
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||
.setExpiration(new Date(System.currentTimeMillis() + jwtExpiration))
|
||||
.signWith(SignatureAlgorithm.HS512, secretKey)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public Boolean validateToken(String token, UserDetails userDetails) {
|
||||
final String username = extractUsername(token);
|
||||
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Безопасность API
|
||||
|
||||
### Лучшие практики безопасности API
|
||||
|
||||
#### 1. Input Validation
|
||||
```java
|
||||
@RestController
|
||||
@Validated
|
||||
public class UserController {
|
||||
@PostMapping("/users")
|
||||
public ResponseEntity<User> createUser(@Valid @RequestBody CreateUserRequest request) {
|
||||
// валидация выполняется автоматически через @Valid
|
||||
User user = userService.createUser(request);
|
||||
return ResponseEntity.ok(user);
|
||||
}
|
||||
}
|
||||
|
||||
public class CreateUserRequest {
|
||||
@NotBlank(message = "Username is required")
|
||||
@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "Email is required")
|
||||
@Email(message = "Email must be valid")
|
||||
private String email;
|
||||
|
||||
@NotBlank(message = "Password is required")
|
||||
@Size(min = 8, message = "Password must be at least 8 characters")
|
||||
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$",
|
||||
message = "Password must contain at least one uppercase letter, one lowercase letter, one number and one special character")
|
||||
private String password;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Rate Limiting
|
||||
```java
|
||||
@Component
|
||||
public class RateLimitingFilter implements Filter {
|
||||
private final Map<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||||
String clientId = getClientId(httpRequest);
|
||||
|
||||
RateLimiter rateLimiter = rateLimiters.computeIfAbsent(clientId,
|
||||
k -> RateLimiter.create(10.0)); // 10 запросов в секунду
|
||||
|
||||
if (!rateLimiter.tryAcquire()) {
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
httpResponse.setStatus(429); // Too Many Requests
|
||||
httpResponse.getWriter().write("Rate limit exceeded");
|
||||
return;
|
||||
}
|
||||
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. API Versioning
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/users")
|
||||
public class UserControllerV1 {
|
||||
@GetMapping("/{id}")
|
||||
public UserV1 getUser(@PathVariable Long id) {
|
||||
return userService.getUserV1(id);
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v2/users")
|
||||
public class UserControllerV2 {
|
||||
@GetMapping("/{id}")
|
||||
public UserV2 getUser(@PathVariable Long id) {
|
||||
return userService.getUserV2(id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Безопасность API Gateway
|
||||
|
||||
```yaml
|
||||
# api-gateway-security.yml
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: AuthorizationPolicy
|
||||
metadata:
|
||||
name: api-gateway-policy
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: api-gateway
|
||||
rules:
|
||||
- from:
|
||||
- source:
|
||||
principals: ["cluster.local/ns/default/sa/api-gateway"]
|
||||
to:
|
||||
- operation:
|
||||
methods: ["GET", "POST"]
|
||||
paths: ["/api/v1/*"]
|
||||
- from:
|
||||
- source:
|
||||
principals: ["cluster.local/ns/default/sa/admin-service"]
|
||||
to:
|
||||
- operation:
|
||||
methods: ["*"]
|
||||
paths: ["/admin/*"]
|
||||
```
|
||||
|
||||
## Безопасность данных
|
||||
|
||||
### Классификация данных
|
||||
|
||||
#### 1. Data Sensitivity Levels
|
||||
- **Public** — общедоступные данные
|
||||
- **Internal** — внутренние данные
|
||||
- **Confidential** — конфиденциальные данные
|
||||
- **Restricted** — ограниченные данные
|
||||
|
||||
```java
|
||||
@Entity
|
||||
public class User {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Column(name = "username")
|
||||
@DataClassification(level = DataClassification.Level.INTERNAL)
|
||||
private String username;
|
||||
|
||||
@Column(name = "email")
|
||||
@DataClassification(level = DataClassification.Level.CONFIDENTIAL)
|
||||
private String email;
|
||||
|
||||
@Column(name = "ssn")
|
||||
@DataClassification(level = DataClassification.Level.RESTRICTED)
|
||||
@Encrypted
|
||||
private String ssn;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Data Masking
|
||||
```java
|
||||
@Service
|
||||
public class DataMaskingService {
|
||||
public User maskUserData(User user, UserRole requesterRole) {
|
||||
User maskedUser = new User();
|
||||
maskedUser.setId(user.getId());
|
||||
|
||||
switch (requesterRole) {
|
||||
case ADMIN:
|
||||
// админы видят все данные
|
||||
maskedUser.setEmail(user.getEmail());
|
||||
maskedUser.setSsn(user.getSsn());
|
||||
break;
|
||||
case MANAGER:
|
||||
// менеджеры видят частично замаскированные данные
|
||||
maskedUser.setEmail(maskEmail(user.getEmail()));
|
||||
maskedUser.setSsn(maskSSN(user.getSsn()));
|
||||
break;
|
||||
case USER:
|
||||
// обычные пользователи видят только базовую информацию
|
||||
maskedUser.setEmail(maskEmail(user.getEmail()));
|
||||
maskedUser.setSsn("***-**-****");
|
||||
break;
|
||||
}
|
||||
|
||||
return maskedUser;
|
||||
}
|
||||
|
||||
private String maskEmail(String email) {
|
||||
String[] parts = email.split("@");
|
||||
String username = parts[0];
|
||||
String domain = parts[1];
|
||||
|
||||
if (username.length() <= 2) {
|
||||
return "*@" + domain;
|
||||
}
|
||||
|
||||
return username.substring(0, 2) + "*".repeat(username.length() - 2) + "@" + domain;
|
||||
}
|
||||
|
||||
private String maskSSN(String ssn) {
|
||||
return "***-**-" + ssn.substring(ssn.length() - 4);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Шифрование данных
|
||||
|
||||
#### 1. Encryption at Rest
|
||||
```java
|
||||
@Entity
|
||||
public class PaymentCard {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Column(name = "card_number")
|
||||
@Convert(converter = EncryptedStringConverter.class)
|
||||
private String cardNumber;
|
||||
|
||||
@Column(name = "cvv")
|
||||
@Convert(converter = EncryptedStringConverter.class)
|
||||
private String cvv;
|
||||
}
|
||||
|
||||
@Converter
|
||||
public class EncryptedStringConverter implements AttributeConverter<String, String> {
|
||||
@Autowired
|
||||
private EncryptionService encryptionService;
|
||||
|
||||
@Override
|
||||
public String convertToDatabaseColumn(String attribute) {
|
||||
return encryptionService.encrypt(attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertToEntityAttribute(String dbData) {
|
||||
return encryptionService.decrypt(dbData);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Encryption in Transit
|
||||
```java
|
||||
@Configuration
|
||||
public class SSLConfig {
|
||||
@Bean
|
||||
public RestTemplate restTemplate() throws Exception {
|
||||
SSLContext sslContext = SSLContextBuilder.create()
|
||||
.loadTrustMaterial(null, (certificate, authType) -> true)
|
||||
.build();
|
||||
|
||||
HttpClient httpClient = HttpClients.custom()
|
||||
.setSSLContext(sslContext)
|
||||
.build();
|
||||
|
||||
HttpComponentsClientHttpRequestFactory factory =
|
||||
new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
|
||||
return new RestTemplate(factory);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Соответствие требованиям
|
||||
|
||||
### GDPR (Общий регламент по защите данных)
|
||||
|
||||
#### 1. Data Subject Rights
|
||||
```java
|
||||
@Service
|
||||
public class GDPRService {
|
||||
public void handleDataSubjectRequest(DataSubjectRequest request) {
|
||||
switch (request.getType()) {
|
||||
case ACCESS:
|
||||
provideDataAccess(request.getSubjectId());
|
||||
break;
|
||||
case RECTIFICATION:
|
||||
rectifyData(request.getSubjectId(), request.getData());
|
||||
break;
|
||||
case ERASURE:
|
||||
eraseData(request.getSubjectId());
|
||||
break;
|
||||
case PORTABILITY:
|
||||
provideDataPortability(request.getSubjectId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void eraseData(Long subjectId) {
|
||||
// удаляем персональные данные
|
||||
userRepository.deletePersonalData(subjectId);
|
||||
|
||||
// логируем удаление
|
||||
auditService.logDataErasure(subjectId);
|
||||
|
||||
// уведомляем заинтересованные системы
|
||||
notificationService.notifyDataErasure(subjectId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Consent Management
|
||||
```java
|
||||
@Entity
|
||||
public class Consent {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private Long userId;
|
||||
private String purpose;
|
||||
private ConsentStatus status;
|
||||
private LocalDateTime givenAt;
|
||||
private LocalDateTime withdrawnAt;
|
||||
|
||||
public boolean isValid() {
|
||||
return status == ConsentStatus.GIVEN && withdrawnAt == null;
|
||||
}
|
||||
}
|
||||
|
||||
@Service
|
||||
public class ConsentService {
|
||||
public void giveConsent(Long userId, String purpose) {
|
||||
Consent consent = new Consent();
|
||||
consent.setUserId(userId);
|
||||
consent.setPurpose(purpose);
|
||||
consent.setStatus(ConsentStatus.GIVEN);
|
||||
consent.setGivenAt(LocalDateTime.now());
|
||||
|
||||
consentRepository.save(consent);
|
||||
}
|
||||
|
||||
public void withdrawConsent(Long userId, String purpose) {
|
||||
Consent consent = consentRepository.findByUserIdAndPurpose(userId, purpose);
|
||||
if (consent != null) {
|
||||
consent.setStatus(ConsentStatus.WITHDRAWN);
|
||||
consent.setWithdrawnAt(LocalDateTime.now());
|
||||
consentRepository.save(consent);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### PCI DSS (Стандарт безопасности данных индустрии платежных карт)
|
||||
|
||||
#### 1. Card Data Protection
|
||||
```java
|
||||
@Service
|
||||
public class PCIService {
|
||||
public String tokenizeCard(String cardNumber) {
|
||||
// генерируем токен вместо хранения номера карты
|
||||
String token = UUID.randomUUID().toString();
|
||||
|
||||
// сохраняем токен в безопасном хранилище
|
||||
secureStorage.storeToken(token, cardNumber);
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
public String detokenizeCard(String token) {
|
||||
// получаем номер карты по токену
|
||||
return secureStorage.getCardByToken(token);
|
||||
}
|
||||
|
||||
public boolean validateCard(String cardNumber) {
|
||||
// проверяем номер карты по алгоритму Луна
|
||||
return luhnAlgorithm.validate(cardNumber);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. Secure Payment Processing
|
||||
```java
|
||||
@Service
|
||||
public class PaymentService {
|
||||
@Transactional
|
||||
public PaymentResult processPayment(PaymentRequest request) {
|
||||
// валидируем данные карты
|
||||
if (!validateCardData(request.getCardData())) {
|
||||
return PaymentResult.failure("Invalid card data");
|
||||
}
|
||||
|
||||
// токенизируем данные карты
|
||||
String token = tokenizeCard(request.getCardData().getNumber());
|
||||
|
||||
// обрабатываем платеж
|
||||
PaymentResult result = paymentProcessor.processPayment(request);
|
||||
|
||||
// логируем транзакцию
|
||||
auditService.logPaymentTransaction(request, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### SOX (Закон Сарбейнса-Оксли)
|
||||
|
||||
#### 1. Financial Data Controls
|
||||
```java
|
||||
@Service
|
||||
public class FinancialControlService {
|
||||
public void validateFinancialTransaction(FinancialTransaction transaction) {
|
||||
// проверяем авторизацию
|
||||
if (!isAuthorized(transaction.getAmount())) {
|
||||
throw new AuthorizationException("Transaction amount exceeds authorization limit");
|
||||
}
|
||||
|
||||
// проверяем дублирование
|
||||
if (isDuplicateTransaction(transaction)) {
|
||||
throw new DuplicateTransactionException("Duplicate transaction detected");
|
||||
}
|
||||
|
||||
// проверяем целостность данных
|
||||
if (!validateDataIntegrity(transaction)) {
|
||||
throw new DataIntegrityException("Data integrity check failed");
|
||||
}
|
||||
}
|
||||
|
||||
public void auditFinancialData() {
|
||||
// аудит финансовых данных
|
||||
List<FinancialTransaction> transactions = getTransactionsForAudit();
|
||||
|
||||
for (FinancialTransaction transaction : transactions) {
|
||||
validateTransaction(transaction);
|
||||
logAuditTrail(transaction);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Заключение
|
||||
|
||||
Security by Design обеспечивает встроенную безопасность на всех уровнях приложения:
|
||||
|
||||
1. **Принципы безопасности** — Secure by Default, Fail Secure, Security as Code
|
||||
2. **Моделирование угроз** — STRIDE модель для выявления рисков
|
||||
3. **Архитектура безопасности** — Zero Trust, Microsegmentation
|
||||
4. **Аутентификация и авторизация** — MFA, RBAC, ABAC, JWT
|
||||
5. **Безопасность API** — Валидация, Rate Limiting, Versioning
|
||||
6. **Безопасность данных** — Классификация, Маскирование, Шифрование
|
||||
7. **Соответствие требованиям** — GDPR, PCI DSS, SOX
|
||||
|
||||
Интеграция этих принципов в процесс разработки обеспечивает надежную защиту приложения от угроз безопасности.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Практические задания
|
||||
|
||||
## Навигация
|
||||
- [🏠 Главная страница курса](../../../README.md)
|
||||
- [📚 Модуль 6](../README.md)
|
||||
|
||||
---
|
||||
|
||||
### Задание 1: Security Implementation
|
||||
1. Реализуйте аутентификацию с JWT
|
||||
2. Настройте авторизацию с RBAC
|
||||
3. Добавьте валидацию входных данных
|
||||
|
||||
### Задание 2: Data Protection
|
||||
1. Реализуйте шифрование данных
|
||||
2. Настройте маскирование данных
|
||||
3. Добавьте аудит доступа к данным
|
||||
|
||||
### Задание 3: Compliance
|
||||
1. Реализуйте GDPR compliance
|
||||
2. Настройте PCI DSS для платежей
|
||||
3. Добавьте SOX controls
|
||||
|
||||
---
|
||||
Reference in new issue
Block a user