Case Study: Đánh giá kiến trúc bảo mật Cloud

Chuyện về devsecops

Case Study: Đánh giá kiến trúc bảo mật Cloud cho một hệ thống E-commerce

Checklist thực chiến của một Cloud Architect về cách "săn lùng" lỗ hổng và sửa chữa chúng bằng mã, trước khi chúng trở thành thảm họa.

Bối Cảnh Mới: Rủi Ro Rình Rập E-commerce 2024

Vận hành một trang E-commerce không chỉ là cuộc đua về tính năng, mà còn là cuộc chiến thầm lặng trên mặt trận an ninh mạng. Khi khách hàng giao phó dữ liệu nhạy cảm nhất, niềm tin trở thành đơn vị tiền tệ quý giá nhất.

Nhưng bối cảnh mối đe dọa đã thay đổi. Đây không còn là các cuộc tấn công đơn lẻ, mà là các chiến dịch có tổ chức. Điều đáng lo ngại là sự thay đổi trong bản chất của mối đe dọa: thị trường ngầm đã chuyển từ bán *dữ liệu* sang bán *quyền truy cập*.

Loại Mối Đe Dọa Trên Dark Web Nhắm Vào E-commerce
46.82% Bán Quyền Truy Cập
41.22% Bán Dữ Liệu
10.53% Tấn Công Website
1.43% Khác

Ngày Đầu Tiên Tại Dự Án "Phoenix": Tiếp Cận Kiến Trúc

Câu chuyện bắt đầu với một Cloud Security Architect gia nhập "Phoenix", một dự án E-commerce mới đang chạy đua với thời gian. Thay vì làm "cảnh sát bảo mật", kiến trúc sư tiếp cận với vai trò cố vấn, sử dụng AWS Well-Architected Framework (Security Pillar) làm kim chỉ nam.

Mục tiêu là áp dụng 7 nguyên tắc thiết kế cốt lõi của Trụ cột An ninh:

1
Implement a strong identity foundation: Áp dụng nguyên tắc đặc quyền tối thiểu (least privilege).
2
Enable traceability: Giám sát, cảnh báo và kiểm toán các hành động.
3
Apply security at all layers: Áp dụng phòng thủ theo chiều sâu (defense in depth).
4
Automate security best practices: Quản lý các biện pháp kiểm soát dưới dạng mã (as code).
5
Protect data in transit and at rest: Mã hóa dữ liệu khi truyền và khi lưu trữ.
6
Keep people away from data: Giảm thiểu nhu cầu truy cập trực tiếp vào dữ liệu.
7
Prepare for security events: Xây dựng quy trình ứng phó sự cố.

Checklist Rà Soát Kiến Trúc: "Săn Lùng" Lỗ Hổng

3.1. Nền Tảng Định Danh và Truy Cập (IAM)

  • Các vai trò IAM có tuân thủ đặc quyền tối thiểu không? Có tồn tại "Action: *", "Resource: *" không?
  • Đội ngũ có đang sử dụng access key tĩnh được hard-code không?
  • MFA có được bật cho các tài khoản quan trọng không?
Phát hiện tại Phoenix: Một số pipeline CI/CD vẫn còn sử dụng access key của IAM User được hard-code trong cấu hình. Một số vai trò EC2 được cấp quyền truy cập vào tất cả các bucket S3.

3.2. Vành Đai Bảo Vệ Hạ Tầng (Infrastructure Protection)

  • Mạng VPC có được phân chia thành các lớp (public, private, isolated) hợp lý không?
  • Các Security Group (SG) có mở cổng inbound từ 0.0.0.0/0 không?
  • Các cổng quản trị nhạy cảm (SSH 22, RDP 3389) có bị lộ ra Internet không?
  • Hệ thống có sử dụng AWS WAF (Web Application Firewall) không?
Phát hiện tại Phoenix: Chuông báo động vang lên! Một "bastion host" được đặt trong public subnet có Security Group cho phép SSH (port 22) từ 0.0.0.0/0. Đây là một "cánh cửa mở rộng" cho hacker.

3.3. Bảo Vệ Dữ Liệu (Data Protection)

  • Dữ liệu trên EBS, S3, RDS có được mã hóa khi lưu trữ (Encryption at Rest) không?
  • Lưu lượng mạng có được mã hóa khi truyền (Encryption in Transit) bằng TLS không?
  • Kiến trúc có tuân thủ các yêu cầu của PCI DSS (cho E-commerce) không?
