Bảo mật 02/09/2026 9 phút

Không có PKCE, chỉ cần chặn được một mã là kẻ tấn công đổi được token của bạn — tôi đo đúng lỗ hổng đó trong 'Đăng nhập bằng Google'

Đo luồng authorization code của OAuth2: mã dùng một lần, redirect_uri khớp chính xác, và PKCE. Ba dòng cuối bảng đối xứng sáng tỏ — có PKCE thì chặn được mã vẫn vô dụng, không PKCE thì chặn được mã là chiếm được tài khoản. Vì sao authorization code + PKCE là luồng khuyến nghị cho MỌI ứng dụng.

Bảo mật 02/09/2026 9 phút

Tôi viết SELECT * FROM don_hang quên hẳn lọc quyền — và cơ sở dữ liệu vẫn chỉ trả về đúng đơn của người dùng đó, nhờ 3 câu SQL

IDOR là một câu if bị quên ở tầng ứng dụng, và bạn phải nhớ viết nó ở MỌI endpoint, mãi mãi. Row-Level Security của PostgreSQL đẩy phép kiểm quyền xuống chính bảng — nên cả một câu SELECT trần trụi không điều kiện cũng chỉ trả về dữ liệu người dùng được xem. Từ 'nhớ kiểm ở mọi nơi' thành 'định nghĩa một nơi, không thể bỏ qua'.

Bảo mật 02/09/2026 9 phút

Một tệp vừa là ảnh PNG thật (Pillow đọc được) vừa là mã PHP chạy được — và nó trả về uid=33 trên máy chủ của tôi

Cho tải tệp lên là một trong những tính năng nguy hiểm nhất. Tôi dựng một tệp 118 byte vừa mở ra thành PNG 1×1 hợp lệ, vừa thực thi lệnh trên máy chủ khi bị phục vụ nhầm. Bài học phản trực giác: 'kiểm xem có phải ảnh không' vô dụng, vì nó THẬT SỰ là ảnh.

Bảo mật 02/09/2026 10 phút

Chức năng 'tải ảnh từ URL' vô hại của tôi đọc trộm khoá cloud của chính máy chủ — đây là cơ chế đằng sau vụ Capital One 100 triệu hồ sơ

SSRF biến máy chủ của bạn thành cầu nối vào mạng nội bộ mà internet không chạm tới. Tôi dựng một tính năng tải ảnh từ URL và dùng nó đọc endpoint metadata cloud, rò ra khoá bí mật. Bài học: danh sách chặn thua danh sách cho phép — và lý do sâu hơn 'liệt kê cái xấu là vô tận'.

Bảo mật 02/09/2026 9 phút

Một dòng pickle.loads() và lệnh của kẻ tấn công chạy với quyền root — mà không có bước 'thực thi' nào cả; JSON cùng dữ liệu thì trơ như đá

Với pickle, 'đọc dữ liệu' và 'chạy mã' là cùng một hành động: tôi dựng payload thật, và chỉ tải nó lên đã cho ra uid=0(root). JSON cùng chuỗi ấy chỉ trả về một dict. Khác biệt không nằm ở cách dùng mà ở bản chất định dạng — cái này lưu dữ liệu, cái kia lưu cả cách gọi hàm.

Bảo mật 02/09/2026 9 phút

So sánh token dừng sớm rò 9 nano giây cho mỗi byte đoán đúng — đủ để dò cả bí mật, byte một, mà phản hồi chẳng nói gì

Một timing side channel: cách bạn so sánh token với bí mật để lộ token đúng tới đâu — không qua nội dung, mà qua thời gian. Byte-by-byte dừng sớm rò tuyến tính ~9 ns/byte; compare_digest giữ 666 ns bất biến. Và một bất ngờ: == của Python lại KHÔNG lộ, vì một lý do đáng nhớ.