Mở đầu — vì sao bài này quan trọng
Khi bạn kiểm thử API bằng Postman, phần lớn thời gian bạn làm việc với header Authorization chứa token (Bearer JWT, API Key). Nhưng có một mảng khổng lồ ứng dụng web tại Việt Nam — đặc biệt là hệ thống nội bộ ngân hàng, cổng thương mại điện tử đời cũ, các trang quản trị viết bằng PHP/Laravel, Java Spring, hay ASP.NET — vẫn dùng cookie-based session để xác thực. Với những hệ thống này, nếu bạn không hiểu cách Postman lưu và gửi cookie, bạn sẽ gặp cảnh "chạy request đăng nhập thành công, nhưng request tiếp theo lại trả 401" mà không hiểu vì sao.
Ba khái niệm cookie, session và CSRF gắn chặt với nhau. Cookie là phương tiện vận chuyển; session là trạng thái đăng nhập lưu ở phía server và được móc nối qua cookie; còn CSRF (Cross-Site Request Forgery) là lỗ hổng phát sinh chính vì trình duyệt tự động gửi cookie. Là một QA, bạn không chỉ cần biết cách "làm cho request chạy được" mà còn phải biết cách kiểm thử các cơ chế bảo mật này: server có set HttpOnly không? Có yêu cầu CSRF token không? Session có hết hạn đúng lúc không? Đây là những lỗ hổng thật, và bạn là người phát hiện chúng trước khi kẻ tấn công làm điều đó.
Bài này tập trung vào cách Postman quản lý cookie (cookie jar), cách bạn kiểm thử luồng session đăng nhập/đăng xuất, và cách viết test cho CSRF protection. Đây là mảnh ghép bổ sung cho bài Authorization (OAuth/JWT) mà bạn đã học — không trùng lặp, mà đi vào đúng phần cookie/session mà token-based bỏ qua.
Khái niệm cốt lõi
Cookie jar trong Postman
Không giống như một curl thô nơi bạn phải tự truyền header Cookie cho từng lệnh, Postman có một cookie jar — một kho lưu trữ cookie tự động, hoạt động giống hệt trình duyệt. Bạn truy cập nó qua Settings → Cookies → Manage Cookies, hoặc nút "Cookies" ngay dưới nút Send trong tab request.
Ba đặc điểm bạn phải nắm chắc:
- Mỗi domain có một jar riêng. Cookie của
api.shopee.vnkhông lẫn với cookie củastaging.shopee.vn. Postman lưu trữ theo domain và path, đúng theo chuẩn RFC 6265. - Postman tự động gửi cookie khi domain khớp. Khi server trả về header
Set-Cookie: JSESSIONID=abc123, Postman lưu vào jar của domain đó. Ở request kế tiếp cùng domain, Postman tự đính kèmCookie: JSESSIONID=abc123mà bạn không cần làm gì. Đây chính là lý do luồng đăng nhập rồi gọi API bảo mật "tự chạy được". - Cookie jar là stateful giữa các request. Điều này vừa tiện vừa nguy hiểm: nếu một test trước để lại cookie cũ, test sau có thể "vô tình đăng nhập" bằng session không mong muốn. Đây là nguồn gốc của nhiều bug flaky.
Session hoạt động thế nào
Session là trạng thái phía server. Khi bạn POST tới /login với đúng username/password, server tạo một bản ghi session (lưu trong Redis, database, hoặc bộ nhớ) và trả về một session ID qua Set-Cookie. Session ID này giống như một chiếc vé giữ xe — bản thân nó vô nghĩa, nhưng server dùng nó để tra ra "à, đây là ông Khang đã đăng nhập lúc 9h sáng".
Các thuộc tính cookie mà QA phải kiểm tra:
HttpOnly: cookie không đọc được bằng JavaScript ở phía client → chống XSS đánh cắp session. Session cookie bắt buộc phải có cờ này.Secure: cookie chỉ gửi qua HTTPS. Bắt buộc trên production.SameSite:Strict,Lax, hoặcNone— kiểm soát việc cookie có được gửi trong request cross-site không. Đây là tuyến phòng thủ CSRF hiện đại.Max-Age/Expires: thời gian sống của session.
CSRF là gì và vì sao Postman kiểm thử được
CSRF xảy ra khi kẻ tấn công lừa trình duyệt của nạn nhân (đang đăng nhập) gửi một request độc hại — ví dụ chuyển tiền — mà trình duyệt tự đính kèm cookie session hợp lệ. Để chống lại, server yêu cầu mỗi request thay đổi dữ liệu phải kèm một CSRF token — một giá trị bí mật, khó đoán, mà chỉ trang hợp pháp mới lấy được (thường nhúng trong form HTML hoặc trả qua header X-CSRF-TOKEN).
Vai trò của QA: kiểm chứng rằng server từ chối request thiếu CSRF token hoặc có token sai, nhưng chấp nhận request có token đúng. Postman rất hợp để mô phỏng cả hai kịch bản.
Tình huống thực tế
Ví dụ 1 — Cổng quản trị Laravel của một sàn TMĐT tại TP.HCM
Một công ty thương mại điện tử giả định, "ChoTot Mini", có trang admin viết bằng Laravel. Bạn được giao kiểm thử API /admin/products (tạo sản phẩm). Bạn viết request POST, chạy thử — server trả 419 Page Expired. Nhiều QA mới vào nghề bối rối vì 419 không phải mã lỗi HTTP quen thuộc.
Nguyên nhân: Laravel bật CSRF protection mặc định. Server chờ header X-CSRF-TOKEN hoặc field _token. Luồng đúng là: (1) GET trang login để nhận cookie XSRF-TOKEN và laravel_session; (2) đọc giá trị XSRF-TOKEN từ cookie jar; (3) đính kèm vào header request POST. Sau khi thêm X-XSRF-TOKEN với giá trị đã URL-decode, request trả về 201 Created.
Bài học rút ra: mã 419/403 khi POST thường không phải lỗi code của bạn, mà là CSRF protection đang làm đúng việc của nó. Là QA, bạn còn phải viết thêm một test case âm — cố tình gửi request không có token và assert rằng server trả 419 — để chứng minh cơ chế bảo mật thực sự hoạt động, chứ không phải bị tắt.
Ví dụ 2 — Session hết hạn tại ngân hàng số
Một ngân hàng số giả định, "VBank Digital", quy định session hết hạn sau 15 phút không hoạt động — yêu cầu tuân thủ của Ngân hàng Nhà nước về bảo mật giao dịch. QA cần chứng minh rằng sau khi hết hạn, mọi request đều bị đẩy về đăng nhập lại.
Bạn thiết kế collection: request 1 đăng nhập, lưu thời điểm; sau đó dùng một pre-request script để mô phỏng "cookie đã cũ". Trong thực tế, bạn không thể đợi 15 phút mỗi lần chạy, nên team dùng một environment staging có timeout rút xuống 60 giây để test nhanh. Bạn gọi request bảo mật ngay (phải 200), rồi dùng Postman Monitor hoặc delay 65 giây rồi gọi lại (phải 401). Kết quả: lần đầu QA phát hiện session của VBank không hết hạn đúng — do một bug cấu hình Redis khiến TTL bị reset mỗi lần đọc, session sống mãi. Đây là lỗ hổng bảo mật nghiêm trọng, phát hiện nhờ test session lifecycle.
Bài học rút ra: đừng tin lời khai của dev rằng "session hết hạn sau 15 phút". Hãy tạo môi trường có timeout ngắn và chứng minh bằng test thật. Session lifecycle là thứ dễ cấu hình sai và cực kỳ nguy hiểm khi sai.
Ví dụ 3 — Cookie jar bẩn gây flaky test
Một team QA tại một startup fintech ở Hà Nội gặp cảnh: bộ regression chạy ổn khi test từng request riêng lẻ, nhưng khi chạy cả collection bằng Runner thì ngẫu nhiên fail. Điều tra ra: một test "đăng xuất" ở giữa collection xóa session cookie, nhưng các test phía sau vẫn giả định còn đăng nhập. Vì cookie jar dùng chung xuyên suốt collection, trạng thái của request này rò rỉ sang request kia.
Giải pháp: họ thêm một request clearCookies đầu collection và dùng pm.cookies.jar() để chủ động dọn jar trước mỗi luồng độc lập, đảm bảo mỗi test bắt đầu từ trạng thái sạch.
Bài học rút ra: tính stateful của cookie jar là con dao hai lưỡi. Với luồng tuần tự (login → dùng session) thì nó tuyệt vời; với bộ test độc lập thì bạn phải chủ động cô lập trạng thái.
Hướng dẫn từng bước
Bước 1 — Kiểm tra Set-Cookie sau đăng nhập. Sau khi POST /login, mở tab Cookies (dưới nút Send) hoặc Headers của response để xem Set-Cookie. Viết test khẳng định cookie session tồn tại:
pm.test("Server tra ve session cookie", function () {
const cookie = pm.cookies.get("JSESSIONID");
pm.expect(cookie).to.not.be.null;
});
Bước 2 — Kiểm tra các cờ bảo mật. Postman pm.cookies không phơi bày trực tiếp cờ HttpOnly/Secure, nên bạn parse từ header Set-Cookie thô:
pm.test("Session cookie co HttpOnly va Secure", function () {
const raw = pm.response.headers.get("Set-Cookie");
pm.expect(raw).to.include("HttpOnly");
pm.expect(raw).to.include("Secure");
pm.expect(raw).to.match(/SameSite=(Strict|Lax)/i);
});
Bước 3 — Xác nhận cookie tự động được gửi. Ở request bảo mật kế tiếp cùng domain, chỉ cần Send. Nếu server trả 200, cookie jar đã hoạt động. Nếu trả 401, kiểm tra domain có khớp không (thường lỗi do gọi www. ở request này nhưng cookie set cho domain không có www.).
Bước 4 — Lấy CSRF token và đính kèm. Sau khi GET trang có token, đọc từ cookie jar bằng pm.cookies rồi lưu vào biến để dùng ở request POST:
// Trong Tests cua request GET
const jar = pm.cookieJar();
jar.get(pm.request.url.getHost(), "XSRF-TOKEN", (err, value) => {
pm.collectionVariables.set("csrfToken", decodeURIComponent(value));
});
Rồi ở request POST, thêm header X-XSRF-TOKEN: {{csrfToken}}.
Bước 5 — Viết test âm cho CSRF. Tạo một request POST cố tình bỏ token, và assert server từ chối:
pm.test("Request thieu CSRF token bi tu choi", function () {
pm.expect(pm.response.code).to.be.oneOf([403, 419]);
});
Bước 6 — Test đăng xuất và dọn dẹp. Gọi /logout, sau đó gọi lại request bảo mật và assert 401 — chứng minh session thực sự bị hủy phía server, không chỉ xóa cookie phía client.
Lỗi thường gặp & mẹo
- Nhầm giữa xóa cookie và hủy session. Xóa cookie trong Postman chỉ làm client "quên vé", nhưng session vẫn sống ở server. Luôn test rằng sau
/logout, session ID cũ (nếu ai đó giữ được) cũng không còn dùng được — đây là kiểm thử "session fixation / invalidation".
- Cookie không khớp domain. Cookie set cho
.example.vnsẽ gửi cho mọi subdomain, nhưng cookie set choapi.example.vn(không có dấu chấm đầu) chỉ gửi cho đúng host đó. Nếu request 401 dù đã đăng nhập, hãy vào Manage Cookies kiểm tra chính xác domain/path.
- URL-encoding của CSRF token. Token trong cookie thường bị URL-encode (
%3Dthay cho=). QuêndecodeURIComponentsẽ khiến server báo token sai. Đây là bẫy kinh điển với Laravel/Django.
- Interceptor và cookie trình duyệt. Nếu bạn bật Postman Interceptor để đồng bộ cookie từ Chrome, jar có thể chứa cookie "lậu" từ phiên duyệt thật, gây kết quả sai lệch. Với test tự động, hãy tắt Interceptor để jar sạch, có kiểm soát.
- SameSite=None thiếu Secure. Trình duyệt hiện đại từ chối cookie
SameSite=Nonemà không cóSecure. Nếu API di động/cross-site gặp lỗi mất session, đây là nghi phạm số một — và là một bug đáng báo cáo.
- Mẹo cô lập test: với bộ test chạy bằng Newman/Runner, thêm một request đầu tiên chỉ chạy
pm.cookieJar().clear(host, cb)để đảm bảo mỗi lần chạy bắt đầu từ jar rỗng, tránh flaky do trạng thái rò rỉ.
Bài tập thực hành
- Luồng session cơ bản: Với một API cookie-based bất kỳ (hoặc dùng
https://postman-echo.com/cookies/set), tạo collection 3 request: đăng nhập → gọi API bảo mật (assert 200 và cookie tự gửi) → đăng xuất (assert request bảo mật sau đó trả 401).
- Audit cờ bảo mật: Viết một test tái sử dụng được, kiểm tra session cookie có đủ
HttpOnly,Secure, vàSameSite. Chạy nó trên cả môi trường staging và production, ghi lại khác biệt (nhiều team quên bậtSecureở staging).
- CSRF dương và âm: Với một backend Laravel/Django cục bộ, viết hai test: một request POST có CSRF token đúng (assert 2xx) và một request thiếu token (assert 403/419). Chứng minh cơ chế CSRF thực sự bật.
- Session timeout: Cấu hình một môi trường staging với timeout session 60 giây. Viết collection gọi API ngay (200), delay >60s, gọi lại (401). Ghi lại liệu server có thực sự hết hạn đúng không.
Tóm tắt
Cookie, session và CSRF là bộ ba xác thực của thế giới web truyền thống mà token-based không thay thế được. Postman quản lý cookie qua cookie jar — mỗi domain một jar, tự động gửi khi khớp domain, và giữ trạng thái xuyên suốt collection. Tính stateful này là bạn tốt của luồng login tuần tự nhưng là kẻ thù của test độc lập, nên hãy chủ động dọn jar khi cần cô lập.
Là QA, công việc của bạn vượt xa "làm request chạy được": bạn kiểm chứng session cookie có HttpOnly/Secure/SameSite, session hết hạn đúng lúc, đăng xuất thực sự hủy session phía server, và CSRF protection từ chối đúng các request thiếu token. Ba tình huống thực tế — 419 của Laravel, session không hết hạn của ngân hàng, và cookie jar bẩn gây flaky — cho thấy đây không phải lý thuyết suông mà là những lỗ hổng thật bạn sẽ gặp trong nghề. Nắm vững chương này, bạn trở thành người gác cổng cho tầng xác thực của hệ thống.