Phát hiện tại Phoenix: Một bucket S3 dùng để lưu trữ hình ảnh sản phẩm do người dùng tải lên chưa được bật mã hóa mặc định.

3.4. Phát Hiện và Ghi Log (Detection and Logging)

  • AWS CloudTrail có được bật trên tất cả các Region không?
  • VPC Flow Logs và ELB Access Logs có được bật và tập trung hóa không?
  • Amazon GuardDuty có được kích hoạt để phát hiện mối đe dọa tự động không?
  • AWS Config có được dùng để theo dõi thay đổi cấu hình không?
Phát hiện tại Phoenix: VPC Flow Logs đã bị tắt với lý do "tiết kiệm chi phí", khiến việc điều tra sự cố mạng gần như là không thể.

Từ Lý Thuyết Đến Thực Chiến: 3 Lỗ Hổng và Cách Khắc Phục Bằng Code

4.1. Lỗ Hổng #1: Cánh Cửa Mở Rộng (Port SSH Public)

Vấn đề: Bastion host cho phép truy cập SSH (port 22) từ bất kỳ địa chỉ IP nào trên Internet (0.0.0.0/0).
Rủi ro: Lời mời công khai cho các cuộc tấn công brute-force tự động. Nếu thành công, kẻ tấn công sẽ có "chỗ đứng" trong mạng của bạn, và quyền truy cập này có thể được bán trên dark web.
Giải pháp: Loại bỏ hoàn toàn nhu cầu mở cổng SSH. Sử dụng AWS Systems Manager (SSM) Session Manager. Dịch vụ này cho phép truy cập shell an toàn, được kiểm toán mà không cần mở bất kỳ cổng inbound nào.

Mã Terraform - Trước Khi Sửa Lỗi


resource "aws_security_group" "bastion_sg_insecure" {
  name        = "bastion-sg-insecure"
  description = "Allow SSH from anywhere - DANGEROUS"
  vpc_id      = var.vpc_id

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"] # <-- LỖ HỔNG BẢO MẬT
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
        

Mã Terraform - Sau Khi Sửa Lỗi (Giải pháp 3 phần)

1. Sửa đổi Security Group (Đóng cổng 22)


resource "aws_security_group" "bastion_sg_secure" {
  name        = "bastion-sg-secure"
  description = "No inbound ports needed for SSM"
  vpc_id      = var.vpc_id

  # KHÔNG CÓ QUY TẮC INBOUND NÀO ĐƯỢC MỞ RA INTERNET

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"] # Cho phép instance gọi ra API AWS
  }
}
        

2. Đính kèm chính sách SSM vào vai trò IAM của EC2


