+2

Tấn Công Embedding Trong Hệ Thống RAG - Phần 4: "Cẩm Nang Toàn Diện - Đảo Ngược Có Huấn Luyện, Chọn Vũ Khí Và Phòng Thủ Cho Hệ Thống RAG"

Mở đầu: Khi zero-shot không đủ mạnh

Ở phần trước, chúng ta đã thử hai cách đảo ngược embedding mà không cần huấn luyện gì cả. Cách đầu (ngân hàng mẫu) nhanh, nhẹ, nhưng phụ thuộc nhiều vào việc bạn có câu mẫu phù hợp. Cách sau (beam search) mạnh hơn, nhưng cần GPU và thời gian.

Cả hai đều có một yêu cầu ngầm mà tôi chưa nhấn mạnh đủ: bạn phải biết mô hình embedding của nạn nhân. Không biết — tức là bạn không có gì để so sánh, không có gì để tối ưu hóa. Beam search sẽ chạy vào tường.

Nhưng trong thực tế, bạn hiếm khi biết chính xác mô hình nào đang chạy. Và ngay cả khi biết, zero-shot vẫn có giới hạn về độ chính xác.

Phần này đi vào hai phương pháp nặng hơn — có huấn luyện. Một cái giải quyết bài toán "không biết mô hình", một cái giải quyết bài toán "cần chính xác tuyệt đối". Và sau đó là phần quan trọng nhất: khi nào dùng cái nào, và làm sao phòng thủ.


PHẦN 1: ALGEN — Khi bạn không biết mô hình của nạn nhân

1. Vấn đề các phương pháp trước đây không ai nói thẳng

Vec2Text — phương pháp supervised mạnh nhất hiện tại — cần từ 1 đến 5 triệu cặp văn bản-embedding để huấn luyện. Các phương pháp khác thì ít hơn: 100.000, hay tối thiểu 8.000 mẫu.

Nghe có vẻ ổn cho nghiên cứu. Nhưng trong tấn công thực tế, bạn may mắn lắm mới có được vài trăm cặp rò rỉ. Nhiều khi còn không có cái đó.

ALGEN (Few-shot Textual Embedding Inversion Attack, ACL 2025) giải quyết vấn đề này theo một hướng hoàn toàn khác: thay vì huấn luyện một bộ giải mã riêng cho mô hình của nạn nhân, nó căn chỉnh không gian embedding của nạn nhân về không gian embedding của bạn — rồi dùng bộ giải mã bạn đã huấn luyện sẵn.

Tức là bạn không cần hiểu ổ khóa của nạn nhân. Bạn chỉ cần tìm ra cách vặn chìa sao cho ổ kia mở theo hướng quen thuộc với bạn.

2. Ba bước của ALGEN

Bước 1 — Huấn luyện bộ giải mã trên không gian của bạn

Trước khi biết gì về nạn nhân, bạn lấy dữ liệu công khai và huấn luyện một bộ giải mã FlanT5-small. Mục tiêu đơn giản: bộ giải mã này biết cách biến một embedding thành văn bản — nhưng chỉ trong không gian embedding mà bạn kiểm soát.

Mất khoảng 2 giờ trên một GPU. Không có gì đặc biệt ở bước này.

Bước 2 — Tìm phép căn chỉnh giữa hai không gian embedding

Đây là phần thú vị. Bạn có một số cặp (văn bản, embedding_nạn_nhân) — dù chỉ là vài trăm cặp rò rỉ từ API. Với cùng những văn bản đó, bạn cũng tính embedding bằng mô hình của mình.

Sau đó bạn giải bài toán: tìm ma trận W sao cho embedding_nạn_nhân × W ≈ embedding_của_bạn.

Nghe có vẻ phức tạp, nhưng bản chất là một bài toán hồi quy tuyến tính. Điều ngạc nhiên là nó hoạt động rất tốt dù không gian hai mô hình hoàn toàn khác nhau — vì các mô hình embedding đều học để encode nghĩa ngữ nghĩa, nên cấu trúc ẩn của chúng có nhiều điểm tương đồng.

Cần bao nhiêu dữ liệu? Chỉ 1 cặp đã đủ để có tấn công một phần. Với 1.000 cặp, hiệu suất bão hòa và không cải thiện thêm nữa.

Bước 3 — Tấn công

Giờ bạn có hai thứ trong tay: bộ giải mã của mình và ma trận căn chỉnh W. Khi gặp embedding của nạn nhân:

embedding_nạn_nhân × W → embedding_của_bạn → bộ_giải_mã → văn_bản_gốc

