Chương 13 — Bảo mật Đám mây
Tổng quan
Mình học mảng cloud security này không phải vì tò mò lý thuyết, mà vì công việc thật: hạ tầng ngày nay hiếm khi nằm gọn trên một nhà cung cấp — AWS chỗ này, OCI chỗ kia, rồi sớm muộn cũng đụng GCP khi làm việc với đối tác. Đọc vài báo cáo lộ dữ liệu thực tế mới thấy một điểm chung đáng sợ: phần lớn không phải do nhà cung cấp bị hack, mà do chính khách hàng cấu hình sai — một bucket để public, một security group mở nhầm cổng quản trị, một IMDSv1 quên tắt. Nên câu hỏi mình cần trả lời trước tiên không phải "cloud có an toàn không", mà là "trong cả chồng hạ tầng này, đâu mới là phần việc của mình".
Chương bắt đầu từ đúng ranh giới đó: mô hình IaaS/PaaS/SaaS và Shared Responsibility Model, hai thứ chỉ ra ai vá lỗi tầng nào và dập tắt luôn ngộ nhận "lên cloud là nhà cung cấp lo hết". Từ đó chương đi vào các khối mình phải tự quản — IAM và least privilege, VPC cùng Security Group/Network ACL, những dịch vụ dễ gây sự cố nhất trong thực tế như S3 và KMS, lớp giám sát CloudTrail/CloudWatch/GuardDuty, và IMDS, thứ hay bị khai thác qua SSRF nếu còn chạy bản v1. Organizations và SCP khép lại phần này bằng vai trò chốt guardrail ở cấp tổ chức, không phụ thuộc từng tài khoản con có cẩn thận hay không.
Nửa sau chương bước ra ngoài AWS: trước là bảng ánh xạ dịch vụ sang GCP để học một nền tảng suy ra nền tảng kia, sau là hẳn một phần riêng cho OCI (13.14). Mình từng tưởng OCI chỉ đổi tên dịch vụ so với AWS, hoá ra nhiều khái niệm nền tảng thiết kế khác hẳn — cô lập bằng compartment thay vì account, policy viết dạng câu thay vì JSON, bucket private mặc định thay vì phải tự khoá lại. Đối chiếu xong cả ba nền tảng, chương khép bằng các đường tấn công cloud phổ biến, công cụ CSPM quét cấu hình sai liên tục, và Secret Manager để không phải hardcode bí mật trong code.
Ví dụ trong chương là lệnh AWS CLI và OCI CLI thật kèm output mẫu; chỗ nào số liệu có thể đổi theo thời gian đều ghi "cần kiểm chứng".
13.1. Mô hình dịch vụ đám mây: IaaS / PaaS / SaaS
13.1.1. Là gì
Điện toán đám mây phân tầng theo "ai vận hành tầng nào" trong ngăn xếp hạ tầng. Ba mô hình chuẩn (định nghĩa gốc: NIST SP 800-145):
| Mô hình | Nhà cung cấp quản lý | Khách hàng quản lý | Ví dụ AWS | Ví dụ GCP |
|---|---|---|---|---|
| IaaS (Infrastructure) | Hypervisor, host OS, mạng vật lý, lưu trữ vật lý | Guest OS, runtime, app, data, cấu hình mạng ảo | EC2, EBS, VPC | Compute Engine, Persistent Disk |
| PaaS (Platform) | Cộng thêm: OS, runtime, patching | App code + data + cấu hình ứng dụng | Elastic Beanstalk, Lambda, RDS | App Engine, Cloud Functions, Cloud SQL |
| SaaS (Software) | Toàn bộ ngăn xếp | Chỉ dữ liệu người dùng + cấu hình trong app | WorkMail, QuickSight | Workspace |
13.1.2. Cơ chế bên trong: ranh giới tin cậy thay đổi theo tầng
Điểm cốt lõi về bảo mật: bề mặt tấn công và nghĩa vụ vá lỗi dịch chuyển theo mô hình.
IaaS PaaS SaaS
+--------+ +--------+ +--------+
App | KH | | KH | | NCC |
Data | KH | | KH | | KH* | *KH vẫn sở hữu/cấu hình truy cập dữ liệu
Runtime | KH | | NCC | | NCC |
OS | KH | | NCC | | NCC |
Hypervisor | NCC | | NCC | | NCC |
Mạng vật lý | NCC | | NCC | | NCC |
+--------+ +--------+ +--------+
KH = Khách hàng, NCC = Nhà cung cấp
Vì sao: với IaaS, một lỗ hổng kernel chưa vá trên guest OS là trách nhiệm của bạn; với PaaS như Lambda, AWS vá runtime nhưng một thư viện npm có lỗ hổng trong gói deploy vẫn là của bạn. Hiểu sai ranh giới này là gốc rễ của hầu hết sự cố misconfiguration (cấu hình sai).
13.2. Shared Responsibility Model (Mô hình trách nhiệm chung)
13.2.1. Là gì
Khung phân định trách nhiệm bảo mật. AWS gọi tắt:
- Security OF the cloud — của AWS: phần cứng, region/AZ, mạng vật lý, hypervisor, dịch vụ nền.
- Security IN the cloud — của khách hàng: IAM, mã hóa dữ liệu, cấu hình Security Group, vá guest OS (với IaaS), phân loại dữ liệu.
GCP dùng cụm "Shared fate" (số phận chung) — nhấn mạnh Google chủ động cung cấp blueprint an toàn mặc định, nhưng phân định trách nhiệm về cơ bản tương đương.
Sơ đồ ranh giới trách nhiệm:
===== SECURITY IN THE CLOUD (Khách hàng) =====
+-----------------------------------------------+
| Dữ liệu khách hàng + phân loại dữ liệu |
| IAM / policy / quản lý danh tính & truy cập |
| Cấu hình Security Group, NACL, firewall |
| Mã hóa at-rest / in-transit (bật & cấu hình) |
| Vá guest OS, runtime, ứng dụng (mức IaaS) |
+-----------------------------------------------+
| ranh giới phân định
+-----------------------------------------------+
| Hypervisor / ảo hóa |
| Dịch vụ nền (compute, storage, DB engine) |
| Mạng vật lý, region / AZ |
| Phần cứng, cơ sở vật lý (datacenter) |
+-----------------------------------------------+
===== SECURITY OF THE CLOUD (Nhà cung cấp) =====
Đường phân định dịch chuyển lên/xuống theo mô hình dịch vụ (IaaS đẩy ranh giới xuống thấp, SaaS đẩy lên cao), nhưng dữ liệu và cấu hình truy cập luôn thuộc trách nhiệm khách hàng. Đây là lý do hầu hết sự cố rò rỉ nằm ở nửa trên của sơ đồ.
13.2.2. Bảng phân định chi tiết theo dịch vụ
| Hạng mục | EC2 (IaaS) | RDS (PaaS) | S3 (lưu trữ quản lý) |
|---|---|---|---|
| Vá hypervisor | AWS | AWS | AWS |
| Vá OS | Khách hàng | AWS | N/A |
| Vá DB engine | Khách hàng | AWS | N/A |
| Mã hóa at-rest | Khách hàng bật | Khách hàng bật | Khách hàng bật/SSE |
| Mã hóa in-transit | Khách hàng cấu hình | Khách hàng (force SSL) | Khách hàng (policy aws:SecureTransport) |
| Cấu hình truy cập (IAM/policy) | Khách hàng | Khách hàng | Khách hàng |
| Phân loại dữ liệu | Khách hàng | Khách hàng | Khách hàng |
Lưu ý: gần như toàn bộ sự cố rò rỉ dữ liệu đám mây nổi tiếng nằm ở phần "IN the cloud" — tức lỗi của khách hàng (bucket public, key lộ trong git, IAM quá rộng). AWS/GCP hiếm khi bị thủng ở tầng họ quản lý.
13.3. AWS IAM — Identity and Access Management
13.3.1. Các thực thể (entities)
| Thực thể | Định nghĩa | ARN ví dụ |
|---|---|---|
| Root user | Chủ tài khoản, toàn quyền tuyệt đối, không giới hạn được bằng IAM policy | arn:aws:iam::123456789012:root |
| IAM User | Danh tính người/dịch vụ lâu dài, có credentials | arn:aws:iam::123456789012:user/alice |
| IAM Group | Tập hợp user để gắn policy chung | arn:aws:iam::123456789012:group/devs |
| IAM Role | Danh tính tạm thời, không có credential cố định, được "assume" | arn:aws:iam::123456789012:role/app-role |
| Policy | Tài liệu JSON định nghĩa quyền | arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess |
Cấu trúc ARN (Amazon Resource Name) — từng trường:
arn : partition : service : region : account-id : resource-type / resource
| | | | | |
| | | | | +-- vd: user/alice, bucket/my-data
| | | | +-- 12 chữ số tài khoản
| | | +-- vd: us-east-1 (S3/IAM thường để trống vì global)
| | +-- vd: iam, s3, ec2
| +-- aws | aws-cn (Trung Quốc) | aws-us-gov
+-- hằng "arn"
| Trường ARN | Kích thước | Ý nghĩa | Ví dụ |
|---|---|---|---|
| arn | cố định 3 ký tự | literal | arn |
| partition | chuỗi | vùng pháp lý | aws |
| service | chuỗi | namespace dịch vụ | s3 |
| region | chuỗi | region (trống nếu global) | us-east-1 hoặc rỗng |
| account-id | 12 chữ số | ID tài khoản | 123456789012 |
| resource | chuỗi | định danh tài nguyên | bucket/logs |
13.3.2. Policy JSON — mổ xẻ từng trường
Đây là cấu trúc trung tâm của toàn bộ ủy quyền AWS. Một policy là một tài liệu JSON.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReadSpecificBucket",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::company-reports",
"arn:aws:s3:::company-reports/*"
],
"Condition": {
"StringEquals": { "aws:PrincipalTag/team": "finance" },
"IpAddress": { "aws:SourceIp": "203.0.113.0/24" },
"Bool": { "aws:MultiFactorAuthPresent": "true" }
}
}
]
}
Mô tả từng trường:
| Trường | Bắt buộc | Ý nghĩa | Giá trị ví dụ |
|---|---|---|---|
Version |
Có | Phiên bản ngôn ngữ policy. PHẢI là 2012-10-17 để dùng được biến/condition. 2008-10-17 là cũ, không hỗ trợ policy variables |
2012-10-17 |
Statement |
Có | Mảng các câu lệnh quyền | [ {...} ] |
Sid |
Không | Statement ID, nhãn để đọc/quản lý | AllowReadSpecificBucket |
Effect |
Có | Allow hoặc Deny |
Allow |
Action |
Có (hoặc NotAction) | Hành động API, dạng service:Operation, hỗ trợ * |
s3:GetObject |
Resource |
Có với policy gắn vào identity | ARN tài nguyên áp dụng | arn:aws:s3:::bucket/* |
Principal |
Chỉ trong resource-based/trust policy | Ai được phép (user/role/service) | {"AWS": "...role/x"} |
Condition |
Không | Điều kiện bổ sung (khóa toán tử) | StringEquals, IpAddress, Bool |
Vì sao Version lại là một ngày cố định: đó không phải ngày bạn viết policy mà là phiên bản của ngữ pháp policy language. AWS đóng băng giá trị này; ghi sai (vd ngày hôm nay) sẽ làm condition/variable không hoạt động đúng.
Các khóa Condition toán tử thường gặp:
| Toán tử | Dùng cho | Ví dụ key |
|---|---|---|
StringEquals / StringLike |
so chuỗi (Like hỗ trợ *) |
aws:PrincipalTag/team |
IpAddress / NotIpAddress |
CIDR | aws:SourceIp |
Bool |
true/false | aws:MultiFactorAuthPresent, aws:SecureTransport |
DateGreaterThan |
thời gian | aws:CurrentTime |
ArnLike |
so ARN | aws:SourceArn |
13.3.3. Thuật toán đánh giá quyền (Policy Evaluation Logic) — từng bước
Khi một request API tới, AWS chạy quy trình quyết định Allow / Deny như sau:
1. Mặc định: DENY ngầm (implicit deny) cho mọi thứ.
2. Thu thập TẤT CẢ policy áp dụng: identity-based, resource-based,
permission boundary, SCP (Organizations), session policy.
3. Có Explicit DENY ở bất kỳ policy nào? --> YES => DENY (kết thúc).
4. SCP cho phép Action? --> NO => DENY.
5. Permission boundary cho phép? --> NO => DENY.
6. Có Explicit ALLOW trong identity/resource policy? --> YES => ALLOW.
7. Ngược lại --> DENY (implicit).
Sơ đồ state machine:
+----------------+
| Implicit Deny | (trạng thái khởi đầu)
+-------+--------+
|
có Explicit Deny? --yes--> [DENY] (luôn thắng)
| no
SCP allow & Boundary allow & Explicit Allow? --no--> [DENY]
| yes
[ALLOW]
Lưu ý: Explicit Deny luôn thắng mọi Allow. Đây là nền tảng để dựng guardrail (hàng rào) cho tài khoản: dù ai đó vô tình cấp AdministratorAccess, một Deny trong SCP/boundary vẫn chặn được.
13.3.4. STS AssumeRole và Trust Policy
STS (Security Token Service) cấp credential tạm thời. Một Role có hai phần policy:
- Permission policy: role làm được gì.
- Trust policy (assume role policy document): ai được phép assume role này — đây là một resource-based policy đặc biệt với trường
Principal.
Trust policy mẫu (cho phép một role trong tài khoản khác assume, kèm ExternalId chống "confused deputy"):
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::222233334444:role/partner-app" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "U7x-9213-secret" }
}
}]
}
Lệnh assume thật và output:
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/app-role \
--role-session-name audit-2026 \
--external-id U7x-9213-secret \
--duration-seconds 3600
Output (rút gọn):
{
"Credentials": {
"AccessKeyId": "ASIA....EXAMPLE",
"SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"SessionToken": "FwoGZXIvYXdz....<rất dài>",
"Expiration": "2026-06-19T15:00:00Z"
},
"AssumedRoleUser": {
"Arn": "arn:aws:sts::123456789012:assumed-role/app-role/audit-2026"
}
}
Phân biệt qua prefix của AccessKeyId — đây là dấu hiệu nhận diện quan trọng khi điều tra log:
| Prefix | Loại credential | Thời hạn |
|---|---|---|
AKIA |
Long-term (IAM user access key) | Vĩnh viễn tới khi xóa |
ASIA |
Temporary (STS) | Có Expiration |
Vì sao cần ExternalId: chống tấn công confused deputy. Nếu một nhà cung cấp SaaS dùng cùng một role-arn cho nhiều khách hàng, kẻ tấn công biết role-arn của bạn có thể lừa SaaS assume role của bạn. ExternalId là bí mật chỉ bạn và SaaS biết, đính kèm trong condition để chặn.
13.3.5. Permission Boundary
Là một managed policy gắn vào user/role, đặt trần quyền tối đa. Quyền hiệu lực = giao (intersection) của permission policy AND boundary.
Quyền hiệu lực = (Identity policy ALLOW) ∩ (Boundary ALLOW) - (mọi DENY)
Ví dụ: developer được gắn AdministratorAccess nhưng boundary chỉ cho s3:* và ec2:* => thực tế chỉ thao tác được S3 và EC2. Dùng để ủy quyền tạo IAM an toàn (cho dev tự tạo role nhưng không vượt quá boundary).
13.3.6. MFA
MFA TOTP theo RFC 6238 (TOTP) dựa trên RFC 4226 (HOTP). Mã 6 chữ số tính từ:
TOTP = HOTP(K, T) với T = floor((UnixTime - T0) / X)
K = shared secret (Base32), X = 30 giây (time step), T0 = 0
HOTP = Truncate( HMAC-SHA1(K, T) ) mod 10^6
Ép buộc MFA bằng condition aws:MultiFactorAuthPresent (như mục 13.3.2). Lưu ý: với root user, bật MFA phần cứng/ảo là biện pháp ưu tiên số một.
13.4. AWS VPC — Virtual Private Cloud
Sơ đồ kiến trúc VPC điển hình (subnet public/private, IGW/NAT, vị trí SG và NACL):
Internet
|
+-----------+
| IGW | (Internet Gateway)
+-----------+
|
VPC 10.0.0.0/16 |
+-------------------------|-----------------------------------+
| PUBLIC SUBNET 10.0.1.0/24 |
| route: 0.0.0.0/0 -> IGW [NACL áp ở mức subnet] |
| +----------------+ +-----------------+ |
| | NAT Gateway | | Bastion/LB | (SG ở mức ENI) |
| +-------+--------+ +-----------------+ |
| | |
| ---------|------------------------------------------------ |
| PRIVATE SUBNET 10.0.2.0/24 |
| route: 0.0.0.0/0 -> NAT GW [NACL áp ở mức subnet] |
| +----------------+ +-----------------+ |
| | App server |----->| Database (RDS) | (SG ở mức ENI) |
| | (SG) | | (SG) | |
| +----------------+ +-----------------+ |
| không có public IP — chỉ ra Internet QUA NAT GW |
+-------------------------------------------------------------+
Điểm then chốt: NACL lọc ở biên subnet (stateless), còn Security Group lọc ngay tại ENI của từng instance (stateful). Tài nguyên nhạy cảm (DB) đặt trong private subnet, chỉ ra Internet một chiều qua NAT Gateway và không nhận kết nối đến từ Internet.
13.4.1. CIDR và phân bổ địa chỉ
VPC là mạng ảo cô lập L3. Bạn gán một khối CIDR (Classless Inter-Domain Routing).
Ký hiệu CIDR 10.0.0.0/16:
10.0.0.0/16 => 10.0.0.0 - 10.0.255.255
/16 = 32 - 16 = 16 bit host => 2^16 = 65536 địa chỉ
| Khái niệm | Bit | Ý nghĩa |
|---|---|---|
| Địa chỉ IPv4 | 32 bit | 4 octet |
Prefix /n |
n bit network | phần cố định |
| Host bits | 32 - n | số host |
AWS chiếm 5 địa chỉ đầu/cuối mỗi subnet: .0 (network), .1 (VPC router), .2 (DNS Amazon-provided), .3 (dự phòng tương lai), và .255 (broadcast — dù VPC không broadcast, vẫn giữ chỗ). Nên subnet /24 (256 địa chỉ) chỉ dùng được 251 host.
13.4.2. Subnet public vs private
Khác biệt không nằm ở cấu hình subnet mà ở route table:
- Public subnet: route table có đường
0.0.0.0/0 -> igw-xxxx(Internet Gateway). - Private subnet: route mặc định trỏ
0.0.0.0/0 -> nat-xxxx(NAT Gateway) hoặc không có đường ra Internet.
Route table mẫu:
Destination Target (public subnet)
10.0.0.0/16 local
0.0.0.0/0 igw-0abc123
Destination Target (private subnet)
10.0.0.0/16 local
0.0.0.0/0 nat-0def456
13.4.3. IGW vs NAT Gateway
| Thành phần | Hướng | Chức năng | Lưu ý |
|---|---|---|---|
| Internet Gateway (IGW) | Hai chiều | Cho phép instance có public IP nhận/gửi Internet, thực hiện NAT 1:1 với Elastic IP | Không tính phí giờ |
| NAT Gateway | Một chiều (ra) | Instance private ra Internet (update, gọi API) nhưng Internet KHÔNG vào được | Tính phí theo giờ + theo GB; đặt trong public subnet |
Vì sao NAT GW phải nằm trong public subnet: bản thân NAT GW cần đường ra qua IGW; instance private trỏ route về NAT GW, NAT GW trỏ route về IGW.
13.4.4. VPC Flow Logs — định dạng bản ghi
Flow Logs ghi metadata luồng (không nội dung). Định dạng default (v2) gồm các trường theo thứ tự:
version account-id interface-id srcaddr dstaddr srcport dstport protocol packets bytes start end action log-status
2 123456789012 eni-0abc 10.0.1.5 203.0.113.7 51000 443 6 20 4520 1655640000 1655640060 ACCEPT OK
| Trường | Ý nghĩa | Ví dụ |
|---|---|---|
version |
phiên bản format | 2 |
srcaddr/dstaddr |
IP nguồn/đích | 10.0.1.5 |
srcport/dstport |
cổng | 443 |
protocol |
số IANA (6=TCP,17=UDP,1=ICMP) | 6 |
action |
ACCEPT/REJECT (theo SG/NACL) |
ACCEPT |
log-status |
OK/NODATA/SKIPDATA |
OK |
Lưu ý: REJECT lặp lại tới nhiều cổng từ một IP = dấu hiệu port scan; dùng làm nguồn cho GuardDuty và phân tích đe dọa.
13.5. Security Group (stateful) vs Network ACL (stateless)
13.5.1. So sánh cơ chế
| Tiêu chí | Security Group | Network ACL (NACL) |
|---|---|---|
| Tầng áp dụng | ENI (network interface của instance) | Subnet |
| Stateful? | CÓ — trả lời tự động được phép | KHÔNG — phải mở cả chiều vào và ra |
| Rule | Chỉ Allow (không có Deny) | Allow VÀ Deny |
| Đánh giá | Tất cả rule cùng lúc (không thứ tự) | Theo THỨ TỰ số rule (thấp -> cao), dừng ở match đầu tiên |
| Mặc định | Deny inbound, Allow all outbound | "default NACL": Allow tất cả; NACL mới tạo: Deny tất cả |
13.5.2. Stateful nghĩa là gì (ở mức gói tin)
Khi instance gửi request ra 443 đến server bên ngoài:
Outbound: src 10.0.1.5:51000 -> dst 1.2.3.4:443 (SG outbound rule cho phép)
Inbound trả về: src 1.2.3.4:443 -> dst 10.0.1.5:51000
- Với SG (stateful): AWS ghi nhớ "connection tracking", gói trả về tự động được phép — KHÔNG cần inbound rule cho port ephemeral 51000.
- Với NACL (stateless): gói trả về về port ephemeral (1024-65535) phải có inbound rule cho phép dải ephemeral, nếu không bị chặn.
Vì sao phải mở ephemeral port range trên NACL: TCP client chọn port nguồn ngẫu nhiên trong dải ephemeral; phản hồi từ server đến đúng port đó. NACL không nhớ trạng thái nên phải khai báo tường minh.
13.5.3. NACL có thứ tự — ví dụ
Rule# Type Protocol Port Source Allow/Deny
100 HTTP TCP 80 0.0.0.0/0 ALLOW
130 SSH TCP 22 0.0.0.0/0 DENY <-- số 130 < 200
200 SSH TCP 22 203.0.113.0/24 ALLOW
* ALL ALL ALL 0.0.0.0/0 DENY
Lưu ý: NACL duyệt từ số nhỏ tới lớn, dừng ở match đầu tiên. Trong ví dụ trên, gói SSH từ 203.0.113.5 khớp rule 130 (DENY) TRƯỚC khi tới rule 200 (ALLOW) => bị chặn. Đây là lỗi sắp xếp số rule điển hình. Luôn để rule cụ thể (allow IP tin cậy) có số NHỎ hơn rule deny rộng.
13.5.4. Ví dụ thực tế tạo SG
aws ec2 create-security-group --group-name web-sg \
--description "Web tier" --vpc-id vpc-0a1b2c3d
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789 \
--protocol tcp --port 443 --cidr 0.0.0.0/0
# Cho phép tier app gọi DB chỉ từ SG của app (tham chiếu SG, không dùng IP)
aws ec2 authorize-security-group-ingress \
--group-id sg-db-999 \
--protocol tcp --port 5432 --source-group sg-app-888
Cảnh báo: tham chiếu SG-to-SG thay vì CIDR là best practice — khi IP instance thay đổi, rule vẫn đúng; và không vô tình mở cho IP lạ. Tuyệt đối tránh --cidr 0.0.0.0/0 cho port 22/3389/3306/5432.
13.6. Amazon S3 — lưu trữ đối tượng
13.6.1. Mô hình truy cập nhiều lớp
Truy cập S3 được quyết định bởi tổ hợp: Block Public Access (BPA) > IAM policy > Bucket policy > ACL. Một explicit Deny ở bất kỳ lớp nào thắng tất cả.
13.6.2. Block Public Access — 4 cờ
| Cờ | Tác dụng |
|---|---|
BlockPublicAcls |
Chặn PUT ACL công khai mới |
IgnorePublicAcls |
Bỏ qua ACL công khai hiện có |
BlockPublicPolicy |
Chặn đặt bucket policy công khai |
RestrictPublicBuckets |
Hạn chế truy cập qua policy công khai chỉ còn principal cùng account/service |
aws s3api put-public-access-block --bucket company-reports \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
Từ tháng 4/2023, AWS bật BPA mặc định cho bucket mới. Vì sao 4 cờ riêng: ACL và policy là hai cơ chế độc lập lịch sử; cần chặn cả hai nguồn "public".
13.6.3. Bucket policy JSON — ép mã hóa khi truyền và chặn non-TLS
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyInsecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::company-reports",
"arn:aws:s3:::company-reports/*"
],
"Condition": { "Bool": { "aws:SecureTransport": "false" } }
},
{
"Sid": "DenyUnEncryptedUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::company-reports/*",
"Condition": {
"StringNotEquals": { "s3:x-amz-server-side-encryption": "aws:kms" }
}
}
]
}
Statement đầu chặn mọi request không qua HTTPS; statement hai từ chối upload không kèm header x-amz-server-side-encryption: aws:kms.
13.6.4. Mã hóa SSE — các loại
| Loại | Key quản lý bởi | Header request | Audit qua CloudTrail |
|---|---|---|---|
| SSE-S3 | AWS (AES-256, key của S3) | x-amz-server-side-encryption: AES256 |
Không thấy từng lần dùng key |
| SSE-KMS | AWS KMS (CMK của bạn) | ...: aws:kms + x-amz-server-side-encryption-aws-kms-key-id |
CÓ — mỗi Decrypt ghi CloudTrail |
| SSE-C | Khách hàng cung cấp key mỗi request | gửi key trong header | Bạn tự quản key |
| DSSE-KMS | Mã hóa hai lớp KMS | aws:kms:dsse |
Cho yêu cầu tuân thủ cao |
Từ đầu 2023, S3 áp dụng SSE-S3 mặc định cho mọi object mới (cần kiểm chứng phiên bản chính xác theo region). Vì sao chọn SSE-KMS: kiểm soát được ai dùng key (qua KMS key policy) và có dấu vết audit từng lần giải mã — cực kỳ giá trị cho điều tra.
13.6.5. Versioning
Bật versioning giữ mọi phiên bản object; xóa chỉ đặt "delete marker". Chống ghi đè/xóa do mã độc (ransomware) và lỗi người dùng. Kết hợp với MFA Delete và Object Lock (WORM) cho bất biến.
aws s3api put-bucket-versioning --bucket company-reports \
--versioning-configuration Status=Enabled
13.6.6. Mẫu sự cố rò rỉ điển hình
Kịch bản lặp lại nhiều lần trong thực tế: bucket chứa backup/PII được đặt ACL public-read hoặc bucket policy Principal:"*" mà không có điều kiện. Bất kỳ ai có URL https://bucket.s3.amazonaws.com/key đọc được dữ liệu.
Phát hiện bằng lệnh kiểm tra nhanh:
# Liệt kê bucket KHÔNG bật đủ Block Public Access
for b in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
echo "== $b =="
aws s3api get-public-access-block --bucket "$b" \
--query 'PublicAccessBlockConfiguration' 2>/dev/null \
|| echo " !! KHONG CO public-access-block (rui ro)" # khong co Block Public Access
done
13.7. AWS KMS — Key Management Service
13.7.1. CMK và phân loại key
| Loại key | Ai quản lý | Rotation (xoay key) tự động |
|---|---|---|
| AWS owned | AWS dùng nội bộ, bạn không thấy | Tự động |
AWS managed (aws/s3...) |
AWS thay mặt dịch vụ | Tự động hàng năm |
| Customer managed (CMK) | Bạn (key policy, rotation, alias) | Tùy chọn (mặc định 1 năm) |
13.7.2. Envelope Encryption — từng bước
KMS không mã hóa khối dữ liệu lớn trực tiếp (giới hạn ~4 KB cho Encrypt). Thay vào đó dùng envelope encryption (nguyên lý AES, AES-GCM và quản lý khóa xem Chương 4):
1. Client gọi KMS GenerateDataKey(KeyId=CMK, KeySpec=AES_256).
2. KMS trả về:
- Plaintext data key (DEK) : 256-bit dùng mã hóa dữ liệu cục bộ
- CiphertextBlob : chính DEK đó nhưng đã được CMK mã hóa
3. Client dùng DEK plaintext (AES-256-GCM) mã hóa file lớn.
4. Client GHI: ciphertext_file + CiphertextBlob(DEK đã mã hóa).
5. Client XÓA DEK plaintext khỏi RAM.
Giải mã:
6. Client gọi KMS Decrypt(CiphertextBlob) -> nhận lại DEK plaintext.
7. Dùng DEK giải mã file.
Sơ đồ:
[CMK trong KMS - không bao giờ rời HSM]
| mã hóa/giải mã
v
[DEK đã mã hóa: CiphertextBlob] <-- lưu cạnh dữ liệu
| Decrypt qua KMS
v
[DEK plaintext] --AES-256-GCM--> [dữ liệu]
Vì sao thiết kế envelope: (1) giảm số lần gọi KMS (mã hóa dữ liệu cục bộ nhanh); (2) CMK gốc không bao giờ rời khỏi HSM của KMS; (3) thu hồi quyền Decrypt CMK lập tức vô hiệu mọi DEK.
Ví dụ thực tế:
# Tạo DEK
aws kms generate-data-key --key-id alias/app-cmk --key-spec AES_256 \
--query '{plain:Plaintext, blob:CiphertextBlob}' --output json
# Plaintext (base64) dùng để mã hóa; CiphertextBlob lưu cùng ciphertext.
13.7.3. KMS Key Policy
Khác IAM: KMS luôn cần key policy (resource-based). Một key policy phổ biến trao quyền quản trị cho account root rồi ủy quyền tiếp qua IAM:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "EnableRootIAM",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "kms:*",
"Resource": "*"
}]
}
Lưu ý: xóa toàn bộ statement trao root quyền có thể khiến key không thể quản lý được nữa (cần mở ticket AWS Support để khôi phục).
13.8. CloudTrail — nhật ký API
13.8.1. Là gì
Ghi lại mọi lệnh gọi API (management events) và tùy chọn data events (S3 object-level, Lambda invoke). Là nguồn dữ liệu điều tra số một trong AWS.
13.8.2. Cấu trúc một event JSON — từng trường
{
"eventVersion": "1.09",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAEXAMPLE:audit-2026",
"arn": "arn:aws:sts::123456789012:assumed-role/app-role/audit-2026",
"accountId": "123456789012",
"accessKeyId": "ASIA....EXAMPLE",
"sessionContext": {
"attributes": { "mfaAuthenticated": "false", "creationDate": "2026-06-19T14:00:00Z" }
}
},
"eventTime": "2026-06-19T14:05:11Z",
"eventSource": "s3.amazonaws.com",
"eventName": "GetObject",
"awsRegion": "us-east-1",
"sourceIPAddress": "203.0.113.7",
"userAgent": "aws-cli/2.15.0",
"requestParameters": { "bucketName": "company-reports", "key": "q1.pdf" },
"responseElements": null,
"readOnly": true,
"eventType": "AwsApiCall",
"recipientAccountId": "123456789012",
"eventID": "a1b2c3d4-....",
"managementEvent": false
}
| Trường | Ý nghĩa điều tra |
|---|---|
userIdentity.type |
Root, IAMUser, AssumedRole, AWSService — ai gọi |
userIdentity.accessKeyId |
ASIA=tạm thời, AKIA=lâu dài (xem 13.3.4) |
sessionContext.mfaAuthenticated |
"false" đáng nghi với hành động nhạy cảm |
eventSource + eventName |
dịch vụ + API cụ thể |
sourceIPAddress |
IP nguồn — đối chiếu IP lạ/quốc gia lạ |
errorCode (nếu có) |
AccessDenied lặp lại = dấu hiệu dò quyền |
Lưu ý: bật log file integrity validation (CloudTrail tạo digest ký số SHA-256) để phát hiện log bị giả mạo/xóa. Lưu log vào bucket riêng có Object Lock.
13.9. CloudWatch
CloudWatch = metrics + logs + alarms. Liên quan bảo mật:
- CloudWatch Logs: gom log ứng dụng/VPC Flow Logs.
- Metric filter + Alarm: phát hiện hành vi từ CloudTrail. Ví dụ tạo alarm khi có
ConsoleLoginthất bại nhiều lần hoặc khi root đăng nhập.
aws logs put-metric-filter \
--log-group-name CloudTrail/Logs \
--filter-name RootLogin \
--filter-pattern '{ $.userIdentity.type = "Root" && $.eventName = "ConsoleLogin" }' \
--metric-transformations metricName=RootLoginCount,metricNamespace=Security,metricValue=1
Filter pattern dùng cú pháp JSON path; $.userIdentity.type trỏ vào trường lồng. Sau đó gắn put-metric-alarm để gửi SNS cảnh báo.
13.10. GuardDuty
13.10.1. Là gì
Dịch vụ phát hiện mối đe dọa dựa trên ML + threat intel, phân tích CloudTrail, VPC Flow Logs, DNS logs, EKS audit logs, S3 data events — không cần cài agent.
13.10.2. Các họ finding (định dạng tên)
Tên finding theo cấu trúc ThreatPurpose:ResourceType/ThreatFamilyName.DetectionMechanism!Artifact:
| Finding ví dụ | Ý nghĩa |
|---|---|
UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS |
Credential của EC2 role bị dùng từ ngoài AWS (dấu hiệu IMDS bị đánh cắp) |
Recon:IAMUser/MaliciousIPCaller |
API gọi từ IP độc hại đã biết |
CryptoCurrency:EC2/BitcoinTool.B!DNS |
EC2 truy vấn domain mining tiền số |
Backdoor:EC2/C&CActivity.B |
EC2 liên lạc C2 |
Exfiltration:S3/ObjectRead.Unusual |
Đọc S3 bất thường |
Policy:IAMUser/RootCredentialUsage |
Dùng credential root |
Vì sao InstanceCredentialExfiltration.OutsideAWS rất mạnh: nó tương quan vị trí — credential EC2 role phải được dùng từ trong AWS; nếu xuất hiện từ IP bên ngoài, gần như chắc chắn đã bị trích xuất qua SSRF/IMDS.
13.11. IMDS — Instance Metadata Service (v1 vs v2)
13.11.1. Là gì
IMDS phục vụ metadata tại địa chỉ link-local cố định 169.254.169.254. Quan trọng nhất: nó cấp credential tạm thời của IAM role gắn vào instance.
# Lấy tên role
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Lấy credential
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/app-role
Output credential:
{
"Code": "Success",
"AccessKeyId": "ASIA....",
"SecretAccessKey": "....",
"Token": "....",
"Expiration": "2026-06-19T20:00:00Z"
}
13.11.2. IMDSv1 (request/response đơn giản — và lỗ hổng SSRF)
IMDSv1: chỉ cần một HTTP GET tới 169.254.169.254. KHÔNG cần token. Đây là gốc của vô số sự cố SSRF (cơ chế SSRF được trình bày ở Chương 5 — An ninh ứng dụng Web).
Kịch bản tấn công SSRF qua IMDSv1:
1. Ứng dụng web có tham số: https://app/fetch?url=<URL> (server tự GET URL).
2. Kẻ tấn công gửi: ?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/app-role
3. Server (chạy trên EC2) GET hộ, trả về credential ASIA/Secret/Token.
4. Kẻ tấn công dùng credential từ máy của họ (ngoài AWS) -> GuardDuty
InstanceCredentialExfiltration.OutsideAWS.
13.11.3. IMDSv2 — session token, từng bước
IMDSv2 yêu cầu quy trình hai bước PUT lấy token rồi GET kèm token:
# Bước 1: PUT lấy token (TTL tối đa 21600s = 6h)
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# Bước 2: GET kèm token trong header
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ \
-H "X-aws-ec2-metadata-token: $TOKEN"
| Cơ chế bảo vệ IMDSv2 | Vì sao chặn được SSRF |
|---|---|
| Yêu cầu PUT lấy token | Đa số lỗ hổng SSRF chỉ cho GET, không gửi được PUT tùy ý có header |
Yêu cầu header tùy chỉnh X-aws-ec2-metadata-token |
SSRF cơ bản không thêm được header này |
Mặc định X-Forwarded-For => từ chối |
Chặn khi request đi qua proxy/WAF/reverse proxy (dấu hiệu SSRF/relay) |
| Giới hạn PUT response hop limit = 1 | Token không "nhảy" qua container/route khác; gói có IP-TTL bị giảm sẽ bị từ chối |
Ép buộc IMDSv2 (chặn hẳn v1):
aws ec2 modify-instance-metadata-options \
--instance-id i-0123456789 \
--http-tokens required \
--http-put-response-hop-limit 1 \
--http-endpoint enabled
--http-tokens required = bắt buộc token (tắt v1). --http-put-response-hop-limit 1 = gói phản hồi metadata chỉ đi được 1 hop (IP TTL), ngăn container trên instance hoặc reverse proxy chuyển tiếp metadata.
Lưu ý: chặn IMDS ở tầng mạng cũng nên kèm — ví dụ iptables drop ra 169.254.169.254 cho user của ứng dụng web không cần metadata.
13.12. AWS Organizations & SCP (Service Control Policy)
13.12.1. Là gì
Organizations quản lý nhiều account theo cây OU (Organizational Unit). SCP là guardrail áp ở cấp org/OU/account — đặt trần quyền tối đa, KHÔNG tự cấp quyền.
Root
├── OU: Security (SCP: Deny tắt CloudTrail)
├── OU: Prod (SCP: chỉ cho region us-east-1, eu-west-1)
└── OU: Sandbox (SCP: Deny dịch vụ đắt tiền)
13.12.2. SCP ví dụ — khóa region và chặn tắt audit
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyOutsideAllowedRegions",
"Effect": "Deny",
"NotAction": [ "iam:*", "sts:*", "organizations:*", "cloudfront:*", "route53:*" ],
"Resource": "*",
"Condition": {
"StringNotEquals": { "aws:RequestedRegion": [ "us-east-1", "eu-west-1" ] }
}
},
{
"Sid": "ProtectCloudTrail",
"Effect": "Deny",
"Action": [ "cloudtrail:StopLogging", "cloudtrail:DeleteTrail" ],
"Resource": "*"
}
]
}
Vì sao NotAction chứa iam/sts: các dịch vụ này là global (region us-east-1 ngầm); chặn theo region có thể khóa nhầm chúng. SCP áp cho cả root user của member account — đây là cách duy nhất hạn chế được root.
13.13. GCP — tương đương AWS
13.13.0. Bảng ánh xạ AWS ↔ GCP
Tra nhanh trước khi vào chi tiết: học một nền tảng rồi suy ra nền tảng còn lại.
| Khái niệm | AWS | GCP |
|---|---|---|
| Danh tính người | IAM User | Google account / member |
| Danh tính workload | IAM Role | Service Account |
| Token tạm thời | STS | SA token qua metadata / STS API |
| Mạng ảo | VPC (theo region) | VPC (global), subnet theo region |
| Tường lửa instance | Security Group (stateful) | Firewall rule (stateful, có priority) |
| ACL stateless | NACL | (không có tương đương trực tiếp; dùng firewall priority) |
| Lưu trữ đối tượng | S3 | Cloud Storage |
| Quản lý khóa | KMS | Cloud KMS |
| Audit API | CloudTrail | Cloud Audit Logs |
| Metrics/log | CloudWatch | Cloud Monitoring / Logging |
| Phát hiện mối đe dọa | GuardDuty | SCC / Event Threat Detection |
| Guardrail tổ chức | SCP | Organization Policy |
| Quản lý bí mật | Secrets Manager | Secret Manager |
| Metadata service | IMDS 169.254.169.254 | metadata.google.internal (169.254.169.254) |
13.13.1. GCP IAM
GCP IAM gắn quyền theo công thức binding: ai (member) + vai trò (role) + tài nguyên (qua hệ phân cấp).
who = member (user:, serviceAccount:, group:, domain:, allUsers, allAuthenticatedUsers)
what = role (roles/storage.objectViewer ...)
where = resource (Organization > Folder > Project > Resource) -- quyền KẾ THỪA xuống dưới
Phân loại role:
| Loại role | Mô tả | Ví dụ | Lưu ý |
|---|---|---|---|
| Primitive (basic) | Owner/Editor/Viewer áp toàn project | roles/owner |
Quá rộng — tránh dùng |
| Predefined | Do Google định nghĩa, hạt mịn theo dịch vụ | roles/storage.objectAdmin |
Khuyến nghị |
| Custom | Bạn tự gộp permission | projects/p/roles/myAuditor |
Least privilege thực sự |
IAM Policy binding ví dụ (định dạng JSON khi getIamPolicy):
{
"version": 3,
"bindings": [
{
"role": "roles/storage.objectViewer",
"members": [ "user:alice@example.com", "group:auditors@example.com" ],
"condition": {
"title": "only-prod-bucket",
"expression": "resource.name.startsWith('projects/_/buckets/prod-')"
}
}
],
"etag": "BwXyz..."
}
| Trường | Ý nghĩa | Ví dụ |
|---|---|---|
version |
Phiên bản schema policy (3 để dùng condition) | 3 |
bindings[].role |
Vai trò gán | roles/storage.objectViewer |
bindings[].members[] |
Danh sách thành viên (có prefix loại) | user:, serviceAccount: |
condition.expression |
Biểu thức CEL (Common Expression Language) | điều kiện theo tài nguyên/thời gian |
etag |
Khóa tránh ghi đè đồng thời (optimistic locking) | base64 |
Cảnh báo: allUsers (bất kỳ ai trên Internet) và allAuthenticatedUsers (bất kỳ tài khoản Google nào) là tương đương "public" — nguồn rò rỉ Cloud Storage hàng đầu.
13.13.2. Service Account & Service Account Key
Service Account (SA) là danh tính cho workload. Có hai loại bí mật:
- Google-managed key: Google tự xoay, dùng khi workload chạy trên GCP (Metadata server cấp token tự động — tương đương IAM role của AWS).
- User-managed key (JSON): file
.jsontải về — bí mật lâu dài, là rủi ro lớn nhất nếu lọt vào git.
Cấu trúc file SA key JSON:
{
"type": "service_account",
"project_id": "my-project",
"private_key_id": "a1b2c3...",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----\n",
"client_email": "app-sa@my-project.iam.gserviceaccount.com",
"client_id": "10293847566...",
"token_uri": "https://oauth2.googleapis.com/token"
}
| Trường | Ý nghĩa |
|---|---|
type |
luôn service_account — dấu hiệu nhận diện khi quét secret |
private_key |
RSA private key PEM — bí mật cốt lõi |
client_email |
định danh SA (kiểm tra quyền của email này) |
token_uri |
endpoint đổi JWT lấy access token OAuth2 |
Lưu ý: nên vô hiệu hóa tạo user-managed key qua Org Policy iam.disableServiceAccountKeyCreation và dùng Workload Identity Federation thay vì key file.
13.13.3. GCP VPC
Khác AWS điểm then chốt: VPC của GCP là global, subnet thuộc region (AWS: VPC theo region, subnet theo AZ). Firewall rules áp ở mức VPC, theo network tag/SA, có priority (0-65535, nhỏ = ưu tiên cao), stateful.
gcloud compute firewall-rules create allow-https \
--network=prod-vpc --direction=INGRESS --action=ALLOW \
--rules=tcp:443 --source-ranges=0.0.0.0/0 \
--target-tags=web --priority=1000
13.13.4. Cloud Storage (tương đương S3)
- Quyền: IAM (uniform bucket-level access — khuyến nghị) hoặc ACL (fine-grained — nên tắt).
- Bật Uniform bucket-level access để bỏ ACL, tránh nhầm lẫn ACL public.
- Mã hóa mặc định Google-managed; chọn CMEK (Customer-Managed Encryption Key qua Cloud KMS) hoặc CSEK.
gsutil uniformbucketlevelaccess set on gs://prod-data
# Phát hiện public:
gsutil iam get gs://prod-data | grep -E "allUsers|allAuthenticatedUsers"
13.13.5. Cloud Logging (tương đương CloudTrail/CloudWatch Logs)
GCP ghi Audit Logs chia loại:
| Loại Audit Log | Nội dung | Mặc định |
|---|---|---|
| Admin Activity | thay đổi cấu hình/IAM | Luôn bật, không tắt được |
| Data Access | đọc/ghi dữ liệu | Mặc định TẮT (trừ BigQuery) — phải bật |
| System Event | hành động hệ thống của Google | Luôn bật |
| Policy Denied | bị từ chối do policy | Tự động |
Một entry Cloud Audit Log (định dạng protoPayload kiểu AuditLog):
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": { "principalEmail": "alice@example.com" },
"requestMetadata": { "callerIp": "203.0.113.7" },
"serviceName": "storage.googleapis.com",
"methodName": "storage.objects.get",
"resourceName": "projects/_/buckets/prod-data/objects/q1.pdf"
},
"severity": "INFO",
"timestamp": "2026-06-19T14:05:11Z",
"logName": "projects/my-project/logs/cloudaudit.googleapis.com%2Fdata_access"
}
Lưu ý: Data Access log TẮT mặc định — nếu không bật, bạn không có dấu vết ai đọc object nào (khác AWS cần bật S3 data events). Đây là khoảng mù điều tra phổ biến.
13.13.6. Security Command Center (tương đương GuardDuty + Security Hub + Config)
SCC là nền tảng quản lý tư thế bảo mật của GCP: phát hiện misconfig, lỗ hổng, mối đe dọa.
| Module | Vai trò |
|---|---|
| Security Health Analytics | Quét misconfig (bucket public, SA key, firewall mở) — kiểu CSPM |
| Event Threat Detection | Phân tích log phát hiện hành vi (brute force SSH, IAM bất thường) |
| Container Threat Detection | Phát hiện runtime trong GKE |
| Web Security Scanner | Quét app web |
Finding ví dụ: PUBLIC_BUCKET_ACL, SERVICE_ACCOUNT_KEY_NOT_ROTATED, OPEN_FIREWALL, MFA_NOT_ENFORCED.
13.14. OCI — Oracle Cloud Infrastructure
13.14.1. Là gì và vì sao mình phải học nó
Nhiều nơi chạy song song hai đám mây, và OCI hay xuất hiện ở vế thứ hai vì giá compute rẻ hơn đáng kể. Lý do phải nắm nó: OCI không phải bản sao đổi tên của AWS — nhiều khái niệm nền tảng được thiết kế khác hẳn, và người quen tư duy AWS rất dễ hiểu nhầm rồi cấu hình sai. Mục này ghi lại đúng những chỗ mình từng bỡ ngỡ, và đối chiếu 1-1 với AWS để chuyển tư duy cho nhanh.
Điểm khác biệt lớn nhất phải nắm trước: AWS cô lập bằng account (tài khoản), còn OCI cô lập bằng compartment (ngăn) bên trong một tenancy. Hiểu sai chỗ này là hiểu sai toàn bộ mô hình phân quyền của OCI.
13.14.2. Tenancy và Compartment — cây phân quyền logic
- Tenancy: gốc của toàn bộ tài nguyên OCI của một tổ chức — tương đương "AWS Organizations + account gốc" gộp làm một. Mỗi tài nguyên OCI có một OCID (Oracle Cloud ID) dạng
ocid1.<type>.<realm>..<hash>, luôn thuộc về một tenancy. - Compartment: một cây thư mục logic để nhóm tài nguyên và áp quyền. Đây là chỗ khác AWS nhiều nhất. Trên AWS, muốn tách môi trường/nhóm người ta thường tách account rồi ghép bằng Organizations; trên OCI, một tenancy duy nhất chứa nhiều compartment lồng nhau (
dev,prod,prod/db,shared-network...), và quyền được cấp theo compartment.
Tenancy (gốc)
├── Compartment: network-shared (VCN, subnet dùng chung)
├── Compartment: dev (tài nguyên môi trường dev)
│ └── Compartment: dev/app
└── Compartment: prod (tài nguyên prod chạy trên OCI)
├── Compartment: prod/app
└── Compartment: prod/data (bucket, DB — siết chặt nhất)
Vì sao compartment tiện: phân quyền, quota và cả bảng theo dõi chi phí đều gắn theo compartment; xóa compartment là dọn sạch mọi thứ bên trong. Nhưng cũng chính vì "một tenancy nhiều compartment", ranh giới cô lập yếu hơn tách account của AWS — nếu viết policy lỏng ở cấp tenancy, quyền có thể rò xuống mọi compartment. Đây là lý do phần prod chạy trên OCI phải đặt trong compartment riêng và siết policy đúng phạm vi (xem 13.14.9).
13.14.3. IAM policy dạng câu lệnh (khác hẳn JSON của AWS)
Chỗ này làm mình bỡ ngỡ nhất khi từ AWS sang. IAM policy của OCI không phải tài liệu JSON với Effect/Action/Resource như AWS — nó là những câu tiếng Anh gần như đọc được thành lời:
Allow group <tên-group> to <động-từ> <loại-tài-nguyên> in compartment <tên> [where <điều-kiện>]
Ví dụ thật:
Allow group Developers to manage object-family in compartment dev
Allow group DBAdmins to manage database-family in compartment prod:data
Allow group Auditors to read all-resources in tenancy
Allow group AppOps to use secret-family in compartment prod where request.region = 'ap-singapore-1'
Bốn động từ quyền xếp theo mức tăng dần (đây là điểm cần nhớ, khác cách liệt kê từng action của AWS):
| Động từ | Bao gồm | Tương đương AWS (khái niệm) |
|---|---|---|
inspect |
liệt kê tài nguyên (metadata, không nội dung nhạy cảm) | List* |
read |
inspect + đọc nội dung/chi tiết | List* + Get*/Describe* |
use |
read + thao tác/cập nhật hiện có (không tạo/xóa) | Get* + Update*/thao tác chạy |
manage |
use + tạo và xóa | quyền đầy đủ trên loại tài nguyên |
<loại-tài-nguyên> là các "family" gộp sẵn: object-family (Object Storage), instance-family (Compute), virtual-network-family (VCN), secret-family, vaults, keys... Mệnh đề where gắn điều kiện (region, thời gian, target.compartment.name, tag...) — tương đương Condition của AWS nhưng viết chung trong câu.
Vì sao mình thích cú pháp này: đọc policy OCI gần như đọc chính sách bằng tiếng Anh, ít bị "JSON đúng cú pháp nhưng sai ý" như AWS. Chỗ dễ sai: không có Deny tường minh — OCI IAM chỉ có Allow (mặc định deny ngầm). Muốn "cấm" thì phải không cấp, hoặc siết bằng phạm vi compartment, hoặc dùng cơ chế khác (Network Sources, tag-based). Thói quen viết Deny guardrail như SCP của AWS không áp thẳng được — cần nghĩ lại theo hướng thu hẹp phạm vi cấp quyền.
13.14.4. Dynamic Group + Instance/Resource Principal (danh tính workload, không cắm key tĩnh)
Đây là phần mình đánh giá cao nhất của OCI, và nó tương đương thẳng với "IAM role gắn vào EC2" của AWS: cho phép compute/function tự lấy credential tạm để gọi OCI API mà không cắm key tĩnh vào code.
Cơ chế hai bước:
- Dynamic Group: nhóm các tài nguyên (không phải người dùng) theo quy tắc khớp — ví dụ mọi instance trong một compartment, hoặc function cụ thể.
# Matching rule của một dynamic group
ALL {instance.compartment.id = 'ocid1.compartment.oc1..aaaa....dev'}
- Policy cấp quyền cho dynamic group đó (viết y như policy thường, chủ thể là
dynamic-group):
Allow dynamic-group DevInstances to read secret-family in compartment dev
Allow dynamic-group DevInstances to use keys in compartment dev
Sau đó code chạy trên instance dùng Instance Principal để tự xác thực (SDK tự lấy chứng chỉ tạm từ metadata endpoint), hoàn toàn không có key file:
import oci
signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
secrets = oci.secrets.SecretsClient(config={}, signer=signer)
# gọi API với danh tính máy — credential tạm, tự xoay, không nằm trên đĩa
- Instance Principal: cho VM/compute.
- Resource Principal: cho serverless (OCI Functions) và một số dịch vụ managed — tương đương execution role của Lambda.
Vì sao quan trọng: giống AWS, mấu chốt là "danh tính máy, không phải key file". Kể cả "secret để lấy secret" cũng không nằm trên đĩa: app dùng Instance Principal tự xác thực rồi mới fetch bí mật thật từ Vault lúc khởi động. Đây là cách đúng để service ở môi trường dev OCI truy cập Vault mà không hardcode credential.
13.14.5. OCI Vault — KMS + Secrets gộp một chỗ
OCI Vault gộp hai vai trò mà AWS tách thành KMS (khóa) và Secrets Manager (bí mật):
- Keys (khóa mã hóa): master key bảo vệ trong HSM. Hai loại vault: Default (dùng chung phân vùng HSM với khách khác — rẻ, đủ cho hầu hết nhu cầu) và Virtual Private Vault (được cấp phân vùng HSM riêng, mức cô lập cao hơn, dành cho yêu cầu tuân thủ ngặt — cần kiểm chứng giá và giới hạn theo thời điểm). Dùng cho encryption at-rest của Block Volume, Object Storage, DB. Hỗ trợ rotation và envelope encryption y hệt nguyên lý KMS (mục 13.7.2).
- Secrets (bí mật): DB password, API key... lưu mã hóa bằng chính key trong Vault, quản theo version bất biến.
# Đọc một secret (bundle) — nội dung trả về ở dạng base64
oci secrets secret-bundle get-secret-bundle-by-name \
--secret-name db-password --vault-id ocid1.vault.oc1..aaaa
Kiến trúc bí mật thực tế của mình: prod dùng AWS Secrets Manager, phần chạy trên OCI dùng OCI Vault — cùng một nguyên tắc: app không giữ key tĩnh để gọi secret store, nó dùng IAM role (AWS) / Instance Principal (OCI) tự xác thực rồi fetch bí mật lúc runtime.
13.14.6. VCN — Security List (mức subnet) vs NSG (mức VNIC)
VCN (Virtual Cloud Network) là mạng ảo cô lập của OCI, tương đương VPC. Nhưng cơ chế tường lửa của OCI có hai lớp cùng tồn tại, và ánh xạ sang AWS không hoàn toàn 1-1 nên rất dễ nhầm:
| Tiêu chí | Security List (OCI) | Network Security Group / NSG (OCI) |
|---|---|---|
| Gắn ở đâu | Subnet (áp cho MỌI VNIC trong subnet) | VNIC của từng tài nguyên (nhóm theo chức năng) |
| Gần với AWS nào | gần NACL (mức subnet) nhưng stateful mặc định | gần Security Group (mức interface) |
| Stateful? | Từng rule chọn được: stateful hoặc stateless | Stateful mặc định (chọn stateless được) |
| Tham chiếu nguồn | CIDR, hoặc Service (cho Service Gateway) | CIDR hoặc chính NSG khác (như SG-to-SG của AWS) |
Điểm dễ sai nhất khi từ AWS sang: trên OCI, một gói tin phải qua CẢ Security List của subnet LẪN NSG của VNIC — cả hai đều phải cho phép thì gói mới đi được (giao AND). Người quen AWS hay chỉ chỉnh SG (nghĩ như Security Group) mà quên Security List ở subnet vẫn đang chặn, hoặc ngược lại.
Khác AWS nữa: Security List mặc định stateful (không phải stateless như NACL), nên thường không phải mở tay dải ephemeral port cho gói trả về — trừ khi cố ý đặt rule stateless. Khuyến nghị thực tế: ưu tiên dùng NSG để phân quyền mạng theo chức năng (tham chiếu NSG-to-NSG, giống best practice SG-to-SG), giữ Security List ở mức tối giản/mặc định.
13.14.7. Object Storage — bucket private mặc định, PAR thay cho public
Object Storage là kho đối tượng của OCI (tương đương S3). Có một điểm mình coi là điểm cộng thiết kế so với lịch sử S3:
- Bucket mặc định là private. Muốn public phải chủ động đổi visibility sang Public. Khác S3 thời kỳ đầu (nơi cấu hình lỏng + ACL public-read là nguồn rò rỉ kinh điển), rủi ro "vô tình public" của OCI thấp hơn theo mặc định — nhưng vẫn phải kiểm, vì đổi nhầm sang public vẫn xảy ra.
- Pre-Authenticated Request (PAR) thay cho việc mở public: tạo một URL có thời hạn hết hạn (expiry) và phạm vi (một object hoặc cả bucket, read/write) — tương đương pre-signed URL của S3. Cần chia sẻ file thì phát PAR có hạn, không để bucket public.
- Mã hóa at-rest bật mặc định (Oracle-managed key); dữ liệu nhạy cảm nên chuyển sang customer-managed key trong OCI Vault để kiểm soát rotation + audit ai dùng key.
# Tạo PAR đọc một object, hết hạn sau 24h — thay cho việc để bucket public
oci os preauth-request create --namespace <ns> --bucket-name reports \
--name share-q1 --access-type ObjectRead \
--object-name q1.pdf --time-expires 2026-08-15T00:00:00Z
Cloud Guard (mục 13.14.8) tự gắn cờ bucket public — nên bật sẵn như một lớp phát hiện.
13.14.8. Cloud Guard — CSPM của OCI
Cloud Guard là dịch vụ quản lý tư thế bảo mật (CSPM) của OCI, tương đương GuardDuty + Security Hub + Config gộp lại (hoặc SCC của GCP):
- Detector quét cấu hình/hành vi rủi ro sinh ra Problem: bucket public, security rule mở
0.0.0.0/0cổng quản trị, IAM policy quá rộng, instance thiếu bản vá, key/secret sắp hết hạn... - Responder cho phép xử lý tự động hoặc bán tự động (ví dụ tự đóng bucket public).
- Đối chiếu benchmark (CIS OCI Foundations).
Cloud Guard đóng vai trò cho môi trường OCI đúng như AWS Config + GuardDuty cho môi trường AWS: phát hiện drift cấu hình, bucket public, rule mạng nới rộng, hành vi bất thường — và đẩy cảnh báo về kênh vận hành thay vì để một người rà tay hàng tháng.
13.14.9. Metadata endpoint OCI — IMDS v2 yêu cầu header Authorization: Bearer Oracle
OCI cũng có metadata service tại đúng địa chỉ link-local 169.254.169.254 như AWS/GCP, và Instance Principal lấy chứng chỉ tạm qua đây — nên cùng bề mặt tấn công SSRF như IMDS của AWS (cơ chế SSRF: Chương 5).
Điểm khác biệt phòng thủ đáng chú ý — cách chống SSRF của mỗi bên:
| AWS IMDSv2 | OCI IMDS v2 | |
|---|---|---|
| Đường dẫn | /latest/meta-data/... |
/opc/v2/... |
| Cơ chế bảo vệ | PUT lấy token trước, rồi GET kèm header token | GET kèm header cố định Authorization: Bearer Oracle |
| Vì sao chặn SSRF ngây thơ | SSRF cơ bản chỉ làm được GET, không PUT được token | SSRF cơ bản không tự thêm được header Authorization |
# OCI IMDS v2 — thiếu header sẽ bị từ chối
curl -H "Authorization: Bearer Oracle" http://169.254.169.254/opc/v2/instance/
curl -H "Authorization: Bearer Oracle" http://169.254.169.254/opc/v2/identity/cert.pem
Lưu ý quan trọng: header Authorization: Bearer Oracle là hằng số cố định, không phải bí mật — nó chỉ nâng rào cho SSRF ngây thơ (loại chỉ chèn được URL, không thêm được header). Một SSRF cho phép điều khiển header vẫn vượt qua được. Vì vậy đừng coi metadata v2 (dù AWS hay OCI) là biện pháp đủ: vẫn phải áp đủ bộ SSRF defense ở tầng ứng dụng (allowlist domain, chặn IP private/link-local 169.254.0.0/16, validate IP sau khi resolve DNS chống rebinding, tắt/validate redirect). OCI v1 (/opc/v1/) không yêu cầu header — nên ép dùng v2 giống như ép IMDSv2 trên AWS.
13.14.10. Bảng ánh xạ AWS ↔ OCI ↔ GCP
Tra nhanh khi nhảy giữa ba nền tảng — học một, suy ra hai cái còn lại:
| Khái niệm | AWS | OCI | GCP |
|---|---|---|---|
| Ranh giới cô lập | Account (+ Organizations) | Compartment (trong 1 tenancy) | Project (+ Folder/Org) |
| Cú pháp IAM policy | JSON (Effect/Action/Resource) |
Câu lệnh (Allow group … to … in compartment …) |
JSON binding (member+role) |
| Danh tính workload | IAM Role gắn EC2 | Instance/Resource Principal (+ Dynamic Group) | Service Account (+ Workload Identity) |
| Guardrail tổ chức | SCP (có Deny) | (không có Deny; siết bằng phạm vi compartment / policy) | Organization Policy |
| Mạng ảo | VPC | VCN | VPC (global) |
| Tường lửa mức interface | Security Group | NSG | Firewall rule (theo tag/SA) |
| Firewall mức subnet | NACL (stateless) | Security List (stateful mặc định) | (không có; dùng priority) |
| Lưu trữ đối tượng | S3 | Object Storage | Cloud Storage |
| Bucket private mặc định | BPA (bật mặc định từ 4/2023) | Mặc định private sẵn | Uniform bucket-level access |
| URL chia sẻ có hạn | Pre-signed URL | Pre-Authenticated Request (PAR) | Signed URL |
| Quản lý khóa | KMS | Vault (Keys) | Cloud KMS |
| Quản lý bí mật | Secrets Manager | Vault (Secrets) | Secret Manager |
| Audit API | CloudTrail | Audit (bật mặc định) | Cloud Audit Logs |
| CSPM / phát hiện đe dọa | GuardDuty + Security Hub + Config | Cloud Guard | Security Command Center |
| Metadata endpoint | 169.254.169.254 /latest/ (IMDSv2: PUT token) |
169.254.169.254 /opc/v2/ (header Bearer Oracle) |
metadata.google.internal |
13.14.11. Nguyên tắc vận hành đa đám mây (kinh nghiệm thực tế)
Vận hành song song AWS (prod) + OCI (dev và một phần prod), mình rút ra mấy nguyên tắc mà lý thuyết ít nói thẳng:
- Ranh giới đúng là prod vs non-prod, KHÔNG phải AWS vs OCI. Việc prod nằm ở AWS còn dev ở OCI chỉ là tình cờ có lợi (tách cả nhà cung cấp lẫn credential nên blast radius nhỏ), nhưng đừng biến "nhà cung cấp" thành tiêu chí phân loại bảo mật.
- Phần prod chạy trên đám mây "dev" phải áp CÙNG chuẩn siết như prod chính. Đây là bẫy nguy hiểm nhất: một compartment prod đặt chung tenancy với dev rất dễ "thừa hưởng" thói quen cấu hình lỏng của dev (rule mạng rộng, bucket dễ dãi, policy tenancy-wide). Prod-OCI phải: least privilege theo compartment, không bucket public, bí mật trong Vault, bật logging/Cloud Guard — y như prod-AWS.
- Tách credential / network / quyền giữa môi trường. Credential dev lộ ra ngoài không được mở nổi bất cứ thứ gì ở prod. Kết nối giữa hai đám mây (nếu có, ví dụ VPN AWS↔OCI) chỉ mở đúng cổng thật sự cần.
- Dữ liệu thật không nằm ở dev. Môi trường dev cấu hình lỏng hơn là chuyện bình thường — nhưng chỉ chấp nhận được khi dev không chạm dữ liệu thật. Rủi ro lớn nhất của mô hình "dev trên OCI" là một máy dev cấu hình lỏng lại vô tình có dữ liệu production.
- Chuẩn hóa bằng IaC để quản hai bộ IAM/tooling. Cái giá của đa đám mây là phải quản hai hệ IAM, hai bộ công cụ; bù lại bằng Terraform/IaC + policy-as-code (Checkov/Trivy quét cả cấu hình AWS lẫn OCI) để không phải nhớ hai lối cấu hình bằng tay.
13.15. Tấn công đám mây và cách phát hiện
13.15.1. Misconfiguration
Nguyên nhân hàng đầu. Phát hiện bằng CSPM (mục 13.16). Các dạng: bucket public, SG mở 0.0.0.0/0 port quản trị (22/3389/database), CloudTrail không bật, mã hóa tắt, IMDSv1 còn bật, IAM policy "Action":"*","Resource":"*".
13.15.2. Credential leak — key trong git
AWS access key có dạng nhận diện rõ:
AKIA[0-9A-Z]{16} <- Access Key ID (20 ký tự, prefix AKIA)
40-ký-tự base64 secret <- Secret Access Key
Quét git bằng các công cụ thật:
# trufflehog quét toàn lịch sử repo và xác thực key còn sống
trufflehog git file://./myrepo --only-verified
# gitleaks
gitleaks detect --source . --report-format json --report-path leaks.json
Output gitleaks mẫu:
{
"Description": "AWS Access Key",
"Secret": "AKIAIOSFODNN7EXAMPLE",
"File": "config/old_settings.py",
"Commit": "9f3c1a...",
"Author": "dev@example.com",
"RuleID": "aws-access-token"
}
Vì sao quét cả lịch sử commit: xóa key trong commit mới nhưng vẫn còn trong lịch sử git => kẻ tấn công git log -p lấy được. Phải vừa revoke key (vô hiệu hóa ngay) vừa làm sạch lịch sử (hoặc coi như đã lộ vĩnh viễn).
GitHub Secret Scanning + push protection chặn ngay khi push; AWS có cơ chế tự gắn policy AWSCompromisedKeyQuarantine khi phát hiện key public.
13.15.3. Privilege Escalation qua iam:PassRole
Đây là một trong những đường leo thang quyền nguy hiểm và phổ biến nhất.
Cơ chế: iam:PassRole cho phép một principal "trao" một role cho một dịch vụ (EC2, Lambda...). Nếu kẻ tấn công có:
- iam:PassRole cho một role quyền cao (vd AdminRole), VÀ
- quyền tạo tài nguyên gắn role đó (vd ec2:RunInstances hoặc lambda:CreateFunction),
thì họ leo thang lên Admin dù bản thân tài khoản của họ không phải Admin.
Bước khai thác cụ thể (qua Lambda):
# 1. Tạo Lambda chạy dưới AdminRole (cần iam:PassRole + lambda:CreateFunction)
aws lambda create-function --function-name pwn \
--runtime python3.12 --handler index.handler \
--role arn:aws:iam::123456789012:role/AdminRole \
--zip-file fileb://payload.zip
# 2. Lambda code gọi AWS API với quyền AdminRole, vd tạo user admin mới
aws lambda invoke --function-name pwn out.json
Hoặc qua EC2 + IMDS:
# Khởi tạo EC2 gắn AdminRole, rồi đọc credential từ IMDS bên trong
aws ec2 run-instances --image-id ami-xxx --instance-type t3.micro \
--iam-instance-profile Name=AdminInstanceProfile
# Sau đó SSH/SSM vào, curl IMDS lấy credential AdminRole.
Phòng thủ:
- Hạn chế iam:PassRole chỉ tới role cụ thể qua Resource (không để "Resource":"*").
- Dùng condition iam:PassedToService để giới hạn role chỉ trao cho dịch vụ đúng.
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::123456789012:role/app-limited-role",
"Condition": { "StringEquals": { "iam:PassedToService": "lambda.amazonaws.com" } }
}
Phát hiện: trong CloudTrail tìm RunInstances/CreateFunction có requestParameters chứa role ARN quyền cao, được gọi bởi principal không phải admin. Công cụ Pacu (framework khai thác AWS) có module liệt kê đường leo thang:
pacu
> run iam__enum_permissions
> run iam__privesc_scan
13.15.4. Exposed bucket — phát hiện và khai thác
# Truy cập ẩn danh thử liệt kê
aws s3 ls s3://target-bucket --no-sign-request
aws s3 cp s3://target-bucket/secret.txt . --no-sign-request
--no-sign-request = không gửi credential => kiểm tra liệu bucket có cho phép truy cập ẩn danh (public). Phát hiện phía phòng thủ: bật BPA toàn tài khoản, Macie quét PII, Config rule s3-bucket-public-read-prohibited.
13.16. CSPM — Cloud Security Posture Management
13.16.1. Là gì
CSPM tự động quét cấu hình cloud so với benchmark (CIS, PCI) và phát hiện sai lệch liên tục. Native: AWS Security Hub + Config, GCP SCC. Open source: Prowler, ScoutSuite.
13.16.2. Prowler — ví dụ thực tế
# Quét toàn bộ checks theo CIS benchmark, xuất HTML + JSON
prowler aws --compliance cis_2.0_aws --output-formats html json-ocsf
Output mẫu (rút gọn):
FAIL s3_bucket_public_access company-reports Bucket allows public read
FAIL iam_root_mfa_enabled account 1234 Root account MFA not enabled
PASS cloudtrail_multi_region account 1234 Multi-region trail enabled
13.16.3. ScoutSuite
scout aws --report-dir ./scout-report
# Mở ./scout-report/scoutsuite-results/...html xem dashboard rủi ro theo dịch vụ
13.16.4. AWS Config rule (native CSPM)
# Bật managed rule kiểm tra bucket không được public
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "s3-no-public-read",
"Source": { "Owner": "AWS", "SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED" }
}'
Config liên tục đánh giá tài nguyên khi thay đổi và gắn cờ NON_COMPLIANT, có thể tự động remediation qua SSM Automation.
13.17. Secret Manager
13.17.1. AWS Secrets Manager
Lưu bí mật (DB password, API key) mã hóa bằng KMS, hỗ trợ rotation (xoay key) tự động qua Lambda rotation function.
aws secretsmanager create-secret --name prod/db/password \
--secret-string '{"username":"app","password":"S3cr3t!"}' \
--kms-key-id alias/app-cmk
aws secretsmanager get-secret-value --secret-id prod/db/password \
--query SecretString --output text
So với Parameter Store: Secrets Manager có rotation tích hợp + tính phí/bí mật; SSM Parameter Store (SecureString) rẻ hơn, không tự rotate.
13.17.2. GCP Secret Manager
Phiên bản (version) bất biến; truy cập kiểm soát qua IAM roles/secretmanager.secretAccessor.
echo -n "S3cr3t!" | gcloud secrets create db-pass --data-file=-
gcloud secrets versions access latest --secret=db-pass
Cảnh báo (bảo mật chung): (1) không bao giờ truyền secret qua biến môi trường hiện trong log/ps; ưu tiên fetch lúc runtime; (2) cấp quyền đọc secret theo least privilege và bật audit log mỗi lần access; (3) bật rotation định kỳ.
13.18. Tổng kết các nguyên tắc phòng thủ cốt lõi
| Nguyên tắc | Áp dụng cụ thể |
|---|---|
| Least privilege | IAM/role hạt mịn, Resource/compartment cụ thể, bỏ * |
| Explicit deny guardrail | SCP / Org Policy / permission boundary (OCI không có Deny — siết bằng phạm vi compartment) |
| Loại bỏ credential lâu dài | IAM Role/SA token/Instance Principal thay access key; WIF; IMDSv2 |
| Mã hóa mặc định | SSE-KMS/CMEK/OCI Vault key + chặn non-TLS |
| Khả năng quan sát | CloudTrail + Config + GuardDuty / Audit Logs + SCC / OCI Audit + Cloud Guard, log bất biến |
| Chặn public mặc định | S3 BPA / Uniform bucket-level access / OCI bucket private sẵn |
| Phát hiện liên tục | CSPM (Prowler/ScoutSuite/SCC/Cloud Guard) + secret scanning trong CI |
| Ranh giới môi trường | Tách theo prod vs non-prod (không theo nhà cung cấp); prod trên cloud "dev" siết ngang prod chính |
Toàn bộ kiến trúc bảo mật đám mây quy về: kiểm soát danh tính (IAM) chặt, loại bỏ bí mật lâu dài, mã hóa mọi nơi, ghi log đầy đủ và bất biến, chặn public mặc định, và quét cấu hình sai liên tục. Phần lớn sự cố thực tế nằm ở "IN the cloud" — tức trong tầm kiểm soát và trách nhiệm của bạn.
Ghi chú của mình
Khu vực ghi chú cá nhân: những điểm từng hiểu sai, phần còn đang tìm hiểu, hoặc kinh nghiệm rút ra khi thực hành — cập nhật dần.
Từ AWS sang OCI — mấy chỗ mình bị hụt tư duy. Mình học cloud từ AWS trước, nên khi phải vận hành thêm OCI (dev và một phần prod) thì bị "vấp" đúng ở những khái niệm tưởng giống mà không giống:
- Compartment không phải account. Ban đầu mình cứ tìm "tạo account con" như Organizations của AWS, mãi mới nhận ra OCI cô lập bằng compartment trong cùng một tenancy. Hệ quả bảo mật: ranh giới yếu hơn tách account, nên một policy viết ở cấp tenancy có thể rò quyền xuống mọi compartment. Từ đó mình cẩn thận đặt phạm vi
in compartment <cụ thể>chứ khôngin tenancycho tiện. - Policy dạng câu, và KHÔNG có Deny. Cú pháp
Allow group … to manage … in compartment …đọc rất dễ chịu, nhưng mình mất một lúc mới thấm là không cóDenytường minh như SCP. Thói quen dựng guardrail bằng explicit Deny của AWS không bê thẳng sang được — phải nghĩ lại theo hướng "chỉ cấp đúng phạm vi", hoặc dùng Network Sources/tag. Đây là chỗ mình vẫn đang tìm hiểu tiếp: làm guardrail cấp tenancy trên OCI cho gọn thì dùng gì cho tương đương SCP. - Hai lớp tường lửa Security List + NSG. Một lần đổi rule trên NSG mà gói vẫn không đi, mất công debug mới nhớ ra Security List ở subnet vẫn đang chặn — cả hai phải cùng cho phép (AND). Và Security List stateful mặc định (khác NACL của AWS là stateless), nên đừng máy móc mở dải ephemeral port như thói quen NACL. Giờ mình theo nguyên tắc: phân quyền mạng bằng NSG (tham chiếu NSG-to-NSG), giữ Security List tối giản.
- Bucket private mặc định là điểm cộng thật. Sau ám ảnh "S3 public-read" kinh điển, việc Object Storage của OCI mặc định private làm mình nhẹ đầu hơn — nhưng vẫn bật Cloud Guard để nó gắn cờ nếu ai đó lỡ đổi sang public. Chia sẻ file thì phát Pre-Authenticated Request có hạn, không bao giờ để bucket public "cho tiện".
- Metadata
Bearer Oraclekhông phải bí mật. Lúc đầu mình tưởng headerAuthorization: Bearer Oraclelà một dạng token nên yên tâm. Không phải — nó là hằng số cố định, chỉ chặn được SSRF ngây thơ (loại không thêm được header). SSRF điều khiển được header vẫn qua. Nên mình vẫn siết đủ bộ SSRF defense ở tầng app, coi metadata v2 (cả AWS lẫn OCI) chỉ là một lớp phụ.
Bài học vận hành đa đám mây lớn nhất: ranh giới bảo mật đúng là prod vs non-prod, không phải AWS vs OCI. Prod chạy trên OCI mình bắt áp đúng chuẩn siết như prod AWS — cái bẫy là để compartment prod "thừa hưởng" thói quen lỏng của dev vì ở chung tenancy. Việc prod-AWS và dev-OCI tách cả nhà cung cấp lẫn credential là một lợi thế blast-radius tình cờ, nhưng mình không dựa vào đó thay cho kỷ luật cấu hình.
Đang tìm hiểu tiếp: chuẩn hóa cả hai đám mây bằng Terraform + policy-as-code (Checkov/Trivy quét cả AWS lẫn OCI) để bớt phải nhớ hai lối cấu hình tay; và cách gom log/alert của Cloud Guard (OCI) với GuardDuty/Config (AWS) về cùng một chỗ để một người vẫn theo dõi xuể.