resource "aws_iam_role" "bastion_role" {
  name = "bastion-ec2-role"
  assume_role_policy = jsonencode({
    Version   = "2012-10-17",
    Statement = [
      {
        Action = "sts:AssumeRole",
        Effect = "Allow",
        Principal = {
          Service = "ec2.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "ssm_policy_attachment" {
  role       = aws_iam_role.bastion_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_instance_profile" "bastion_profile" {
  name = "bastion-instance-profile"
  role = aws_iam_role.bastion_role.name
}
        

3. Tạo chính sách IAM cho người dùng (Đặc quyền tối thiểu)


resource "aws_iam_policy" "ssm_session_developer_policy" {
  name        = "SSMSessionDeveloperPolicy"
  description = "Allow starting SSM sessions on developer-tagged instances"

  policy = jsonencode({
    Version   = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Action = "ssm:StartSession",
        Resource = "arn:aws:ec2:*:*:instance/*",
        Condition = {
          "StringEquals" = {
            "ssm:resourceTag/AccessLevel" = "developer"
          }
        }
      }
    ]
  })
}
        

So Sánh Giải Pháp

Tiêu Chí SSH Truyền Thống (Public Key) AWS SSM Session Manager
Bảo Mật Yêu cầu quản lý, phân phối, thu hồi khóa SSH. Khóa bị rò rỉ có thể bị lạm dụng. Sử dụng quyền IAM. Tích hợp với IdP và MFA. Không cần quản lý khóa.
Kiểm Toán (Audit) Cần cấu hình phức tạp trên từng máy chủ để ghi log. Tự động ghi lại toàn bộ lệnh và đầu ra vào CloudWatch Logs hoặc S3.
Lộ Diện Mạng Yêu cầu mở cổng inbound (port 22), tạo ra bề mặt tấn công. Không cần cổng inbound. Giao tiếp qua các điểm cuối (endpoint) của AWS.
Quản Lý Phức tạp khi số lượng người dùng và máy chủ tăng lên. Quản lý tập trung qua IAM. Thu hồi quyền truy cập ngay lập tức.

4.2. Lỗ Hổng #2: Rò Rỉ Dữ Liệu Đơn Hàng (IDOR)

Vấn đề: API endpoint GET /orders/{orderId} chỉ kiểm tra xem người dùng đã đăng nhập (xác thực), mà không kiểm tra xem họ có phải là chủ sở hữu của đơn hàng đó không (ủy quyền).
Rủi ro: Lỗ hổng Insecure Direct Object Reference (IDOR) kinh điển. Kẻ tấn công có thể duyệt qua các orderId (1, 2, 3...) để truy cập và đánh cắp thông tin đơn hàng của hàng ngàn khách hàng khác.
Giải pháp: Sử dụng API Gateway Lambda Authorizer. Đây là một hàm Lambda hoạt động như "người gác cổng", chạy *trước* khi request được chuyển đến logic nghiệp vụ chính. Nó sẽ kiểm tra quyền sở hữu và trả về "Allow" hoặc "Deny".

Sơ Đồ Luồng Ủy Quyền Chống IDOR

Client (Gửi Request: GET /orders/123 + Auth Token)
API Request
API Gateway (Nhận Request)
Invoke Authorizer
Lambda Authorizer
1. Decode Token → Lấy userId: "user-A"
2. Lấy orderId: "123" từ path
3. Kiểm tra DB: user-A có sở hữu order 123 không? →
4. Trả về chính sách "Allow"
Return "Allow"
API Gateway (Đã xác thực quyền)
Invoke Backend
Backend Lambda (Xử lý logic, trả về chi tiết đơn hàng 123)

1. Terraform: Định nghĩa Lambda Authorizer


# Định nghĩa hàm Lambda cho Authorizer
resource "aws_lambda_function" "order_authorizer_lambda" {
  function_name = "order-authorizer"
  # ... (cấu hình runtime, handler, role...)
}

# Tạo Authorizer trong API Gateway
resource "aws_apigatewayv2_authorizer" "lambda_authorizer" {
  api_id           = aws_apigatewayv2_api.example.id
  authorizer_type  = "REQUEST"
  authorizer_uri   = aws_lambda_function.order_authorizer_lambda.invoke_arn
  name             = "OrderOwnershipAuthorizer"
  identity_sources = ["$request.header.Authorization", "$request.path.orderId"]
  authorizer_payload_format_version = "2.0"
}

# Gắn Authorizer vào route cụ thể
resource "aws_apigatewayv2_route" "get_order" {
  api_id    = aws_apigatewayv2_api.example.id
  route_key = "GET /orders/{orderId}"
  target    = "integrations/${aws_apigatewayv2_integration.backend.id}"

  authorization_type = "CUSTOM"
  authorizer_id      = aws_apigatewayv2_authorizer.lambda_authorizer.id
}
        

2. Node.js: Mã nguồn cho Lambda Authorizer


// Giả sử có hàm để decode token và lấy userId
const getUserIdFromToken = (token) => {
  // Demo: Trong thực tế, dùng thư viện 'jsonwebtoken'
  // const decoded = jwt.verify(token, JWT_SECRET);
  // return decoded.sub;
  return "user-A"; // Giả lập
};

// Giả sử có hàm để kiểm tra quyền sở hữu đơn hàng trong DynamoDB
const isOrderOwner = async (userId, orderId) => {
  // const { Item } = await ddbDocClient.send(new GetCommand({
  //   TableName: 'Orders', Key: { orderId: orderId }
  // }));
  // return Item && Item.ownerId === userId;
  return true; // Giả lập là đúng để demo
};

const generatePolicy = (principalId, effect, resource) => {
  return {
    principalId: principalId,
    policyDocument: {
      Version: '2012-10-17',
      Statement: [
        {
          Action: 'execute-api:Invoke',
          Effect: effect,
          Resource: resource,
        }
      ],
    },
  };
};

exports.handler = async (event) => {
  console.log('Authorizer event:', JSON.stringify(event, null, 2));
  try {
    const token = event.headers.authorization.split(' ')[1]; // Lấy token
    const orderId = event.pathParameters.orderId;
    const methodArn = event.routeArn;

    const userId = getUserIdFromToken(token);
    const isOwner = await isOrderOwner(userId, orderId);

    if (isOwner) {
      console.log(`Authorization successful for user ${userId}`);
      return generatePolicy(userId, 'Allow', methodArn);
    } else {
      console.log(`Authorization FAILED for user ${userId}`);
      // Không trả về "Allow", API Gateway sẽ tự động trả về 403 Forbidden
      return { "isAuthorized": false };
    }
  } catch (error) {
    console.error('An error occurred in the authorizer:', error);
    throw new Error('Unauthorized'); // Trả về 401
  }
};
        

4.3. Lỗ Hổng #3: Thiếu Quản Trị, Dễ Tái Phạm

Vấn đề: Các bản vá là giải pháp tại chỗ. Điều gì ngăn cản một nhà phát triển khác, trong lúc vội, lại commit một đoạn mã tương tự (mở cổng 22) trong tương lai? Đây là nỗi đau về Quản trị (Governance).
Rủi ro: Trạng thái bảo mật của hệ thống sẽ dần suy giảm theo thời gian. Đội ngũ sẽ luôn ở trong trạng thái "dập lửa" thay vì xây dựng tính năng.
Giải pháp: "Dịch chuyển sang trái" (Shift Left) bằng cách triển khai Policy-as-Code (PaC). Sử dụng Open Policy Agent (OPA) để định nghĩa các quy tắc bảo mật và tích hợp vào pipeline CI/CD, ngăn chặn các cấu hình không tuân thủ *trước khi* chúng được triển khai.

Mã Rego - Chính Sách Tự Động

1. Chính sách: Cấm mở cổng SSH ra ngoài Internet


package terraform.aws.security

deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_security_group_rule"
    resource.change.after.type == "ingress"
    resource.change.after.from_port == 22
    resource.change.after.to_port == 22
    
    cidr := resource.change.after.cidr_blocks[_]
    cidr == "0.0.0.0/0"

    msg = sprintf("Resource '%s' illegally opens SSH port 22 to the internet (0.0.0.0/0).", [resource.address])
}
        

2. Chính sách: Bắt buộc mã hóa cho các S3 Bucket


package terraform.aws.s3

deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_s3_bucket"
    
    actions := {"create", "update"}
    actions[resource.change.actions[_]]

    not resource.change.after.server_side_encryption_configuration

    msg = sprintf("S3 Bucket '%s' must have server-side encryption enabled.", [resource.address])
}
        

Tích hợp vào Pipeline CI/CD

  1. Generate Plan: Chạy terraform plan -out=tfplan.binary.
  2. Convert to JSON: Chạy terraform show -json tfplan.binary > tfplan.json.
  3. Evaluate Policies: Chạy OPA để đánh giá file JSON: opa eval -i tfplan.json -d policies/ "data.terraform.deny".
  4. Block or Proceed: Nếu OPA trả về bất kỳ vi phạm nào, pipeline sẽ thất bại, chặn việc merge và triển khai mã.

Xây Dựng Văn Hóa DevSecOps: Bài Học Từ "Phoenix"

Hành trình tại dự án Phoenix không chỉ là một cuộc kiểm tra kỹ thuật, mà là một sự chuyển đổi văn hóa. Bảo mật không còn là trách nhiệm của một người, mà là trách nhiệm chung. Đối với những "DevSecOps Pioneer", đây là những bài học cốt lõi:

  • Bắt đầu từ nhỏ, tập trung vào tác động lớn: Xác định 2-3 rủi ro lớn nhất (như cổng SSH public hoặc IDOR) và giải quyết chúng triệt để. Một chiến thắng nhỏ sẽ tạo ra động lực lớn.
  • Tự động hóa là chìa khóa: Biến các tiêu chuẩn bảo mật thành mã (Policy-as-Code). Tích hợp các công cụ quét bảo mật (SAST, DAST) vào pipeline CI/CD. Tự động hóa giải phóng con người khỏi các công việc lặp đi lặp lại.
  • Coi bảo mật là một chỉ số chất lượng: Tích hợp bảo mật vào "Định nghĩa Hoàn thành" (Definition of Done). Một tính năng chỉ thực sự hoàn thành khi nó không chỉ hoạt động đúng, mà còn phải an toàn.

Trong thế giới DevSecOps, mục tiêu cuối cùng không phải là một bản báo cáo "sạch", mà là một hệ thống mà ở đó, việc làm đúng ngay từ đầu trở nên dễ dàng hơn việc sửa lỗi sau này.

Đăng nhận xét