Kết quả thực tế trên các mô hình khác nhau:

Mô hình embedding Rouge-L Cosine Similarity
T5 45.75% 0.9464
GTR 38.27% 0.8879
mT5 43.35% 0.9370
OpenAI ada-2 41.45% 0.9312

1.000 mẫu căn chỉnh. Không biết mô hình nạn nhân trước đó.

3. Canary Injection — khi bạn không có dữ liệu rò rỉ nào

Câu hỏi tự nhiên: nếu không có bất kỳ cặp dữ liệu nào từ nạn nhân thì sao?

ALGEN có một giải pháp khá táo bạo: bạn tự tạo ra dữ liệu căn chỉnh.

Cụ thể, nếu bạn có quyền ghi vào vector database của nạn nhân (dù chỉ là quyền hạn chế), bạn chèn vào một lượng lớn văn bản "mồi" — những câu vô hại, đa dạng chủ đề. Hệ thống nạn nhân tự động tính embedding cho chúng. Bạn export ra. Thế là bạn có ngay các cặp (văn_bản_của_bạn, embedding_nạn_nhân).

Bạn đã biết văn bản gốc (vì bạn tự viết). Bạn embed chúng bằng mô hình của mình. Bạn có cả hai vế của phép căn chỉnh.

Đây là canary injection. Trong bảo mật truyền thống, canary thường được dùng để phát hiện rò rỉ. Ở đây nó bị dùng ngược lại — để khởi động cuộc tấn công.

4. Kết hợp với RAG Probing

ALGEN không dừng ở đây. Sau khi có văn bản gần đúng từ bộ giải mã, bạn có thể dùng nó làm đầu vào cho RAG Probing (đã nói ở Phần 2): trích từ khóa, truy vấn RAG, xem phản hồi có tiết lộ thêm không. Rồi dùng Membership Inference để điền vào những chỗ bị che.

Tức là ALGEN là bước khởi đầu — không phải kết thúc.

5. Thực hành — ALGEN với canary

Bước 1: Thiết lập môi trường và tạo dữ liệu huấn luyện.

image.png

Bước 2: Repo ALGEN (link) tùy chỉnh đã có sẵn trong thư mục home của user attacker. Giờ cài các dependencies:

image.png

Bước 3: Tạo 50.000 canary texts đa dạng:

image.png

Bước 4: Chèn canary vào vector database của nạn nhân:

image.png

Bước 5: Huấn luyện bộ giải mã FlanT5-small (~2 giờ). Quá trình dùng max_length=64 và batch_size=32:

image.png

Bước 6: Chạy tấn công với căn chỉnh canary:

image.png

image.png

Kết quả mong đợi: Bộ giải mã tái tạo văn bản gần đúng — có thể sai chủ đề, nhưng đủ từ khóa để làm đầu vào cho bước RAG probing tiếp theo.


PHẦN 2: Vec2Text — Khi bạn cần chính xác đến từng chữ

1. Tại sao lại cần thứ này?

ALGEN cho Rouge-L khoảng 40-45%. Không tệ, nhưng cũng không đủ nếu bạn cần khôi phục chính xác một mật khẩu, một chuỗi token API, hay một đoạn văn không thể đoán mò.

Vec2Text (Morris et al., 2023) là phương pháp đạt độ chính xác cao nhất hiện tại: cosine similarity trên 93%, và exact match lên đến 28% — tức là khoảng 1 trong 4 đoạn văn ngắn được khôi phục nguyên văn.

Cái giá: 60 giờ huấn luyện trên GPU.

Nếu bạn cần tấn công một mục tiêu một lần rồi thôi, 60 giờ là quá đắt. Nhưng nếu bạn đã có mô hình huấn luyện sẵn, mỗi chunk sau đó chỉ tốn 15 phút. Với các mục tiêu lớn hoặc pentest dài hạn, đây là sự đánh đổi hợp lý.

2. Kiến trúc hai giai đoạn

Vec2Text không phải một mô hình mà là hai, phối hợp với nhau.

Inverter nhận embedding và tạo ra một giả thuyết văn bản đầu tiên. Giả thuyết này thường đúng ý nhưng sai từ, hoặc gần đúng nhưng thiếu chi tiết quan trọng.

Corrector nhận giả thuyết đó cùng với embedding của nó, so với embedding mục tiêu, tính phần dư giữa hai embedding, rồi tạo ra phiên bản chỉnh sửa. Sau đó lặp lại — 20 đến 50 vòng — mỗi vòng giả thuyết tiệm cận văn bản gốc thêm một chút.

