Zero Trust подход
Что такое Zero Trust
Zero Trust — модель безопасности, основанная на принципе «никогда не доверяй, всегда проверяй». В отличие от периметровой модели (castle-and-moat), Zero Trust не различает внутренний и внешний трафик.
Периметровая модель vs Zero Trust
| Периметровая | Zero Trust | |
|---|---|---|
| Доверие | Внутри сети = доверенный | Никто не доверенный |
| Проверка | На границе сети | На каждом запросе |
| Lateral movement | Свободный внутри | Ограниченный |
| Breach impact | Полный доступ изнутри | Минимальный blast radius |
Принципы Zero Trust
| Принцип | Описание |
|---|---|
| Verify explicitly | Аутентификация и авторизация на каждый запрос |
| Least privilege | Минимально необходимый доступ |
| Assume breach | Проектировать как будто уже взломаны |
| Micro-segmentation | Изоляция на уровне сервисов |
| Continuous validation | Постоянная проверка, не одноразовая |
Компоненты Zero Trust
[User/Device] → [Identity Provider] → [Policy Engine] → [Service]
↓ ↓
Identity verification Access decision
MFA, device health Context-based policy
Risk scoring Continuous evaluation
Identity-based access
Вместо IP-based правил — identity-based. Доступ определяется тем, кто запрашивает, а не откуда.
| Компонент | Описание |
|---|---|
| Service identity | Каждый сервис имеет идентичность (SPIFFE/SPIRE) |
| User identity | SSO + MFA для пользователей |
| Device identity | Состояние устройства (patch level, encryption) |
| Workload identity | Идентичность для контейнеров и VM |
mTLS (Mutual TLS)
В обычном TLS только сервер предъявляет сертификат. В mTLS обе стороны аутентифицируют друг друга.
Standard TLS: Mutual TLS:
Client → Server Client → Server
← Certificate ← Server Certificate
(verify server) (verify server)
Client Certificate →
(server verifies client)
PHP: проверка клиентского сертификата
<?php
declare(strict_types=1);
namespace App\Security;
/**
* Verify client certificate in mTLS setup.
* Nginx/Apache forwards cert info via headers.
*/
final readonly class MtlsVerifier
{
/**
* @param array<string> $allowedSubjects Allowed certificate subjects (CN)
*/
public function __construct(
private array $allowedSubjects,
) {}
/**
* Verify client certificate from proxy headers.
*
* Nginx config:
* proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
* proxy_set_header X-SSL-Client-S-DN $ssl_client_s_dn;
* proxy_set_header X-SSL-Client-Serial $ssl_client_serial;
*/
public function verify(array $headers): MtlsResult
{
$verified = $headers['X-SSL-Client-Verify'] ?? '';
if ($verified !== 'SUCCESS') {
return MtlsResult::failed('Client certificate not verified');
}
$subjectDn = $headers['X-SSL-Client-S-DN'] ?? '';
// Extract CN from Subject DN
if (!preg_match('/CN=([^,\/]+)/', $subjectDn, $matches)) {
return MtlsResult::failed('Cannot extract CN from certificate');
}
$cn = $matches[1];
if (!in_array($cn, $this->allowedSubjects, true)) {
return MtlsResult::failed("Service '{$cn}' is not authorized");
}
return MtlsResult::success($cn);
}
}
final readonly class MtlsResult
{
private function __construct(
public bool $authenticated,
public ?string $serviceIdentity,
public ?string $error,
) {}
public static function success(string $identity): self
{
return new self(true, $identity, null);
}
public static function failed(string $error): self
{
return new self(false, null, $error);
}
}
package security
import (
"fmt"
"net/http"
"regexp"
)
var cnRegex = regexp.MustCompile(`CN=([^,/]+)`)
// MtlsResult holds the result of mTLS client certificate verification.
type MtlsResult struct {
Authenticated bool `json:"authenticated"`
ServiceIdentity string `json:"service_identity,omitempty"`
Error string `json:"error,omitempty"`
}
// MtlsVerifier verifies client certificates forwarded by a reverse proxy.
type MtlsVerifier struct {
allowedSubjects map[string]bool
}
// NewMtlsVerifier creates a verifier with allowed certificate CNs.
func NewMtlsVerifier(subjects []string) *MtlsVerifier {
m := make(map[string]bool, len(subjects))
for _, s := range subjects {
m[s] = true
}
return &MtlsVerifier{allowedSubjects: m}
}
// Verify checks the client certificate headers set by nginx/envoy.
func (v *MtlsVerifier) Verify(r *http.Request) MtlsResult {
verified := r.Header.Get("X-SSL-Client-Verify")
if verified != "SUCCESS" {
return MtlsResult{Error: "Client certificate not verified"}
}
subjectDN := r.Header.Get("X-SSL-Client-S-DN")
matches := cnRegex.FindStringSubmatch(subjectDN)
if len(matches) < 2 {
return MtlsResult{Error: "Cannot extract CN from certificate"}
}
cn := matches[1]
if !v.allowedSubjects[cn] {
return MtlsResult{Error: fmt.Sprintf("Service %q is not authorized", cn)}
}
return MtlsResult{Authenticated: true, ServiceIdentity: cn}
}
Микросегментация
Микросегментация изолирует сервисы друг от друга. Каждый сервис может общаться только с разрешёнными сервисами.
Без сегментации: С микросегментацией:
┌───────────────┐ ┌─────┐ ┌─────┐
│ All services │ │ API │──│ DB │ (API → DB: allowed)
│ can talk to │ └─────┘ └─────┘
│ each other │ ┌─────┐ ┌─────┐
└───────────────┘ │ Web │──│ API │ (Web → API: allowed)
└─────┘ └─────┘
Web → DB: BLOCKED
PHP: межсервисная авторизация
<?php
declare(strict_types=1);
namespace App\Security;
/**
* Service-to-service authorization policy.
* Implements Zero Trust by checking identity + allowed operations.
*/
final readonly class ServiceAuthPolicy
{
/**
* Service communication matrix.
* Key: calling service, Value: allowed target services and operations.
*
* @var array<string, array<string, array<string>>>
*/
private const POLICY_MATRIX = [
'api-gateway' => [
'order-service' => ['createOrder', 'getOrder', 'listOrders'],
'user-service' => ['getUser', 'validateToken'],
'notification-service' => ['sendNotification'],
],
'order-service' => [
'payment-service' => ['processPayment', 'refund'],
'inventory-service' => ['checkStock', 'reserveStock'],
'notification-service' => ['sendNotification'],
],
'payment-service' => [
'order-service' => ['updateOrderStatus'],
'notification-service' => ['sendNotification'],
],
// Explicitly: payment-service CANNOT call user-service
];
/**
* Check if service A is allowed to call operation on service B.
*/
public function isAllowed(string $callerService, string $targetService, string $operation): bool
{
$allowed = self::POLICY_MATRIX[$callerService][$targetService] ?? [];
return in_array($operation, $allowed, true);
}
/**
* Middleware: verify service identity and check policy.
*/
public function authorize(string $callerIdentity, string $targetService, string $operation): void
{
if (!$this->isAllowed($callerIdentity, $targetService, $operation)) {
throw new UnauthorizedServiceCallException(
sprintf(
'Service "%s" is not allowed to call "%s.%s"',
$callerIdentity,
$targetService,
$operation,
),
);
}
}
}
package security
import "fmt"
// ServiceAuthPolicy implements Zero Trust service-to-service authorization.
type ServiceAuthPolicy struct{}
// policyMatrix defines allowed service-to-service communication.
var policyMatrix = map[string]map[string][]string{
"api-gateway": {
"order-service": {"createOrder", "getOrder", "listOrders"},
"user-service": {"getUser", "validateToken"},
"notification-service": {"sendNotification"},
},
"order-service": {
"payment-service": {"processPayment", "refund"},
"inventory-service": {"checkStock", "reserveStock"},
"notification-service": {"sendNotification"},
},
"payment-service": {
"order-service": {"updateOrderStatus"},
"notification-service": {"sendNotification"},
},
}
// IsAllowed checks if caller service can invoke an operation on the target.
func (p *ServiceAuthPolicy) IsAllowed(caller, target, operation string) bool {
ops, ok := policyMatrix[caller][target]
if !ok {
return false
}
for _, op := range ops {
if op == operation {
return true
}
}
return false
}
// Authorize checks the policy and returns an error if denied.
func (p *ServiceAuthPolicy) Authorize(caller, target, operation string) error {
if !p.IsAllowed(caller, target, operation) {
return fmt.Errorf("service %q is not allowed to call %q.%s", caller, target, operation)
}
return nil
}
| Область | Проверка |
|---|---|
| Identity | Каждый сервис имеет identity |
| Authentication | mTLS между сервисами |
| Authorization | Policy engine на каждый запрос |
| Encryption | TLS everywhere, encryption at rest |
| Segmentation | Микросегментация сети |
| Monitoring | Логирование всех access-решений |
| Device trust | Проверка состояния устройств |
| Least privilege | Минимальные права для каждого компонента |
Важно: Zero Trust — это не продукт, а архитектурный подход. Нельзя «купить Zero Trust». Можно постепенно внедрять принципы, начиная с identity и mTLS между сервисами.
Итоги
| Концепция | Суть |
|---|---|
| Zero Trust | Никогда не доверяй, всегда проверяй |
| mTLS | Взаимная аутентификация клиента и сервера |
| Service identity | Каждый сервис имеет криптографическую идентичность |
| Micro-segmentation | Изоляция на уровне сервисов |
| Policy engine | Централизованные решения о доступе |
| Continuous validation | Постоянная проверка, не разовая |