- 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
4010 lines
129 KiB
Markdown
4010 lines
129 KiB
Markdown
# Модуль 6: Безопасность и соответствие требованиям
|
||
|
||
## Содержание модуля
|
||
1. [Основы безопасности](#основы-безопасности)
|
||
2. [Уязвимости и межсервисное взаимодействие](#уязвимости-и-межсервисное-взаимодействие)
|
||
3. [Полный каталог уязвимостей информационной безопасности](#полный-каталог-уязвимостей-информационной-безопасности)
|
||
4. [Обфускация и защита кода](#обфускация-и-защита-кода)
|
||
5. [Security by Design](#security-by-design)
|
||
6. [Аутентификация и авторизация](#аутентификация-и-авторизация)
|
||
7. [Шифрование](#шифрование)
|
||
8. [Безопасность API](#безопасность-api)
|
||
9. [Безопасность данных](#безопасность-данных)
|
||
10. [Соответствие требованиям](#соответствие-требованиям)
|
||
|
||
---
|
||
|
||
## Основы безопасности
|
||
|
||
### Принципы информационной безопасности
|
||
|
||
#### 1. CIA Triad
|
||
- **Confidentiality (Конфиденциальность)** — доступ только авторизованным пользователям
|
||
- **Integrity (Целостность)** — данные не изменяются несанкционированно
|
||
- **Availability (Доступность)** — система доступна когда нужно
|
||
|
||
#### 2. Defense in Depth
|
||
**Принцип:** Многоуровневая защита системы.
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ Application │
|
||
├─────────────────────────────────────┤
|
||
│ Network Security │
|
||
├─────────────────────────────────────┤
|
||
│ Infrastructure │
|
||
├─────────────────────────────────────┤
|
||
│ Physical Security │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
#### 3. Least Privilege
|
||
**Принцип:** Предоставление минимально необходимых прав доступа.
|
||
|
||
**Технический пример - Advanced Security Implementation:**
|
||
|
||
```java
|
||
@Service
|
||
@Transactional
|
||
public class UserService {
|
||
|
||
@Autowired
|
||
private UserRepository userRepository;
|
||
|
||
@Autowired
|
||
private PasswordEncoder passwordEncoder;
|
||
|
||
@Autowired
|
||
private AuditService auditService;
|
||
|
||
@PreAuthorize("hasRole('ADMIN')")
|
||
@Audit(action = "DELETE_USER", resource = "USER")
|
||
public void deleteUser(Long userId) {
|
||
// Только админы могут удалять пользователей
|
||
User user = userRepository.findById(userId)
|
||
.orElseThrow(() -> new UserNotFoundException("User not found"));
|
||
|
||
// Проверяем, что админ не удаляет сам себя
|
||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||
if (auth.getName().equals(user.getEmail())) {
|
||
throw new SecurityException("Cannot delete your own account");
|
||
}
|
||
|
||
// Логируем действие
|
||
auditService.logAction("DELETE_USER", userId, auth.getName());
|
||
|
||
userRepository.deleteById(userId);
|
||
}
|
||
|
||
@PreAuthorize("hasRole('USER') and #userId == authentication.principal.id")
|
||
public User getUserProfile(Long userId) {
|
||
// Пользователи могут видеть только свой профиль
|
||
return userRepository.findById(userId)
|
||
.orElseThrow(() -> new UserNotFoundException("User not found"));
|
||
}
|
||
|
||
@PreAuthorize("hasRole('ADMIN') or hasRole('MANAGER')")
|
||
public List<User> getAllUsers() {
|
||
// Админы и менеджеры могут видеть всех пользователей
|
||
return userRepository.findAll();
|
||
}
|
||
|
||
@PreAuthorize("hasRole('ADMIN')")
|
||
@Audit(action = "UPDATE_USER_ROLE", resource = "USER")
|
||
public void updateUserRole(Long userId, String newRole) {
|
||
// Только админы могут изменять роли
|
||
User user = userRepository.findById(userId)
|
||
.orElseThrow(() -> new UserNotFoundException("User not found"));
|
||
|
||
// Проверяем валидность роли
|
||
if (!isValidRole(newRole)) {
|
||
throw new IllegalArgumentException("Invalid role: " + newRole);
|
||
}
|
||
|
||
String oldRole = user.getRole();
|
||
user.setRole(newRole);
|
||
userRepository.save(user);
|
||
|
||
// Логируем изменение роли
|
||
auditService.logAction("UPDATE_USER_ROLE", userId,
|
||
SecurityContextHolder.getContext().getAuthentication().getName(),
|
||
Map.of("oldRole", oldRole, "newRole", newRole));
|
||
}
|
||
|
||
private boolean isValidRole(String role) {
|
||
return Arrays.asList("USER", "MANAGER", "ADMIN").contains(role);
|
||
}
|
||
}
|
||
|
||
// Security Configuration
|
||
@Configuration
|
||
@EnableWebSecurity
|
||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||
public class SecurityConfig {
|
||
|
||
@Autowired
|
||
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
|
||
|
||
@Autowired
|
||
private JwtRequestFilter jwtRequestFilter;
|
||
|
||
@Autowired
|
||
private UserDetailsService userDetailsService;
|
||
|
||
@Bean
|
||
public PasswordEncoder passwordEncoder() {
|
||
return new BCryptPasswordEncoder(12);
|
||
}
|
||
|
||
@Bean
|
||
public AuthenticationManager authenticationManager(
|
||
AuthenticationConfiguration authConfig) throws Exception {
|
||
return authConfig.getAuthenticationManager();
|
||
}
|
||
|
||
@Bean
|
||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||
http.csrf().disable()
|
||
.authorizeHttpRequests(authz -> authz
|
||
.requestMatchers("/api/auth/**").permitAll()
|
||
.requestMatchers("/api/public/**").permitAll()
|
||
.requestMatchers("/actuator/health").permitAll()
|
||
.requestMatchers("/api/admin/**").hasRole("ADMIN")
|
||
.requestMatchers("/api/users/**").hasAnyRole("USER", "ADMIN", "MANAGER")
|
||
.anyRequest().authenticated()
|
||
)
|
||
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
|
||
.and()
|
||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||
.and()
|
||
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
|
||
.headers(headers -> headers
|
||
.frameOptions().deny()
|
||
.contentTypeOptions().and()
|
||
.httpStrictTransportSecurity(hstsConfig -> hstsConfig
|
||
.maxAgeInSeconds(31536000)
|
||
.includeSubdomains(true)
|
||
)
|
||
);
|
||
|
||
return http.build();
|
||
}
|
||
|
||
@Bean
|
||
public CorsConfigurationSource corsConfigurationSource() {
|
||
CorsConfiguration configuration = new CorsConfiguration();
|
||
configuration.setAllowedOriginPatterns(Arrays.asList("https://*.example.com"));
|
||
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
|
||
configuration.setAllowedHeaders(Arrays.asList("*"));
|
||
configuration.setAllowCredentials(true);
|
||
configuration.setMaxAge(3600L);
|
||
|
||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||
source.registerCorsConfiguration("/**", configuration);
|
||
return source;
|
||
}
|
||
}
|
||
|
||
// JWT Token Service
|
||
@Service
|
||
public class JwtTokenService {
|
||
|
||
private static final String SECRET_KEY = "mySecretKey";
|
||
private static final int JWT_TOKEN_VALIDITY = 5 * 60 * 60; // 5 hours
|
||
|
||
@Value("${jwt.secret}")
|
||
private String jwtSecret;
|
||
|
||
public String generateToken(UserDetails userDetails) {
|
||
Map<String, Object> claims = new HashMap<>();
|
||
claims.put("roles", userDetails.getAuthorities());
|
||
claims.put("username", 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() + JWT_TOKEN_VALIDITY * 1000))
|
||
.signWith(SignatureAlgorithm.HS512, jwtSecret)
|
||
.compact();
|
||
}
|
||
|
||
public Boolean validateToken(String token, UserDetails userDetails) {
|
||
final String username = getUsernameFromToken(token);
|
||
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
||
}
|
||
|
||
public String getUsernameFromToken(String token) {
|
||
return getClaimFromToken(token, Claims::getSubject);
|
||
}
|
||
|
||
public Date getExpirationDateFromToken(String token) {
|
||
return getClaimFromToken(token, Claims::getExpiration);
|
||
}
|
||
|
||
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
|
||
final Claims claims = getAllClaimsFromToken(token);
|
||
return claimsResolver.apply(claims);
|
||
}
|
||
|
||
private Claims getAllClaimsFromToken(String token) {
|
||
return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody();
|
||
}
|
||
|
||
private Boolean isTokenExpired(String token) {
|
||
final Date expiration = getExpirationDateFromToken(token);
|
||
return expiration.before(new Date());
|
||
}
|
||
}
|
||
```
|
||
|
||
**Node.js примеры - безопасность:**
|
||
|
||
**JWT аутентификация с Express.js:**
|
||
```typescript
|
||
import express, { Request, Response, NextFunction } from 'express';
|
||
import jwt from 'jsonwebtoken';
|
||
import bcrypt from 'bcrypt';
|
||
import rateLimit from 'express-rate-limit';
|
||
import helmet from 'helmet';
|
||
import cors from 'cors';
|
||
import { PrismaClient } from '@prisma/client';
|
||
|
||
const app = express();
|
||
const prisma = new PrismaClient();
|
||
|
||
// Middleware для безопасности
|
||
app.use(helmet());
|
||
app.use(cors({
|
||
origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'],
|
||
credentials: true
|
||
}));
|
||
|
||
// Rate limiting
|
||
const limiter = rateLimit({
|
||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||
max: 100, // limit each IP to 100 requests per windowMs
|
||
message: 'Too many requests from this IP, please try again later.',
|
||
standardHeaders: true,
|
||
legacyHeaders: false,
|
||
});
|
||
|
||
app.use('/api/', limiter);
|
||
|
||
// JWT Token Service
|
||
class JwtTokenService {
|
||
private readonly secretKey: string;
|
||
private readonly tokenValidity: number = 5 * 60 * 60; // 5 hours
|
||
|
||
constructor() {
|
||
this.secretKey = process.env.JWT_SECRET || 'mySecretKey';
|
||
}
|
||
|
||
generateToken(userDetails: UserDetails): string {
|
||
const claims = {
|
||
roles: userDetails.roles,
|
||
username: userDetails.username,
|
||
userId: userDetails.id
|
||
};
|
||
|
||
return jwt.sign(
|
||
claims,
|
||
this.secretKey,
|
||
{
|
||
subject: userDetails.username,
|
||
expiresIn: this.tokenValidity,
|
||
issuer: 'my-app',
|
||
audience: 'my-app-users'
|
||
}
|
||
);
|
||
}
|
||
|
||
validateToken(token: string, userDetails: UserDetails): boolean {
|
||
try {
|
||
const decoded = jwt.verify(token, this.secretKey) as any;
|
||
return decoded.username === userDetails.username && !this.isTokenExpired(decoded);
|
||
} catch (error) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
getUsernameFromToken(token: string): string | null {
|
||
try {
|
||
const decoded = jwt.verify(token, this.secretKey) as any;
|
||
return decoded.username;
|
||
} catch (error) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
private isTokenExpired(decoded: any): boolean {
|
||
return Date.now() >= decoded.exp * 1000;
|
||
}
|
||
}
|
||
|
||
// Security Filter
|
||
class SecurityFilter {
|
||
constructor(private jwtService: JwtTokenService) {}
|
||
|
||
async authenticate(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||
try {
|
||
const token = this.extractToken(req);
|
||
|
||
if (!token) {
|
||
res.status(401).json({ error: 'Access token required' });
|
||
return;
|
||
}
|
||
|
||
const username = this.jwtService.getUsernameFromToken(token);
|
||
if (!username) {
|
||
res.status(401).json({ error: 'Invalid token' });
|
||
return;
|
||
}
|
||
|
||
req.user = { username };
|
||
next();
|
||
} catch (error) {
|
||
res.status(401).json({ error: 'Authentication failed' });
|
||
}
|
||
}
|
||
|
||
addSecurityHeaders(req: Request, res: Response, next: NextFunction): void {
|
||
res.setHeader('X-Content-Type-Options', 'nosniff');
|
||
res.setHeader('X-Frame-Options', 'DENY');
|
||
res.setHeader('X-XSS-Protection', '1; mode=block');
|
||
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
||
next();
|
||
}
|
||
|
||
private extractToken(req: Request): string | null {
|
||
const authHeader = req.headers.authorization;
|
||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||
return authHeader.substring(7);
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// API endpoints
|
||
app.post('/api/auth/login', async (req: Request, res: Response) => {
|
||
const { username, password } = req.body;
|
||
|
||
// Mock authentication
|
||
if (username === 'admin' && password === 'password') {
|
||
const jwtService = new JwtTokenService();
|
||
const token = jwtService.generateToken({
|
||
id: 1,
|
||
username: 'admin',
|
||
roles: ['ADMIN']
|
||
});
|
||
|
||
res.json({ token });
|
||
} else {
|
||
res.status(401).json({ error: 'Invalid credentials' });
|
||
}
|
||
});
|
||
|
||
app.get('/api/profile',
|
||
new SecurityFilter(new JwtTokenService()).authenticate.bind(new SecurityFilter(new JwtTokenService())),
|
||
(req: Request, res: Response) => {
|
||
res.json({ user: req.user });
|
||
}
|
||
);
|
||
|
||
app.listen(3000, () => {
|
||
console.log('Server running on port 3000');
|
||
});
|
||
```
|
||
|
||
// Rate Limiting
|
||
@Component
|
||
public class RateLimitingService {
|
||
|
||
private final Map<String, RateLimiter> limiters = new ConcurrentHashMap<>();
|
||
|
||
public boolean isAllowed(String key, int maxRequests, Duration window) {
|
||
RateLimiter limiter = limiters.computeIfAbsent(key, k ->
|
||
RateLimiter.create(maxRequests / window.getSeconds()));
|
||
|
||
return limiter.tryAcquire();
|
||
}
|
||
|
||
public boolean isAllowedForUser(String userId, int maxRequests, Duration window) {
|
||
return isAllowed("user:" + userId, maxRequests, window);
|
||
}
|
||
|
||
public boolean isAllowedForIP(String ip, int maxRequests, Duration window) {
|
||
return isAllowed("ip:" + ip, maxRequests, window);
|
||
}
|
||
}
|
||
|
||
// Security Filter
|
||
@Component
|
||
public class SecurityFilter implements Filter {
|
||
|
||
@Autowired
|
||
private RateLimitingService rateLimitingService;
|
||
|
||
@Override
|
||
public void doFilter(ServletRequest request, ServletResponse response,
|
||
FilterChain chain) throws IOException, ServletException {
|
||
|
||
HttpServletRequest httpRequest = (HttpServletRequest) request;
|
||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||
|
||
String clientIP = getClientIP(httpRequest);
|
||
String userId = getCurrentUserId();
|
||
|
||
// Rate limiting
|
||
if (!rateLimitingService.isAllowedForIP(clientIP, 100, Duration.ofMinutes(1))) {
|
||
httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
|
||
return;
|
||
}
|
||
|
||
if (userId != null && !rateLimitingService.isAllowedForUser(userId, 1000, Duration.ofMinutes(1))) {
|
||
httpResponse.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
|
||
return;
|
||
}
|
||
|
||
// 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");
|
||
|
||
chain.doFilter(request, response);
|
||
}
|
||
|
||
private String getClientIP(HttpServletRequest request) {
|
||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
|
||
return xForwardedFor.split(",")[0].trim();
|
||
}
|
||
return request.getRemoteAddr();
|
||
}
|
||
|
||
private String getCurrentUserId() {
|
||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||
if (auth != null && auth.isAuthenticated()) {
|
||
return auth.getName();
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
```
|
||
|
||
### Угрозы безопасности
|
||
|
||
#### 1. OWASP Top 10 (2021)
|
||
|
||
##### A01: Broken Access Control
|
||
```java
|
||
// Плохо: нет проверки прав
|
||
@GetMapping("/users/{id}")
|
||
public User getUser(@PathVariable Long id) {
|
||
return userRepository.findById(id);
|
||
}
|
||
|
||
// Хорошо: проверка прав
|
||
@GetMapping("/users/{id}")
|
||
@PreAuthorize("hasRole('ADMIN') or #id == authentication.principal.id")
|
||
public User getUser(@PathVariable Long id) {
|
||
return userRepository.findById(id);
|
||
}
|
||
```
|
||
|
||
##### A02: Cryptographic Failures
|
||
```java
|
||
// Плохо: слабое шифрование
|
||
@Value("${app.encryption.key:123456}")
|
||
private String encryptionKey;
|
||
|
||
// Хорошо: сильное шифрование
|
||
@Bean
|
||
public PasswordEncoder passwordEncoder() {
|
||
return new BCryptPasswordEncoder(12);
|
||
}
|
||
```
|
||
|
||
##### A03: Injection
|
||
```java
|
||
// Плохо: SQL injection
|
||
String query = "SELECT * FROM users WHERE name = '" + userName + "'";
|
||
|
||
// Хорошо: параметризованные запросы
|
||
@Query("SELECT u FROM User u WHERE u.name = :name")
|
||
User findByName(@Param("name") String name);
|
||
```
|
||
|
||
##### A04: Insecure Design
|
||
```java
|
||
// Плохо: небезопасный дизайн
|
||
public class User {
|
||
private String password; // пароль в открытом виде
|
||
|
||
public String getPassword() {
|
||
return password; // возвращаем пароль
|
||
}
|
||
}
|
||
|
||
// Хорошо: безопасный дизайн
|
||
public class User {
|
||
private String passwordHash; // хэш пароля
|
||
|
||
public void setPassword(String password) {
|
||
this.passwordHash = passwordEncoder.encode(password);
|
||
}
|
||
|
||
// нет геттера для пароля
|
||
}
|
||
```
|
||
|
||
##### A05: Security Misconfiguration
|
||
```yaml
|
||
# Плохо: небезопасная конфигурация
|
||
spring:
|
||
security:
|
||
user:
|
||
name: admin
|
||
password: admin123
|
||
|
||
# Хорошо: безопасная конфигурация
|
||
spring:
|
||
security:
|
||
user:
|
||
name: ${ADMIN_USERNAME:admin}
|
||
password: ${ADMIN_PASSWORD:}
|
||
```
|
||
|
||
---
|
||
|
||
## Уязвимости и межсервисное взаимодействие
|
||
|
||
### Типы уязвимостей в микросервисной архитектуре
|
||
|
||
#### 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 "))
|
||
.ok_or_else(|| actix_web::error::ErrorUnauthorized("Missing or invalid token"))?;
|
||
|
||
// Получение целевого сервиса из пути
|
||
let target_service = req.path().split('/').nth(1)
|
||
.ok_or_else(|| actix_web::error::ErrorBadRequest("Invalid service path"))?;
|
||
|
||
// Валидация токена
|
||
let middleware = TokenValidationMiddleware::new("your-secret-key");
|
||
let service_info = middleware.validate_service_token(token, target_service)
|
||
.map_err(|e| actix_web::error::ErrorUnauthorized(e))?;
|
||
|
||
// Добавление информации о сервисе в запрос
|
||
req.extensions_mut().insert(service_info);
|
||
|
||
next.run(req).await
|
||
}
|
||
|
||
// Handler с валидацией токена
|
||
async fn protected_endpoint(
|
||
req: HttpRequest,
|
||
service_info: web::Data<ServiceInfo>,
|
||
) -> Result<HttpResponse, Error> {
|
||
Ok(HttpResponse::Ok().json(serde_json::json!({
|
||
"message": "Access granted",
|
||
"service": service_info.service_name,
|
||
"permissions": service_info.permissions
|
||
})))
|
||
}
|
||
|
||
#[actix_web::main]
|
||
async fn main() -> std::io::Result<()> {
|
||
HttpServer::new(|| {
|
||
App::new()
|
||
.wrap(token_validation_middleware)
|
||
.route("/api/protected", web::get().to(protected_endpoint))
|
||
})
|
||
.bind("127.0.0.1:8080")?
|
||
.run()
|
||
.await
|
||
}
|
||
```
|
||
|
||
**Python примеры - TVM с FastAPI:**
|
||
|
||
```python
|
||
# Token Validation Middleware для FastAPI
|
||
from fastapi import FastAPI, HTTPException, Depends, Request
|
||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||
import jwt
|
||
from datetime import datetime, timedelta
|
||
from typing import List, Optional
|
||
import asyncio
|
||
|
||
app = FastAPI(title="TVM Service", version="1.0.0")
|
||
security = HTTPBearer()
|
||
|
||
class ServiceClaims:
|
||
def __init__(self, service_id: str, service_name: str,
|
||
allowed_services: List[str], permissions: List[str],
|
||
issued_at: datetime, expires_at: datetime):
|
||
self.service_id = service_id
|
||
self.service_name = service_name
|
||
self.allowed_services = allowed_services
|
||
self.permissions = permissions
|
||
self.issued_at = issued_at
|
||
self.expires_at = expires_at
|
||
|
||
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) -> ServiceClaims:
|
||
try:
|
||
# Декодирование токена
|
||
payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
|
||
|
||
# Проверка срока действия
|
||
exp_timestamp = payload.get('exp')
|
||
if exp_timestamp and datetime.utcnow().timestamp() > exp_timestamp:
|
||
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}"
|
||
)
|
||
|
||
# Создание объекта ServiceClaims
|
||
return ServiceClaims(
|
||
service_id=payload.get('sub'),
|
||
service_name=payload.get('service_name'),
|
||
allowed_services=allowed_services,
|
||
permissions=payload.get('permissions', []),
|
||
issued_at=datetime.fromtimestamp(payload.get('iat', 0)),
|
||
expires_at=datetime.fromtimestamp(exp_timestamp)
|
||
)
|
||
|
||
except jwt.ExpiredSignatureError:
|
||
raise HTTPException(status_code=401, detail="Token expired")
|
||
except jwt.InvalidTokenError:
|
||
raise HTTPException(status_code=401, detail="Invalid token")
|
||
|
||
# Dependency для валидации токена
|
||
async def validate_token(
|
||
request: Request,
|
||
credentials: HTTPAuthorizationCredentials = Depends(security)
|
||
) -> ServiceClaims:
|
||
token = credentials.credentials
|
||
|
||
# Получение целевого сервиса из пути
|
||
path_parts = request.url.path.split('/')
|
||
target_service = path_parts[1] if len(path_parts) > 1 else "default"
|
||
|
||
# Валидация токена
|
||
middleware = TokenValidationMiddleware("your-secret-key")
|
||
return middleware.validate_service_token(token, target_service)
|
||
|
||
# API endpoints
|
||
@app.get("/api/protected")
|
||
async def protected_endpoint(service_info: ServiceClaims = Depends(validate_token)):
|
||
return {
|
||
"message": "Access granted",
|
||
"service": service_info.service_name,
|
||
"permissions": service_info.permissions,
|
||
"service_id": service_info.service_id
|
||
}
|
||
|
||
@app.get("/api/services/{service_name}/data")
|
||
async def get_service_data(
|
||
service_name: str,
|
||
service_info: ServiceClaims = Depends(validate_token)
|
||
):
|
||
# Проверка дополнительных прав
|
||
if "read_data" not in service_info.permissions:
|
||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||
|
||
return {
|
||
"service": service_name,
|
||
"data": f"Data for {service_name}",
|
||
"accessed_by": service_info.service_name
|
||
}
|
||
|
||
# Utility для создания токенов (для тестирования)
|
||
def create_service_token(service_id: str, service_name: str,
|
||
allowed_services: List[str], permissions: List[str]) -> str:
|
||
payload = {
|
||
"sub": service_id,
|
||
"service_name": service_name,
|
||
"allowed_services": allowed_services,
|
||
"permissions": permissions,
|
||
"iat": datetime.utcnow(),
|
||
"exp": datetime.utcnow() + timedelta(hours=1)
|
||
}
|
||
|
||
return jwt.encode(payload, "your-secret-key", algorithm="HS256")
|
||
|
||
if __name__ == "__main__":
|
||
import uvicorn
|
||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||
```
|
||
|
||
**Node.js примеры - TVM с Express.js:**
|
||
|
||
```typescript
|
||
// Token Validation Middleware для Express.js
|
||
import express, { Request, Response, NextFunction } from 'express';
|
||
import jwt from 'jsonwebtoken';
|
||
import { z } from 'zod';
|
||
|
||
const app = express();
|
||
|
||
// Interfaces
|
||
interface ServiceClaims {
|
||
sub: string; // service_id
|
||
service_name: string;
|
||
allowed_services: string[];
|
||
permissions: string[];
|
||
iat: number; // issued_at
|
||
exp: number; // expires_at
|
||
}
|
||
|
||
interface ServiceInfo {
|
||
serviceId: string;
|
||
serviceName: string;
|
||
permissions: string[];
|
||
}
|
||
|
||
// Token Validation Middleware
|
||
class TokenValidationMiddleware {
|
||
private secretKey: string;
|
||
private algorithm: jwt.Algorithm = 'HS256';
|
||
|
||
constructor(secretKey: string) {
|
||
this.secretKey = secretKey;
|
||
}
|
||
|
||
validateServiceToken(token: string, targetService: string): ServiceInfo {
|
||
try {
|
||
// Декодирование токена
|
||
const decoded = jwt.verify(token, this.secretKey, {
|
||
algorithms: [this.algorithm]
|
||
}) as ServiceClaims;
|
||
|
||
// Проверка срока действия
|
||
const now = Math.floor(Date.now() / 1000);
|
||
if (decoded.exp < now) {
|
||
throw new Error('Token expired');
|
||
}
|
||
|
||
// Проверка прав доступа к сервису
|
||
if (!decoded.allowed_services.includes(targetService)) {
|
||
throw new Error(`No access to service: ${targetService}`);
|
||
}
|
||
|
||
return {
|
||
serviceId: decoded.sub,
|
||
serviceName: decoded.service_name,
|
||
permissions: decoded.permissions
|
||
};
|
||
|
||
} catch (error) {
|
||
if (error instanceof jwt.JsonWebTokenError) {
|
||
throw new Error('Invalid token');
|
||
}
|
||
throw error;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Middleware для Express.js
|
||
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 or invalid token' });
|
||
}
|
||
|
||
const token = authHeader.substring(7);
|
||
|
||
// Получение целевого сервиса из пути
|
||
const pathParts = req.path.split('/');
|
||
const targetService = pathParts[2] || 'default'; // /api/services/{service}
|
||
|
||
// Валидация токена
|
||
const middleware = new TokenValidationMiddleware('your-secret-key');
|
||
const serviceInfo = middleware.validateServiceToken(token, targetService);
|
||
|
||
// Добавление информации о сервисе в запрос
|
||
(req as any).serviceInfo = serviceInfo;
|
||
|
||
next();
|
||
} catch (error) {
|
||
res.status(401).json({ error: error.message });
|
||
}
|
||
};
|
||
|
||
// API endpoints
|
||
app.get('/api/protected', tokenValidationMiddleware, (req: Request, res: Response) => {
|
||
const serviceInfo = (req as any).serviceInfo;
|
||
|
||
res.json({
|
||
message: 'Access granted',
|
||
service: serviceInfo.serviceName,
|
||
permissions: serviceInfo.permissions,
|
||
serviceId: serviceInfo.serviceId
|
||
});
|
||
});
|
||
|
||
app.get('/api/services/:serviceName/data', tokenValidationMiddleware, (req: Request, res: Response) => {
|
||
const serviceInfo = (req as any).serviceInfo;
|
||
const { serviceName } = req.params;
|
||
|
||
// Проверка дополнительных прав
|
||
if (!serviceInfo.permissions.includes('read_data')) {
|
||
return res.status(403).json({ error: 'Insufficient permissions' });
|
||
}
|
||
|
||
res.json({
|
||
service: serviceName,
|
||
data: `Data for ${serviceName}`,
|
||
accessedBy: serviceInfo.serviceName
|
||
});
|
||
});
|
||
|
||
// Utility для создания токенов (для тестирования)
|
||
function createServiceToken(
|
||
serviceId: string,
|
||
serviceName: string,
|
||
allowedServices: string[],
|
||
permissions: string[]
|
||
): string {
|
||
const payload = {
|
||
sub: serviceId,
|
||
service_name: serviceName,
|
||
allowed_services: allowedServices,
|
||
permissions: permissions,
|
||
iat: Math.floor(Date.now() / 1000),
|
||
exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1 hour
|
||
};
|
||
|
||
return jwt.sign(payload, 'your-secret-key', { algorithm: 'HS256' });
|
||
}
|
||
|
||
// Error handling middleware
|
||
app.use((error: Error, req: Request, res: Response, next: NextFunction) => {
|
||
console.error(error);
|
||
res.status(500).json({ error: 'Internal server error' });
|
||
});
|
||
|
||
app.listen(3000, () => {
|
||
console.log('TVM Service running on port 3000');
|
||
});
|
||
```
|
||
|
||
#### 3. Безопасность межсервисного взаимодействия
|
||
|
||
**Принципы:**
|
||
- **Zero Trust** — не доверяй, проверяй
|
||
- **Least Privilege** — минимальные права доступа
|
||
- **Defense in Depth** — многоуровневая защита
|
||
- **Audit Everything** — логирование всех операций
|
||
|
||
**Паттерны безопасности:**
|
||
- Service Mesh с mTLS
|
||
- API Gateway с аутентификацией
|
||
- Token-based аутентификация
|
||
- Rate limiting и throttling
|
||
- Circuit breaker для защиты от DoS
|
||
|
||
---
|
||
|
||
## Полный каталог уязвимостей информационной безопасности
|
||
|
||
### OWASP Top 10 (2021) - Детальный анализ
|
||
|
||
#### A01: Broken Access Control (Нарушение контроля доступа)
|
||
|
||
**Описание:** Неправильная реализация ограничений доступа, позволяющая пользователям действовать вне своих предполагаемых привилегий.
|
||
|
||
**Примеры уязвимостей:**
|
||
- Обход авторизации путем изменения URL
|
||
- Принудительный просмотр, изменение или удаление данных других пользователей
|
||
- Повышение привилегий
|
||
- Метаданные манипуляции (JWT, cookies)
|
||
- CORS неправильная конфигурация
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Правильная реализация контроля доступа
|
||
@RestController
|
||
@RequestMapping("/api/users")
|
||
public class UserController {
|
||
|
||
@GetMapping("/{userId}")
|
||
@PreAuthorize("hasRole('USER') and #userId == authentication.principal.id")
|
||
public ResponseEntity<User> getUser(@PathVariable Long userId) {
|
||
// Проверка прав доступа встроена в аннотацию
|
||
return ResponseEntity.ok(userService.findById(userId));
|
||
}
|
||
|
||
@GetMapping("/admin")
|
||
@PreAuthorize("hasRole('ADMIN')")
|
||
public ResponseEntity<List<User>> getAllUsers() {
|
||
return ResponseEntity.ok(userService.findAll());
|
||
}
|
||
}
|
||
|
||
// Дополнительная проверка на уровне сервиса
|
||
@Service
|
||
public class UserService {
|
||
|
||
public User findById(Long userId) {
|
||
User currentUser = getCurrentUser();
|
||
|
||
// Проверка: пользователь может видеть только свои данные
|
||
if (!currentUser.getId().equals(userId) && !currentUser.hasRole("ADMIN")) {
|
||
throw new AccessDeniedException("Access denied");
|
||
}
|
||
|
||
return userRepository.findById(userId)
|
||
.orElseThrow(() -> new UserNotFoundException("User not found"));
|
||
}
|
||
}
|
||
```
|
||
|
||
**Rust примеры - Контроль доступа:**
|
||
|
||
```rust
|
||
// Контроль доступа в Rust
|
||
use actix_web::{web, HttpRequest, HttpResponse, Error};
|
||
use serde::{Deserialize, Serialize};
|
||
|
||
#[derive(Debug, Serialize, Deserialize)]
|
||
struct User {
|
||
id: u64,
|
||
name: String,
|
||
role: String,
|
||
}
|
||
|
||
#[derive(Debug, Serialize, Deserialize)]
|
||
struct AccessToken {
|
||
user_id: u64,
|
||
role: String,
|
||
permissions: Vec<String>,
|
||
}
|
||
|
||
// Middleware для проверки доступа
|
||
pub async fn access_control_middleware(
|
||
req: HttpRequest,
|
||
next: actix_web::middleware::Next,
|
||
) -> Result<HttpResponse, Error> {
|
||
// Извлечение токена
|
||
let token = extract_token(&req)?;
|
||
|
||
// Валидация токена
|
||
let access_token = validate_token(token)?;
|
||
|
||
// Проверка прав доступа
|
||
if !has_permission(&access_token, &req.path()) {
|
||
return Ok(HttpResponse::Forbidden().json(serde_json::json!({
|
||
"error": "Access denied"
|
||
})));
|
||
}
|
||
|
||
// Добавление информации о пользователе в запрос
|
||
req.extensions_mut().insert(access_token);
|
||
|
||
next.run(req).await
|
||
}
|
||
|
||
fn extract_token(req: &HttpRequest) -> Result<String, Error> {
|
||
req.headers()
|
||
.get("Authorization")
|
||
.and_then(|h| h.to_str().ok())
|
||
.and_then(|s| s.strip_prefix("Bearer "))
|
||
.ok_or_else(|| actix_web::error::ErrorUnauthorized("Missing token"))
|
||
.map(|s| s.to_string())
|
||
}
|
||
|
||
fn validate_token(token: &str) -> Result<AccessToken, Error> {
|
||
// Здесь должна быть реальная валидация JWT
|
||
// Для примера возвращаем mock данные
|
||
Ok(AccessToken {
|
||
user_id: 1,
|
||
role: "USER".to_string(),
|
||
permissions: vec!["read:own_data".to_string()],
|
||
})
|
||
}
|
||
|
||
fn has_permission(token: &AccessToken, path: &str) -> bool {
|
||
// Проверка прав доступа на основе пути и роли
|
||
match path {
|
||
path if path.startsWith("/api/users/") => {
|
||
// Пользователь может видеть только свои данные
|
||
if token.permissions.contains(&"read:own_data".to_string()) {
|
||
return true;
|
||
}
|
||
}
|
||
path if path.startsWith("/api/admin/") => {
|
||
// Только админы могут получить доступ к админ панели
|
||
return token.role == "ADMIN";
|
||
}
|
||
_ => return false,
|
||
}
|
||
false
|
||
}
|
||
```
|
||
|
||
#### A02: Cryptographic Failures (Криптографические сбои)
|
||
|
||
**Описание:** Неправильное использование криптографии или слабые криптографические алгоритмы.
|
||
|
||
**Примеры уязвимостей:**
|
||
- Передача данных без шифрования
|
||
- Использование слабых алгоритмов шифрования
|
||
- Неправильное управление ключами
|
||
- Использование устаревших алгоритмов (MD5, SHA-1)
|
||
- Хранение паролей в открытом виде
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Правильное шифрование данных
|
||
@Service
|
||
public class EncryptionService {
|
||
|
||
private static final String ALGORITHM = "AES/GCM/NoPadding";
|
||
private static final int KEY_LENGTH = 256;
|
||
private static final int IV_LENGTH = 12;
|
||
private static final int TAG_LENGTH = 16;
|
||
|
||
@Value("${encryption.key}")
|
||
private String encryptionKey;
|
||
|
||
public String encrypt(String plaintext) {
|
||
try {
|
||
// Генерация случайного IV
|
||
byte[] iv = new byte[IV_LENGTH];
|
||
SecureRandom.getInstanceStrong().nextBytes(iv);
|
||
|
||
// Создание ключа
|
||
SecretKeySpec keySpec = new SecretKeySpec(
|
||
encryptionKey.getBytes(StandardCharsets.UTF_8), "AES");
|
||
|
||
// Инициализация шифра
|
||
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||
GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv);
|
||
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec);
|
||
|
||
// Шифрование
|
||
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
|
||
|
||
// Объединение IV и зашифрованных данных
|
||
byte[] encryptedData = new byte[IV_LENGTH + ciphertext.length];
|
||
System.arraycopy(iv, 0, encryptedData, 0, IV_LENGTH);
|
||
System.arraycopy(ciphertext, 0, encryptedData, IV_LENGTH, ciphertext.length);
|
||
|
||
return Base64.getEncoder().encodeToString(encryptedData);
|
||
|
||
} catch (Exception e) {
|
||
throw new EncryptionException("Failed to encrypt data", e);
|
||
}
|
||
}
|
||
|
||
public String decrypt(String encryptedData) {
|
||
try {
|
||
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
|
||
|
||
// Извлечение IV
|
||
byte[] iv = new byte[IV_LENGTH];
|
||
System.arraycopy(encryptedBytes, 0, iv, 0, IV_LENGTH);
|
||
|
||
// Извлечение зашифрованных данных
|
||
byte[] ciphertext = new byte[encryptedBytes.length - IV_LENGTH];
|
||
System.arraycopy(encryptedBytes, IV_LENGTH, ciphertext, 0, ciphertext.length);
|
||
|
||
// Создание ключа
|
||
SecretKeySpec keySpec = new SecretKeySpec(
|
||
encryptionKey.getBytes(StandardCharsets.UTF_8), "AES");
|
||
|
||
// Инициализация шифра для расшифровки
|
||
Cipher cipher = Cipher.getInstance(ALGORITHM);
|
||
GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH * 8, iv);
|
||
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec);
|
||
|
||
// Расшифровка
|
||
byte[] plaintext = cipher.doFinal(ciphertext);
|
||
return new String(plaintext, StandardCharsets.UTF_8);
|
||
|
||
} catch (Exception e) {
|
||
throw new EncryptionException("Failed to decrypt data", e);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Правильное хеширование паролей
|
||
@Service
|
||
public class PasswordService {
|
||
|
||
private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(12);
|
||
|
||
public String hashPassword(String plainPassword) {
|
||
return passwordEncoder.encode(plainPassword);
|
||
}
|
||
|
||
public boolean verifyPassword(String plainPassword, String hashedPassword) {
|
||
return passwordEncoder.matches(plainPassword, hashedPassword);
|
||
}
|
||
|
||
// Проверка силы пароля
|
||
public boolean isPasswordStrong(String password) {
|
||
if (password.length() < 8) return false;
|
||
|
||
boolean hasUpperCase = password.chars().anyMatch(Character::isUpperCase);
|
||
boolean hasLowerCase = password.chars().anyMatch(Character::isLowerCase);
|
||
boolean hasDigit = password.chars().anyMatch(Character::isDigit);
|
||
boolean hasSpecialChar = password.chars().anyMatch(ch -> "!@#$%^&*()_+-=[]{}|;:,.<>?".contains(String.valueOf(ch)));
|
||
|
||
return hasUpperCase && hasLowerCase && hasDigit && hasSpecialChar;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### A03: Injection (Инъекции)
|
||
|
||
**Описание:** Внедрение вредоносного кода через пользовательский ввод.
|
||
|
||
**Типы инъекций:**
|
||
- SQL Injection
|
||
- NoSQL Injection
|
||
- Command Injection
|
||
- LDAP Injection
|
||
- XPath Injection
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Защита от SQL Injection
|
||
@Repository
|
||
public class UserRepository {
|
||
|
||
@Autowired
|
||
private JdbcTemplate jdbcTemplate;
|
||
|
||
// НЕПРАВИЛЬНО - уязвимо к SQL Injection
|
||
public List<User> findByUsernameUnsafe(String username) {
|
||
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
|
||
return jdbcTemplate.query(sql, new UserRowMapper());
|
||
}
|
||
|
||
// ПРАВИЛЬНО - использование параметризованных запросов
|
||
public List<User> findByUsernameSafe(String username) {
|
||
String sql = "SELECT * FROM users WHERE username = ?";
|
||
return jdbcTemplate.query(sql, new Object[]{username}, new UserRowMapper());
|
||
}
|
||
|
||
// Использование JPA с именованными параметрами
|
||
@Query("SELECT u FROM User u WHERE u.username = :username")
|
||
List<User> findByUsername(@Param("username") String username);
|
||
}
|
||
|
||
// Защита от NoSQL Injection
|
||
@Service
|
||
public class UserService {
|
||
|
||
@Autowired
|
||
private MongoTemplate mongoTemplate;
|
||
|
||
// НЕПРАВИЛЬНО - уязвимо к NoSQL Injection
|
||
public List<User> findByUsernameUnsafe(String username) {
|
||
Query query = new Query();
|
||
query.addCriteria(Criteria.where("username").is(username));
|
||
return mongoTemplate.find(query, User.class);
|
||
}
|
||
|
||
// ПРАВИЛЬНО - валидация и санитизация ввода
|
||
public List<User> findByUsernameSafe(String username) {
|
||
// Валидация ввода
|
||
if (!isValidUsername(username)) {
|
||
throw new IllegalArgumentException("Invalid username format");
|
||
}
|
||
|
||
// Санитизация ввода
|
||
String sanitizedUsername = sanitizeInput(username);
|
||
|
||
Query query = new Query();
|
||
query.addCriteria(Criteria.where("username").is(sanitizedUsername));
|
||
return mongoTemplate.find(query, User.class);
|
||
}
|
||
|
||
private boolean isValidUsername(String username) {
|
||
// Проверка на допустимые символы
|
||
return username.matches("^[a-zA-Z0-9_]{3,20}$");
|
||
}
|
||
|
||
private String sanitizeInput(String input) {
|
||
// Удаление потенциально опасных символов
|
||
return input.replaceAll("[^a-zA-Z0-9_]", "");
|
||
}
|
||
}
|
||
|
||
// Защита от Command Injection
|
||
@Service
|
||
public class SystemService {
|
||
|
||
// НЕПРАВИЛЬНО - уязвимо к Command Injection
|
||
public String executeCommandUnsafe(String command) {
|
||
try {
|
||
Process process = Runtime.getRuntime().exec(command);
|
||
// обработка результата...
|
||
} catch (IOException e) {
|
||
throw new RuntimeException("Command execution failed", e);
|
||
}
|
||
}
|
||
|
||
// ПРАВИЛЬНО - использование ProcessBuilder с валидацией
|
||
public String executeCommandSafe(String command, String... args) {
|
||
// Валидация команды
|
||
if (!isAllowedCommand(command)) {
|
||
throw new IllegalArgumentException("Command not allowed");
|
||
}
|
||
|
||
// Валидация аргументов
|
||
for (String arg : args) {
|
||
if (!isValidArgument(arg)) {
|
||
throw new IllegalArgumentException("Invalid argument: " + arg);
|
||
}
|
||
}
|
||
|
||
try {
|
||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||
processBuilder.command().addAll(Arrays.asList(args));
|
||
|
||
Process process = processBuilder.start();
|
||
// обработка результата...
|
||
|
||
} catch (IOException e) {
|
||
throw new RuntimeException("Command execution failed", e);
|
||
}
|
||
}
|
||
|
||
private boolean isAllowedCommand(String command) {
|
||
Set<String> allowedCommands = Set.of("ls", "ps", "df", "whoami");
|
||
return allowedCommands.contains(command);
|
||
}
|
||
|
||
private boolean isValidArgument(String arg) {
|
||
// Проверка на допустимые символы
|
||
return arg.matches("^[a-zA-Z0-9._/-]+$");
|
||
}
|
||
}
|
||
```
|
||
|
||
#### A04: Insecure Design (Небезопасный дизайн)
|
||
|
||
**Описание:** Недостатки в архитектуре и дизайне приложения.
|
||
|
||
**Примеры проблем:**
|
||
- Отсутствие threat modeling
|
||
- Неправильная архитектура безопасности
|
||
- Отсутствие принципа "fail secure"
|
||
- Недостаточная изоляция компонентов
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Безопасный дизайн архитектуры
|
||
@Configuration
|
||
@EnableWebSecurity
|
||
public class SecurityConfig {
|
||
|
||
@Bean
|
||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||
http
|
||
// Принцип "fail secure" - по умолчанию все заблокировано
|
||
.authorizeHttpRequests(authz -> authz
|
||
.requestMatchers("/api/public/**").permitAll()
|
||
.requestMatchers("/api/admin/**").hasRole("ADMIN")
|
||
.anyRequest().authenticated()
|
||
)
|
||
// Защита от CSRF
|
||
.csrf(csrf -> csrf
|
||
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
|
||
)
|
||
// Защита от clickjacking
|
||
.headers(headers -> headers
|
||
.frameOptions().deny()
|
||
.contentTypeOptions().and()
|
||
.httpStrictTransportSecurity(hstsConfig -> hstsConfig
|
||
.maxAgeInSeconds(31536000)
|
||
.includeSubdomains(true)
|
||
)
|
||
)
|
||
// Сессии
|
||
.sessionManagement(session -> session
|
||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||
.maximumSessions(1)
|
||
.maxSessionsPreventsLogin(true)
|
||
)
|
||
// Обработка ошибок
|
||
.exceptionHandling(exceptions -> exceptions
|
||
.authenticationEntryPoint(customAuthenticationEntryPoint())
|
||
.accessDeniedHandler(customAccessDeniedHandler())
|
||
);
|
||
|
||
return http.build();
|
||
}
|
||
|
||
@Bean
|
||
public AuthenticationEntryPoint customAuthenticationEntryPoint() {
|
||
return (request, response, authException) -> {
|
||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||
response.setContentType("application/json");
|
||
response.getWriter().write("{\"error\":\"Unauthorized\"}");
|
||
};
|
||
}
|
||
|
||
@Bean
|
||
public AccessDeniedHandler customAccessDeniedHandler() {
|
||
return (request, response, accessDeniedException) -> {
|
||
response.setStatus(HttpStatus.FORBIDDEN.value());
|
||
response.setContentType("application/json");
|
||
response.getWriter().write("{\"error\":\"Access denied\"}");
|
||
};
|
||
}
|
||
}
|
||
|
||
// Threat Modeling в коде
|
||
@Component
|
||
public class ThreatModelingService {
|
||
|
||
// Анализ угроз для каждого endpoint
|
||
public ThreatAnalysis analyzeEndpoint(String endpoint, String method) {
|
||
ThreatAnalysis analysis = new ThreatAnalysis();
|
||
|
||
// STRIDE анализ
|
||
analysis.addThreat(ThreatType.SPOOFING, "User impersonation");
|
||
analysis.addThreat(ThreatType.TAMPERING, "Data modification");
|
||
analysis.addThreat(ThreatType.REPUDIATION, "Action denial");
|
||
analysis.addThreat(ThreatType.INFORMATION_DISCLOSURE, "Data exposure");
|
||
analysis.addThreat(ThreatType.DENIAL_OF_SERVICE, "Service unavailability");
|
||
analysis.addThreat(ThreatType.ELEVATION_OF_PRIVILEGE, "Unauthorized access");
|
||
|
||
return analysis;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### A05: Security Misconfiguration (Неправильная конфигурация безопасности)
|
||
|
||
**Описание:** Неправильная настройка компонентов безопасности.
|
||
|
||
**Примеры проблем:**
|
||
- Отсутствие обновлений безопасности
|
||
- Небезопасные настройки по умолчанию
|
||
- Открытые ненужные порты
|
||
- Неправильная конфигурация CORS
|
||
- Отсутствие заголовков безопасности
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Безопасная конфигурация
|
||
@Configuration
|
||
public class SecurityConfiguration {
|
||
|
||
@Bean
|
||
public CorsConfigurationSource corsConfigurationSource() {
|
||
CorsConfiguration configuration = new CorsConfiguration();
|
||
|
||
// Ограничение источников
|
||
configuration.setAllowedOriginPatterns(List.of("https://*.example.com"));
|
||
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
|
||
configuration.setAllowedHeaders(List.of("*"));
|
||
configuration.setAllowCredentials(true);
|
||
configuration.setMaxAge(3600L);
|
||
|
||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||
source.registerCorsConfiguration("/api/**", configuration);
|
||
|
||
return source;
|
||
}
|
||
|
||
@Bean
|
||
public FilterRegistrationBean<SecurityHeadersFilter> securityHeadersFilter() {
|
||
FilterRegistrationBean<SecurityHeadersFilter> registrationBean = new FilterRegistrationBean<>();
|
||
registrationBean.setFilter(new SecurityHeadersFilter());
|
||
registrationBean.addUrlPatterns("/*");
|
||
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||
return registrationBean;
|
||
}
|
||
}
|
||
|
||
// Фильтр для добавления заголовков безопасности
|
||
public class SecurityHeadersFilter implements Filter {
|
||
|
||
@Override
|
||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||
throws IOException, ServletException {
|
||
|
||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||
|
||
// Заголовки безопасности
|
||
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("Referrer-Policy", "strict-origin-when-cross-origin");
|
||
httpResponse.setHeader("Content-Security-Policy",
|
||
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'");
|
||
|
||
chain.doFilter(request, response);
|
||
}
|
||
}
|
||
|
||
// Проверка конфигурации безопасности
|
||
@Component
|
||
public class SecurityConfigurationValidator {
|
||
|
||
@EventListener
|
||
public void onApplicationReady(ApplicationReadyEvent event) {
|
||
validateSecurityConfiguration();
|
||
}
|
||
|
||
private void validateSecurityConfiguration() {
|
||
// Проверка переменных окружения
|
||
validateEnvironmentVariables();
|
||
|
||
// Проверка конфигурации базы данных
|
||
validateDatabaseConfiguration();
|
||
|
||
// Проверка SSL/TLS
|
||
validateSSLConfiguration();
|
||
}
|
||
|
||
private void validateEnvironmentVariables() {
|
||
String[] requiredVars = {
|
||
"JWT_SECRET", "ENCRYPTION_KEY", "DB_PASSWORD", "ADMIN_PASSWORD"
|
||
};
|
||
|
||
for (String var : requiredVars) {
|
||
if (System.getenv(var) == null) {
|
||
throw new SecurityConfigurationException("Required environment variable not set: " + var);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void validateDatabaseConfiguration() {
|
||
// Проверка подключения к БД с SSL
|
||
// Проверка прав доступа к БД
|
||
// Проверка настроек пула соединений
|
||
}
|
||
|
||
private void validateSSLConfiguration() {
|
||
// Проверка SSL сертификатов
|
||
// Проверка TLS версии
|
||
// Проверка cipher suites
|
||
}
|
||
}
|
||
```
|
||
|
||
#### A06: Vulnerable and Outdated Components (Уязвимые и устаревшие компоненты)
|
||
|
||
**Описание:** Использование компонентов с известными уязвимостями.
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Автоматическая проверка уязвимостей
|
||
@Component
|
||
public class VulnerabilityScanner {
|
||
|
||
@Scheduled(cron = "0 0 2 * * ?") // Каждый день в 2:00
|
||
public void scanForVulnerabilities() {
|
||
scanDependencies();
|
||
scanSystemComponents();
|
||
generateReport();
|
||
}
|
||
|
||
private void scanDependencies() {
|
||
// Сканирование Maven зависимостей
|
||
List<Dependency> dependencies = getMavenDependencies();
|
||
|
||
for (Dependency dependency : dependencies) {
|
||
List<Vulnerability> vulnerabilities = checkVulnerabilityDatabase(dependency);
|
||
|
||
if (!vulnerabilities.isEmpty()) {
|
||
logVulnerabilities(dependency, vulnerabilities);
|
||
notifySecurityTeam(dependency, vulnerabilities);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void scanSystemComponents() {
|
||
// Проверка версий Java, Spring Boot, и других компонентов
|
||
String javaVersion = System.getProperty("java.version");
|
||
String springVersion = SpringBootVersion.getVersion();
|
||
|
||
checkComponentVersions(javaVersion, springVersion);
|
||
}
|
||
|
||
private void generateReport() {
|
||
// Генерация отчета об уязвимостях
|
||
VulnerabilityReport report = new VulnerabilityReport();
|
||
report.setScanDate(LocalDateTime.now());
|
||
report.setVulnerabilities(foundVulnerabilities);
|
||
|
||
saveReport(report);
|
||
}
|
||
}
|
||
|
||
// Конфигурация для автоматических обновлений
|
||
@Configuration
|
||
public class DependencyManagementConfig {
|
||
|
||
@Bean
|
||
public DependencyUpdatesChecker dependencyUpdatesChecker() {
|
||
return new DependencyUpdatesChecker();
|
||
}
|
||
}
|
||
|
||
@Component
|
||
public class DependencyUpdatesChecker {
|
||
|
||
@Scheduled(cron = "0 0 1 * * ?") // Каждый день в 1:00
|
||
public void checkForUpdates() {
|
||
List<DependencyUpdate> updates = findAvailableUpdates();
|
||
|
||
for (DependencyUpdate update : updates) {
|
||
if (update.isSecurityUpdate()) {
|
||
notifySecurityTeam(update);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### A07: Identification and Authentication Failures (Сбои идентификации и аутентификации)
|
||
|
||
**Описание:** Неправильная реализация аутентификации и идентификации.
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Безопасная аутентификация
|
||
@Service
|
||
public class AuthenticationService {
|
||
|
||
@Autowired
|
||
private PasswordService passwordService;
|
||
|
||
@Autowired
|
||
private RateLimitingService rateLimitingService;
|
||
|
||
@Autowired
|
||
private AuditService auditService;
|
||
|
||
public AuthenticationResult authenticate(String username, String password, String ipAddress) {
|
||
// Проверка rate limiting
|
||
if (!rateLimitingService.isAllowed(ipAddress)) {
|
||
auditService.logFailedLogin(username, ipAddress, "Rate limit exceeded");
|
||
throw new AuthenticationException("Too many login attempts");
|
||
}
|
||
|
||
// Поиск пользователя
|
||
User user = userService.findByUsername(username);
|
||
if (user == null) {
|
||
auditService.logFailedLogin(username, ipAddress, "User not found");
|
||
throw new AuthenticationException("Invalid credentials");
|
||
}
|
||
|
||
// Проверка блокировки аккаунта
|
||
if (user.isLocked()) {
|
||
auditService.logFailedLogin(username, ipAddress, "Account locked");
|
||
throw new AuthenticationException("Account is locked");
|
||
}
|
||
|
||
// Проверка пароля
|
||
if (!passwordService.verifyPassword(password, user.getPasswordHash())) {
|
||
handleFailedLogin(user, ipAddress);
|
||
throw new AuthenticationException("Invalid credentials");
|
||
}
|
||
|
||
// Проверка двухфакторной аутентификации
|
||
if (user.isTwoFactorEnabled()) {
|
||
return AuthenticationResult.twoFactorRequired(user.getId());
|
||
}
|
||
|
||
// Успешная аутентификация
|
||
auditService.logSuccessfulLogin(username, ipAddress);
|
||
userService.resetFailedLoginAttempts(user.getId());
|
||
|
||
return AuthenticationResult.success(user);
|
||
}
|
||
|
||
private void handleFailedLogin(User user, String ipAddress) {
|
||
auditService.logFailedLogin(user.getUsername(), ipAddress, "Invalid password");
|
||
|
||
int failedAttempts = userService.incrementFailedLoginAttempts(user.getId());
|
||
|
||
if (failedAttempts >= 5) {
|
||
userService.lockAccount(user.getId());
|
||
auditService.logAccountLocked(user.getUsername(), ipAddress);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Rate Limiting Service
|
||
@Service
|
||
public class RateLimitingService {
|
||
|
||
@Autowired
|
||
private RedisTemplate<String, String> redisTemplate;
|
||
|
||
public boolean isAllowed(String ipAddress) {
|
||
String key = "login_attempts:" + ipAddress;
|
||
|
||
// Получение текущего количества попыток
|
||
String attemptsStr = redisTemplate.opsForValue().get(key);
|
||
int attempts = attemptsStr != null ? Integer.parseInt(attemptsStr) : 0;
|
||
|
||
// Проверка лимита
|
||
if (attempts >= 5) {
|
||
return false;
|
||
}
|
||
|
||
// Увеличение счетчика
|
||
redisTemplate.opsForValue().increment(key);
|
||
redisTemplate.expire(key, Duration.ofMinutes(15));
|
||
|
||
return true;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### A08: Software and Data Integrity Failures (Сбои целостности ПО и данных)
|
||
|
||
**Описание:** Недостаточная проверка целостности данных и кода.
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Проверка целостности данных
|
||
@Service
|
||
public class DataIntegrityService {
|
||
|
||
public String calculateChecksum(String data) {
|
||
try {
|
||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||
byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
|
||
return Base64.getEncoder().encodeToString(hash);
|
||
} catch (NoSuchAlgorithmException e) {
|
||
throw new RuntimeException("SHA-256 algorithm not available", e);
|
||
}
|
||
}
|
||
|
||
public boolean verifyDataIntegrity(String data, String expectedChecksum) {
|
||
String actualChecksum = calculateChecksum(data);
|
||
return actualChecksum.equals(expectedChecksum);
|
||
}
|
||
|
||
public void validateFileIntegrity(String filePath, String expectedChecksum) {
|
||
try {
|
||
String fileContent = Files.readString(Paths.get(filePath));
|
||
if (!verifyDataIntegrity(fileContent, expectedChecksum)) {
|
||
throw new DataIntegrityException("File integrity check failed");
|
||
}
|
||
} catch (IOException e) {
|
||
throw new DataIntegrityException("Failed to read file", e);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Проверка целостности кода
|
||
@Component
|
||
public class CodeIntegrityChecker {
|
||
|
||
@EventListener
|
||
public void onApplicationStart(ApplicationReadyEvent event) {
|
||
validateCodeIntegrity();
|
||
}
|
||
|
||
private void validateCodeIntegrity() {
|
||
// Проверка цифровых подписей
|
||
validateDigitalSignatures();
|
||
|
||
// Проверка контрольных сумм
|
||
validateChecksums();
|
||
|
||
// Проверка целостности зависимостей
|
||
validateDependencies();
|
||
}
|
||
|
||
private void validateDigitalSignatures() {
|
||
// Проверка подписей JAR файлов
|
||
// Проверка подписей DLL/SO файлов
|
||
}
|
||
|
||
private void validateChecksums() {
|
||
// Проверка контрольных сумм критических файлов
|
||
}
|
||
|
||
private void validateDependencies() {
|
||
// Проверка целостности Maven зависимостей
|
||
}
|
||
}
|
||
```
|
||
|
||
#### A09: Security Logging and Monitoring Failures (Сбои логирования и мониторинга безопасности)
|
||
|
||
**Описание:** Недостаточное логирование и мониторинг событий безопасности.
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Комплексная система логирования безопасности
|
||
@Component
|
||
public class SecurityAuditService {
|
||
|
||
@Autowired
|
||
private AuditRepository auditRepository;
|
||
|
||
@Autowired
|
||
private AlertService alertService;
|
||
|
||
public void logSecurityEvent(SecurityEvent event) {
|
||
// Логирование события
|
||
AuditLog auditLog = AuditLog.builder()
|
||
.eventType(event.getType())
|
||
.userId(event.getUserId())
|
||
.ipAddress(event.getIpAddress())
|
||
.userAgent(event.getUserAgent())
|
||
.timestamp(LocalDateTime.now())
|
||
.details(event.getDetails())
|
||
.severity(event.getSeverity())
|
||
.build();
|
||
|
||
auditRepository.save(auditLog);
|
||
|
||
// Отправка алерта при критических событиях
|
||
if (event.getSeverity() == SecuritySeverity.CRITICAL) {
|
||
alertService.sendSecurityAlert(event);
|
||
}
|
||
|
||
// Проверка на подозрительную активность
|
||
checkForSuspiciousActivity(event);
|
||
}
|
||
|
||
private void checkForSuspiciousActivity(SecurityEvent event) {
|
||
// Проверка на множественные неудачные попытки входа
|
||
if (event.getType() == SecurityEventType.FAILED_LOGIN) {
|
||
long failedAttempts = auditRepository.countFailedLogins(
|
||
event.getUserId(),
|
||
LocalDateTime.now().minusHours(1)
|
||
);
|
||
|
||
if (failedAttempts > 10) {
|
||
alertService.sendBruteForceAlert(event);
|
||
}
|
||
}
|
||
|
||
// Проверка на необычные IP адреса
|
||
if (isUnusualIP(event.getIpAddress())) {
|
||
alertService.sendUnusualActivityAlert(event);
|
||
}
|
||
}
|
||
|
||
private boolean isUnusualIP(String ipAddress) {
|
||
// Проверка на известные VPN/Proxy сервисы
|
||
// Проверка на подозрительные географические локации
|
||
return false; // Упрощенная реализация
|
||
}
|
||
}
|
||
|
||
// Система мониторинга безопасности
|
||
@Component
|
||
public class SecurityMonitoringService {
|
||
|
||
@Autowired
|
||
private MetricsService metricsService;
|
||
|
||
@Scheduled(fixedRate = 60000) // Каждую минуту
|
||
public void monitorSecurityMetrics() {
|
||
// Мониторинг количества неудачных попыток входа
|
||
long failedLogins = metricsService.getFailedLoginCount();
|
||
if (failedLogins > 100) {
|
||
alertService.sendHighFailedLoginAlert(failedLogins);
|
||
}
|
||
|
||
// Мониторинг количества активных сессий
|
||
long activeSessions = metricsService.getActiveSessionCount();
|
||
if (activeSessions > 10000) {
|
||
alertService.sendHighSessionCountAlert(activeSessions);
|
||
}
|
||
|
||
// Мониторинг использования ресурсов
|
||
double cpuUsage = metricsService.getCpuUsage();
|
||
if (cpuUsage > 90) {
|
||
alertService.sendHighCpuUsageAlert(cpuUsage);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### A10: Server-Side Request Forgery (SSRF) (Подделка запросов на стороне сервера)
|
||
|
||
**Описание:** Принуждение сервера к выполнению запросов к внутренним ресурсам.
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Защита от SSRF
|
||
@Service
|
||
public class SSRFProtectionService {
|
||
|
||
private final Set<String> allowedHosts = Set.of(
|
||
"api.example.com", "external-service.com"
|
||
);
|
||
|
||
private final Set<String> blockedIPs = Set.of(
|
||
"127.0.0.1", "0.0.0.0", "localhost", "::1"
|
||
);
|
||
|
||
public String makeSafeRequest(String url) {
|
||
// Валидация URL
|
||
validateUrl(url);
|
||
|
||
// Проверка разрешенных хостов
|
||
String host = extractHost(url);
|
||
if (!allowedHosts.contains(host)) {
|
||
throw new SSRFException("Host not allowed: " + host);
|
||
}
|
||
|
||
// Проверка на заблокированные IP
|
||
String ip = resolveIP(host);
|
||
if (blockedIPs.contains(ip)) {
|
||
throw new SSRFException("IP address blocked: " + ip);
|
||
}
|
||
|
||
// Выполнение безопасного запроса
|
||
return executeSafeRequest(url);
|
||
}
|
||
|
||
private void validateUrl(String url) {
|
||
try {
|
||
URI uri = new URI(url);
|
||
|
||
// Проверка схемы
|
||
if (!uri.getScheme().equals("https")) {
|
||
throw new SSRFException("Only HTTPS URLs are allowed");
|
||
}
|
||
|
||
// Проверка порта
|
||
if (uri.getPort() != -1 && uri.getPort() != 443) {
|
||
throw new SSRFException("Non-standard port not allowed");
|
||
}
|
||
|
||
} catch (URISyntaxException e) {
|
||
throw new SSRFException("Invalid URL format", e);
|
||
}
|
||
}
|
||
|
||
private String extractHost(String url) {
|
||
try {
|
||
URI uri = new URI(url);
|
||
return uri.getHost();
|
||
} catch (URISyntaxException e) {
|
||
throw new SSRFException("Invalid URL format", e);
|
||
}
|
||
}
|
||
|
||
private String resolveIP(String host) {
|
||
try {
|
||
InetAddress address = InetAddress.getByName(host);
|
||
return address.getHostAddress();
|
||
} catch (UnknownHostException e) {
|
||
throw new SSRFException("Cannot resolve host: " + host, e);
|
||
}
|
||
}
|
||
|
||
private String executeSafeRequest(String url) {
|
||
// Выполнение запроса с ограничениями
|
||
RestTemplate restTemplate = new RestTemplate();
|
||
|
||
// Настройка таймаутов
|
||
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
|
||
factory.setConnectTimeout(5000);
|
||
factory.setReadTimeout(10000);
|
||
restTemplate.setRequestFactory(factory);
|
||
|
||
try {
|
||
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
|
||
return response.getBody();
|
||
} catch (Exception e) {
|
||
throw new SSRFException("Request failed", e);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Дополнительные уязвимости
|
||
|
||
#### Cross-Site Scripting (XSS)
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Защита от XSS
|
||
@Component
|
||
public class XSSProtectionService {
|
||
|
||
public String sanitizeInput(String input) {
|
||
if (input == null) return null;
|
||
|
||
// HTML экранирование
|
||
return input
|
||
.replace("&", "&")
|
||
.replace("<", "<")
|
||
.replace(">", ">")
|
||
.replace("\"", """)
|
||
.replace("'", "'")
|
||
.replace("/", "/");
|
||
}
|
||
|
||
public boolean containsXSS(String input) {
|
||
// Проверка на подозрительные паттерны
|
||
String[] xssPatterns = {
|
||
"<script", "javascript:", "onload=", "onerror=", "onclick="
|
||
};
|
||
|
||
String lowerInput = input.toLowerCase();
|
||
for (String pattern : xssPatterns) {
|
||
if (lowerInput.contains(pattern)) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// Фильтр для защиты от XSS
|
||
@Component
|
||
public class XSSFilter implements Filter {
|
||
|
||
@Override
|
||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||
throws IOException, ServletException {
|
||
|
||
XSSRequestWrapper wrappedRequest = new XSSRequestWrapper((HttpServletRequest) request);
|
||
chain.doFilter(wrappedRequest, response);
|
||
}
|
||
}
|
||
|
||
public class XSSRequestWrapper extends HttpServletRequestWrapper {
|
||
|
||
public XSSRequestWrapper(HttpServletRequest request) {
|
||
super(request);
|
||
}
|
||
|
||
@Override
|
||
public String getParameter(String parameter) {
|
||
String value = super.getParameter(parameter);
|
||
return sanitizeInput(value);
|
||
}
|
||
|
||
@Override
|
||
public String[] getParameterValues(String parameter) {
|
||
String[] values = super.getParameterValues(parameter);
|
||
if (values != null) {
|
||
for (int i = 0; i < values.length; i++) {
|
||
values[i] = sanitizeInput(values[i]);
|
||
}
|
||
}
|
||
return values;
|
||
}
|
||
|
||
private String sanitizeInput(String input) {
|
||
if (input == null) return null;
|
||
|
||
return input
|
||
.replace("&", "&")
|
||
.replace("<", "<")
|
||
.replace(">", ">")
|
||
.replace("\"", """)
|
||
.replace("'", "'");
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Cross-Site Request Forgery (CSRF)
|
||
|
||
**Методы устранения:**
|
||
|
||
```java
|
||
// Защита от CSRF
|
||
@Configuration
|
||
public class CSRFProtectionConfig {
|
||
|
||
@Bean
|
||
public CsrfTokenRepository csrfTokenRepository() {
|
||
CookieCsrfTokenRepository repository = CookieCsrfTokenRepository.withHttpOnlyFalse();
|
||
repository.setCookiePath("/");
|
||
repository.setCookieName("XSRF-TOKEN");
|
||
return repository;
|
||
}
|
||
}
|
||
|
||
// Проверка CSRF токена
|
||
@Component
|
||
public class CSRFValidationService {
|
||
|
||
public boolean validateCSRFToken(HttpServletRequest request) {
|
||
String tokenFromRequest = request.getHeader("X-XSRF-TOKEN");
|
||
String tokenFromCookie = getCookieValue(request, "XSRF-TOKEN");
|
||
|
||
if (tokenFromRequest == null || tokenFromCookie == null) {
|
||
return false;
|
||
}
|
||
|
||
return tokenFromRequest.equals(tokenFromCookie);
|
||
}
|
||
|
||
private String getCookieValue(HttpServletRequest request, String cookieName) {
|
||
Cookie[] cookies = request.getCookies();
|
||
if (cookies != null) {
|
||
for (Cookie cookie : cookies) {
|
||
if (cookie.getName().equals(cookieName)) {
|
||
return cookie.getValue();
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Обфускация и защита кода
|
||
|
||
### Принципы защиты исходного кода
|
||
|
||
#### 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** - сжатие исполняемых файлов
|
||
|
||
---
|
||
|
||
## Security by Design
|
||
|
||
### Принципы 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));
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Шифрование
|
||
|
||
### Типы шифрования
|
||
|
||
#### 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);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Advanced Hashing Service
|
||
@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);
|
||
}
|
||
}
|
||
|
||
// Key Management Service
|
||
@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);
|
||
}
|
||
}
|
||
|
||
private void scheduleKeyRotation() {
|
||
// Дополнительная логика планирования ротации ключей
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 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);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3. Hashing
|
||
```java
|
||
@Service
|
||
public class HashingService {
|
||
public String hashPassword(String password) {
|
||
return BCrypt.hashpw(password, BCrypt.gensalt(12));
|
||
}
|
||
|
||
public boolean verifyPassword(String password, String hash) {
|
||
return BCrypt.checkpw(password, hash);
|
||
}
|
||
|
||
public String generateHash(String input) throws Exception {
|
||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||
byte[] hash = digest.digest(input.getBytes());
|
||
return Base64.getEncoder().encodeToString(hash);
|
||
}
|
||
}
|
||
```
|
||
|
||
### Управление ключами
|
||
|
||
#### 1. Key Rotation
|
||
```java
|
||
@Service
|
||
public class KeyRotationService {
|
||
@Scheduled(cron = "0 0 0 1 * ?") // каждый месяц
|
||
public void rotateKeys() {
|
||
// генерируем новые ключи
|
||
KeyPair newKeyPair = generateNewKeyPair();
|
||
|
||
// обновляем ключи в системе
|
||
updateSystemKeys(newKeyPair);
|
||
|
||
// перешифровываем существующие данные
|
||
reencryptExistingData(newKeyPair);
|
||
|
||
// удаляем старые ключи
|
||
deleteOldKeys();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. Hardware Security Module (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);
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Безопасность 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);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Практические задания
|
||
|
||
### Задание 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
|
||
|
||
---
|
||
|
||
## Дополнительные ресурсы
|
||
|
||
### Стандарты и фреймворки
|
||
- **OWASP** — Open Web Application Security Project
|
||
- **NIST** — National Institute of Standards and Technology
|
||
- **ISO 27001** — Information Security Management
|
||
- **SOC 2** — Service Organization Control
|
||
|
||
### Инструменты
|
||
- **Security Scanning**: OWASP ZAP, Burp Suite, Nessus
|
||
- **Code Analysis**: SonarQube, Checkmarx, Veracode
|
||
- **Secrets Management**: HashiCorp Vault, AWS Secrets Manager
|
||
- **Identity Management**: Keycloak, Auth0, Okta
|
||
|
||
---
|
||
|
||
## Контрольные вопросы
|
||
|
||
1. Что такое CIA Triad и как он применяется?
|
||
2. Какие принципы Security by Design вы знаете?
|
||
3. Как работает OAuth 2.0 и OpenID Connect?
|
||
4. Какие типы шифрования существуют?
|
||
5. Как обеспечить безопасность API?
|
||
6. Что такое GDPR и как его соблюдать?
|
||
7. Какие требования PCI DSS вы знаете?
|
||
8. Как реализовать Zero Trust Architecture?
|