Inverter phác thảo nhanh, Corrector là người thợ kiên nhẫn ngồi so từng chi tiết với bản gốc rồi chỉnh lại.

Điểm đặc biệt quan trọng với API đóng như OpenAI: Vec2Text không cần trọng số mô hình embedding. Nó chỉ cần gọi API để lấy embedding của các giả thuyết trung gian. Tức là kể cả khi nạn nhân dùng text-embedding-ada-002, bạn vẫn có thể tấn công — miễn là bạn có quyền gọi cùng API đó.

3. Thực hành — Vec2Text

Lưu ý: Toàn bộ quá trình huấn luyện mất khoảng 60 giờ trên Tesla T4.

Bước 1: Tạo hybrid corpus (80% dữ liệu doanh nghiệp, 20% Wikipedia — để Inverter quen với văn phong thực tế):

image.png

Bước 2: Huấn luyện Inverter (~50 giờ). Kiến trúc: MLP chiếu embedding 384 chiều thành 32 pseudo-token (768 chiều mỗi token), T5-base nhận qua cross-attention rồi decode tự hồi quy:

image.png

Sau 20 epoch, cosine similarity đạt 0.92, BLEU 59.4, ROUGE-L 0.78, exact match 0.28. Khoảng 1 trong 4 mẫu validation được tái tạo nguyên văn.

Bước 3: Huấn luyện Corrector (~10 giờ). Trước khi huấn luyện, nó tạo trước các giả thuyết beam search từ Inverter cho toàn bộ tập huấn luyện, sau đó học cách cải thiện chúng bằng phần dư embedding:

image.png

correction_gain — mức cải thiện cosine so với Inverter — nhỏ nhưng dương. Hợp lý vì Inverter đã đạt 0.92; margin còn lại ít, nhưng cái phần ít đó thường là phần quan trọng nhất. Tổng thời gian cả hai giai đoạn khoảng 60 giờ trên một GPU T4.

Bước 4: Chạy pipeline tấn công hoàn chỉnh (tái tạo + khớp mẫu + điền vị trí), ~15 phút mỗi chunk:

image.png

Inverter khôi phục ngữ cảnh cấu trúc với độ tương đồng 93.6%, sau đó pipeline dùng phần văn bản đó để chọn template phù hợp nhất:

image.png

Từ hơn 200 mẫu trên tất cả danh mục, nó thu hẹp xuống 20 mẫu khớp nhất. Với template đã xác định, công cụ điền từng placeholder theo chiến lược "fill-and-lock": điền URL và username (entropy thấp) trước, khóa lại, rồi dùng context đó để giải mật khẩu:

image.png

Chiến lược điền và khóa lũy tiến này cải thiện z-score mật khẩu từ 5.44 lên 6.50 — khi URL và username đã khóa, tín hiệu của mật khẩu trong embedding trở nên rõ hơn nhiều.

Kết quả mong đợi:

[Recon] sim=0.9360 "Please navigate to https://login.megacorpone.ai and click on"
...
[Slot: PASSWORD] Winner: N0=Acc3ss (confidence=HIGH, z=6.50)

PHẦN 3: Cẩm nang chọn vũ khí

1. Bảng so sánh 4 phương pháp

Tiêu chí emb_fin.py zero2text_impl.py ALGEN Vec2Text
Cách làm So với 500K mẫu có sẵn Beam search xây lại token-by-token Căn chỉnh không gian, giải mã bằng FlanT5 Inverter + Corrector, lặp 20-50 vòng
Cần biết mô hình? ✅ Cần ✅ Cần ❌ Không cần ✅ Cần
Thời gian chuẩn bị 0 giờ 0 giờ ~2 giờ ~60 giờ
Thời gian/chunk 2-5 phút 10-60 phút 5-10 phút ~15 phút
Cần GPU? ❌ Không ✅ Có ✅ Có ✅ Có
Cần dữ liệu rò rỉ? ❌ Không ❌ Không ✅ Vài trăm cặp (hoặc canary) ✅ Hàng triệu cặp
Độ chính xác 60-92% 85-96% 40-75% 85-96%
Exact match Thấp Trung bình Trung bình Cao (~28%)
Tấn công hàng loạt? ✅ Có ❌ Chậm ✅ Có ✅ Có
Phù hợp nhất Biết mô hình, cần kết quả nhanh Cách 1 thất bại, có GPU Không biết mô hình, có thể chèn canary Có thời gian chuẩn bị, cần độ chính xác cao

2. Đường dẫn quyết định

┌─────────────────────────────────────────────────────────────────┐
│                     BẮT ĐẦU                                    │
│  Bạn có embedding của nạn nhân. Bạn muốn phục hồi mật khẩu.    │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
              ┌───────────────────────────────┐
              │ Bạn có biết mô hình embedding │
              │ của nạn nhân không?            │
              └───────────────────────────────┘
                     │                    │
                    Có                    Không
                     │                    │
                     ▼                    ▼
    ┌─────────────────────────┐  ┌──────────────────────────┐
    │ ✅ Chạy emb_fin.py      │  │ ✅ Dùng ALGEN            │
    │ (Cách 1 - Ngân hàng mẫu)│  │ - Chèn canary vào DB     │
    └─────────────────────────┘  │ - Huấn luyện decoder     │
              │                  │ - Căn chỉnh & tấn công   │
              ▼                  └──────────────────────────┘
    ┌─────────────────────────┐              │
    │ Kết quả có tốt không?   │              │
    │ - Mẫu > 65%?            │              │
    │ - Z-score > 3.0?        │              │
    │ - Tin cậy CAO?          │              │
    └─────────────────────────┘              │
         │              │                   │
        Có              Không               │
         │              │                   │
         ▼              ▼                   ▼
    ┌──────────┐  ┌────────────────────┐  ┌─────────────────────┐
    │ ✅ DỪNG  │  │ Chạy zero2text     │  │ ALGEN cho kết quả   │
    │ Kết quả  │  │ (Cách 2 - Beam)    │  │ chưa đủ chính xác?  │
    │ đủ tốt!  │  └────────────────────┘  └─────────────────────┘
    └──────────┘           │                          │
                           ▼                          ▼
                    ┌────────────────────┐  ┌─────────────────────────┐
                    │ Kết quả từ Cách 2  │  │ ✅ Dùng Vec2Text        │
                    │ đã tốt chưa?       │  │ (Chỉ khi có 60 giờ)     │
                    └────────────────────┘  └─────────────────────────┘
                         │         │
                        Có        Không
                         │         │
                         ▼         ▼
                    ┌──────────┐ ┌──────────────────────────┐
                    │ ✅ DỪNG  │ │ Cân nhắc:                │
                    │ Kết quả  │ │ - Bạn có 60 giờ không?  │
                    │ đủ tốt!  │ │ - Cần tấn công nhiều?   │
                    └──────────┘ │ - Ngân sách cho phép?   │
                                 └──────────────────────────┘

3. Tình huống thực tế

Tình huống 1: Biết mô hình, cần kết quả nhanh

Bạn đã xác định mô hình là all-MiniLM-L6-v2.
Không có GPU. Cần kết quả trong 10 phút.

→ emb_fin.py: chạy ngay, không cần huấn luyện, kết quả sau 2-5 phút.

Tình huống 2: Cách 1 cho kết quả yếu

emb_fin.py trả về độ tương tự 52%, z-score 1.2, confidence WEAK.
Có GPU Tesla T4.

→ zero2text_impl.py: beam search, không cần mẫu có sẵn, có thể đạt 85-96%.

Tình huống 3: Không biết mô hình embedding

Export được embeddings nhưng không biết model nào tạo ra.
Có quyền ghi vào vector database.

→ ALGEN với canary injection: không cần biết mô hình, tự tạo dữ liệu căn chỉnh.

Tình huống 4: Cần độ chính xác tối đa

Mục tiêu quan trọng, có một tuần chuẩn bị.
Cần phục hồi chính xác 100+ chunk. Có GPU và ngân sách.

→ Vec2Text: đầu tư 60 giờ một lần, mỗi chunk sau đó chỉ mất 15 phút.

PHẦN 4: Phòng thủ — Bảo vệ hệ thống RAG của bạn

1. Điểm yếu của kẻ tấn công — hãy khai thác chúng

Độ dài văn bản là rào cản tự nhiên. Tất cả các phương pháp đều suy giảm rõ rệt khi văn bản dài hơn 64 token.

Độ dài Khả năng đảo ngược
8-16 token Rất cao (mật khẩu, khóa API)
32-64 token Cao (câu ngắn, đoạn nhỏ)
128-256 token Thấp (đoạn văn dài)
>256 token Gần như không khả thi

Mật khẩu ngẫu nhiên cao entropy bảo vệ tốt hơn mật khẩu có nghĩa. N0=Acc3ss — mô hình đoán được vì nó có cấu trúc ngữ nghĩa. 1qaz2wsx3edc4rfv — mô hình xem như nhiễu. Điều này không có nghĩa là bạn nên lưu mật khẩu trong RAG, nhưng nếu buộc phải làm vậy, entropy cao giúp ích.

Loại mật khẩu Khả năng phục hồi
N0=Acc3ss (có nghĩa) Cao
Spring2026! (có cấu trúc) Trung bình
1qaz2wsx3edc4rfv (ngẫu nhiên) Thấp

Lượng tử hóa làm đảo ngược khó hơn đáng kể. Product Quantization, Scalar Quantization — các kỹ thuật này được thiết kế để tiết kiệm bộ nhớ, nhưng cũng làm mất thông tin theo cách có lợi cho phòng thủ. Đây là lý do tốt để bật nén trong vector store ngay cả khi bạn không thiếu RAM.

Kỹ thuật nén Ảnh hưởng đến đảo ngược
Không nén (full dim) Tốt nhất cho kẻ tấn công
PCA (giảm chiều) Giảm độ chính xác 20-30%
Product Quantization (PQ) Giảm đáng kể

2. Chiến lược phòng thủ thực tế

Kiểm soát truy cập vector store là số một. Không để Weaviate hay Qdrant public không xác thực. Phân quyền rõ ràng giữa read/write/admin. Nếu kẻ tấn công không lấy được embedding, họ không có gì để đảo ngược.

Giám sát batch insert bất thường. Canary injection cần chèn vào database một lượng lớn văn bản từ bên ngoài — đây là tín hiệu bất thường nếu bạn có logging đủ tốt: IP lạ insert hàng nghìn document trong thời gian ngắn, nội dung không liên quan đến domain của bạn.

Rate limiting API embedding. Nhiều phương pháp zero-shot cần gọi API rất nhiều lần để quét. Rate limiting không ngăn hoàn toàn nhưng làm chậm và tăng chi phí cho kẻ tấn công.

Mã hóa embedding khi lưu trữ. Nếu kẻ tấn công có thể đọc trực tiếp file lưu trữ của vector database, bạn đã thua trước khi bắt đầu. Encryption at rest là baseline, không phải tùy chọn.

3. Bảng tóm tắt

Mối đe dọa Phương pháp tấn công Phòng thủ
Export được embedding Tất cả các phương pháp Kiểm soát truy cập vector store
Không biết mô hình ALGEN (canary injection) Giám sát batch insert bất thường
Tấn công API hàng loạt emb_fin.py, Vec2Text Rate limiting
Cần độ chính xác cao Vec2Text (60h huấn luyện) Mã hóa embedding + lượng tử hóa
Văn bản ngắn, entropy thấp Tất cả các phương pháp Chunk size lớn hơn, tránh lưu dữ liệu nhạy cảm

4. Đừng ảo tưởng

Có một quan niệm sai lầm phổ biến: "embedding chỉ là vector số, không ai giải mã được." Loạt bài này là bằng chứng ngược lại. ALGEN, Vec2Text, và ngay cả các phương pháp zero-shot — tất cả đều cho thấy embedding mang đủ thông tin để khôi phục văn bản gốc với độ chính xác đáng lo ngại.

Niềm tin sai lầm Thực tế
"Embedding chỉ là số, không ai hiểu được" Sai — có thể đảo ngược
"Lấy được embedding cũng chẳng làm gì được" Sai — ALGEN & Vec2Text chứng minh điều ngược lại
"API của tôi chỉ trả về embedding đã mã hóa" Không đủ — mã hóa cần đi kèm kiểm soát truy cập

Nếu bạn đang xây dựng hệ thống RAG xử lý dữ liệu nhạy cảm, hãy xem vector database như một cơ sở dữ liệu thông thường — cần xác thực, cần mã hóa, cần audit log. Đừng tạm an tâm vì "nó chỉ lưu số."


📌 Link code tổng hợp

Script Mô tả
export_embeddings.py Xuất khẩu embeddings từ Weaviate
inference_probing.py Xác định mô hình embedding
chunk_triage_pipe.py Phân loại 3 tầng
generate_templates.py Tạo ngân hàng mẫu
emb_fin.py Zero-Shot với ngân hàng mẫu
zero2text_impl.py Zero-Shot với beam search
ALGEN/ Few-Shot với ALGEN
Vec2Text/ Supervised với Vec2Text

"Biết người biết ta, trăm trận trăm thắng." — Tôn Tử

Cảm ơn bạn đã theo dõi toàn bộ series!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí