Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Test File Upload — image, PDF, video

Software Testing Fundamentals Bài 54/60

Mở đầu — vì sao bài này quan trọng

Nếu bạn từng dùng bất kỳ ứng dụng hiện đại nào — đăng ảnh đại diện lên mạng xã hội, nộp CV PDF cho nhà tuyển dụng, upload video sản phẩm lên sàn thương mại điện tử — thì bạn đã chạm vào tính năng file upload. Đây là một trong những tính năng "trông có vẻ đơn giản" nhất nhưng lại là nơi ẩn chứa nhiều lỗ hổng bảo mật nghiêm trọng và trải nghiệm người dùng tồi tệ nhất.

Vì sao? Vì upload file là điểm mà người dùng gửi dữ liệu tùy ý vào hệ thống của bạn. Không giống một ô text bạn có thể validate bằng vài dòng regex, một file có thể là ảnh JPG lành tính, cũng có thể là một web shell PHP đội lốt ảnh để chiếm quyền điều khiển máy chủ. Với một tester QA, hiểu cách kiểm thử file upload đúng cách vừa bảo vệ được hệ thống, vừa đảm bảo hàng triệu người dùng thật sự upload được thứ họ cần.

Trong bài này, mình sẽ hướng dẫn bạn tư duy kiểm thử file upload một cách có hệ thống: từ validate định dạng, kích thước, nội dung, cho tới các kịch bản bảo mật và biên (edge case) mà đội dev thường bỏ sót. Đây là kiến thức áp dụng ngay được cho mọi dự án có form upload — mà gần như dự án nào cũng có.

Khái niệm cốt lõi

Kiểm thử file upload không chỉ là "chọn file rồi bấm nút". Bạn cần phân chia thành các nhóm kiểm thử (test categories) rõ ràng để không bỏ sót. Mình chia thành sáu nhóm chính.

1. Format validation — kiểm tra định dạng file

Đây là nhóm quan trọng và cũng dễ làm sai nhất. Hệ thống phải:

  • Accept đúng những định dạng được phép: ví dụ ảnh jpg, png, webp; tài liệu pdf, docx; video mp4, mov.
  • Reject sai những định dạng nguy hiểm: exe, sh, bat, php, jsp, html. Đây là vấn đề bảo mật, không chỉ là chức năng.
Điểm mấu chốt mà nhiều tester non kinh nghiệm bỏ qua: kiểm tra phần mở rộng (extension) là không đủ. Một kẻ tấn công chỉ cần đổi tên shell.php thành shell.jpg là qua được nếu hệ thống chỉ nhìn đuôi file. Vì thế server phải kiểm tra magic byte (còn gọi là file signature) — vài byte đầu tiên của file cho biết loại thật sự của nó.

Ví dụ magic byte của một số định dạng:

  • JPG bắt đầu bằng FF D8 FF
  • PNG bắt đầu bằng 89 50 4E 47 (chính là ‰PNG)
  • PDF bắt đầu bằng 25 50 44 46 (chính là %PDF)
  • MP4 chứa 66 74 79 70 (ftyp) ở vị trí byte thứ 5
Là tester, bạn cần thiết kế test để đánh lừa hệ thống: đổi đuôi file, đổi Content-Type trong request, tạo file "polyglot" (file hợp lệ ở đầu nhưng nhét mã độc phía sau). Nếu server chỉ tin vào extension hoặc chỉ tin vào MIME type do trình duyệt gửi, nó sẽ thủng.

2. Size validation — kiểm tra kích thước

Mỗi loại file cần có giới hạn dung lượng hợp lý. Bạn phải test:

  • File đúng giới hạn (ví dụ ảnh tối đa 5 MB): upload file 4.9 MB (pass), 5 MB (biên), 5.1 MB (reject).
  • File 0 byte (file rỗng) — hệ thống xử lý ra sao?
  • File cực lớn (ví dụ video 2 GB) — có gây timeout, tràn bộ nhớ, hay crash server không?
Đây chính là nơi bạn áp dụng tư duy Boundary Value Analysis: giá trị ngay dưới, đúng bằng, và ngay trên ngưỡng.

3. Content & integrity validation — nội dung và tính toàn vẹn

File upload thành công nhưng có mở được, xem được, phát được không? Một ảnh bị hỏng (corrupted) có thể qua được validate định dạng nhưng khi hiển thị lại vỡ. Video upload xong có phát đúng không? PDF có mở được bằng trình đọc chuẩn không? Bạn cần test cả file hợp lệ nhưng bị lỗi cấu trúc.

4. Security validation — kiểm thử bảo mật

Ngoài magic byte, cần test:

  • Path traversal: đặt tên file kiểu ../../etc/passwd hoặc ../../../var/www/index.php để xem hệ thống có ghi file ra ngoài thư mục dự kiến không.
  • Tên file độc hại: chứa ký tự đặc biệt, null byte (shell.php%00.jpg), tên siêu dài (255+ ký tự), hoặc script injection trong tên file (<script>alert(1)</script>.jpg).
  • Nội dung nhúng mã: ảnh chứa EXIF metadata có mã độc, SVG chứa thẻ <script> (SVG là XML, có thể chạy JavaScript!).
  • Executable ẩn: file .php, .phtml, .php5 upload vào thư mục có thể truy cập qua web và thực thi được.

5. Concurrency & storage — đồng thời và lưu trữ

Nhiều người upload cùng lúc thì sao? Hai file trùng tên có ghi đè nhau không? Dung lượng lưu trữ đầy thì hệ thống báo lỗi thế nào? File upload dở dang (đứt mạng giữa chừng) có để lại rác không?

6. UX & feedback — trải nghiệm người dùng

Có progress bar khi upload file lớn không? Thông báo lỗi có rõ ràng không (báo "File quá lớn, tối đa 5 MB" thay vì "Lỗi 500")? Có preview trước khi submit không? Người dùng có xóa/thay được file đã chọn không?

Tình huống thực tế

Ví dụ 1 — Sàn TMĐT Việt Nam và lỗ hổng web shell

Một sàn thương mại điện tử tại TP.HCM (giả định gọi là ShopViet, khoảng 200.000 người bán) cho phép người bán upload ảnh sản phẩm. Đội dev validate bằng cách kiểm tra đuôi file phải là .jpg, .png, .webp. Trông có vẻ ổn.

Một pentester trong đợt kiểm thử bảo mật đã tạo file product.php.jpg — nhưng thực chất bên trong là mã PHP web shell. Do server dùng cấu hình Apache cũ xử lý .php.jpg như file PHP, kẻ tấn công truy cập /uploads/product.php.jpg và thực thi được lệnh trên máy chủ, đọc được cả file cấu hình chứa mật khẩu database.

Bài học: Nếu tester QA trong sprint trước đã thiết kế test case "upload file đổi tên đuôi + kiểm tra magic byte + kiểm tra khả năng thực thi trong thư mục uploads", lỗ hổng này đã bị chặn từ sớm. Đừng bao giờ chỉ tin vào extension. Luôn test double extension (.php.jpg, .jpg.php) và kiểm tra thư mục uploads có bị cấu hình no execute không.

Ví dụ 2 — Ứng dụng gọi xe và video khiếu nại 2 GB

Một ứng dụng gọi xe ở Đông Nam Á (giả định là RideGo) cho phép tài xế upload video dashcam để khiếu nại khi có tranh chấp. Giới hạn thiết kế là 100 MB, định dạng mp4, mov.

Trong đợt kiểm thử, tester thử upload một video mov dung lượng 1.8 GB từ điện thoại. Ứng dụng mobile không kiểm tra kích thước phía client, gửi thẳng lên server. Server nhận request, load toàn bộ file vào RAM để xử lý, khiến pod backend bị OOM (Out Of Memory) và restart. Trong lúc đó, hàng nghìn tài xế khác bị gián đoạn dịch vụ suốt 4 phút.

Bài học: Phải test cả giới hạn phía client (chặn sớm, tiết kiệm băng thông người dùng) lẫn phía server (chặn muộn, đảm bảo an toàn thật). Test case bắt buộc: upload file vượt ngưỡng từ mọi luồng (web, iOS, Android) và quan sát server có graceful reject (báo lỗi 413 Payload Too Large) hay crash. Đây cũng là lý do tester cần phối hợp với đội hiểu về hạ tầng.

Ví dụ 3 — Nền tảng học trực tuyến và bài nộp PDF tiếng Việt

Một nền tảng học trực tuyến cho phép học viên nộp bài tập dạng PDF. Tester phát hiện: file PDF có tên tiếng Việt như Bài tập cuối kỳ - Nguyễn Văn A.pdf sau khi upload bị lưu thành Bàa i tà¢p...pdf — tên file bị vỡ do server không xử lý encoding UTF-8 đúng cách. Khi giảng viên tải về, tên file thành ký tự loạn, không biết bài của ai.

Ngoài ra, một học viên nộp file 0 byte (do xuất PDF lỗi), hệ thống báo "Nộp thành công" nhưng giảng viên mở lên thì trống trơn.

Bài học: Với bối cảnh Việt Nam, tên file có dấu là kịch bản bắt buộc phải test. Và luôn test file rỗng (0 byte) — hệ thống nên reject hoặc cảnh báo, không nên báo thành công một cách âm thầm. Việc validate "file có nội dung thật" quan trọng ngang với validate định dạng.

Hướng dẫn từng bước

Đây là quy trình thực chiến để kiểm thử một tính năng file upload từ đầu đến cuối.

Bước 1 — Thu thập yêu cầu (requirement). Trước khi test, hỏi rõ: Định dạng nào được phép? Giới hạn dung lượng bao nhiêu cho mỗi loại? Upload được bao nhiêu file một lúc? File lưu ở đâu (local, S3, CDN)? Có quét virus không? Nếu thiếu spec, đây chính là lúc bạn tạo giá trị bằng cách đặt câu hỏi.

Bước 2 — Chuẩn bị bộ test data. Tạo sẵn một thư mục file mẫu: file hợp lệ mỗi loại (jpg, png, pdf, mp4), file đúng biên kích thước (dưới/bằng/trên ngưỡng), file 0 byte, file corrupted, file đổi đuôi (shell.php đổi thành shell.jpg), file double extension, file tên tiếng Việt có dấu, file tên siêu dài, ảnh SVG chứa script. Bộ này dùng lại được cho mọi dự án.

Bước 3 — Test happy path. Upload từng định dạng hợp lệ, xác nhận: upload thành công, hiển thị/preview đúng, mở/phát lại được, lưu đúng vị trí, tên file giữ nguyên (kể cả có dấu).

Bước 4 — Test format validation. Upload định dạng không cho phép (exe, sh, php). Sau đó test nâng cao: đổi đuôi file, đổi Content-Type trong request bằng công cụ như Postman hoặc Burp Suite, upload polyglot file. Xác nhận server chặn dựa trên magic byte chứ không chỉ extension.

Bước 5 — Test size & boundary. Áp dụng Boundary Value Analysis quanh mọi ngưỡng dung lượng. Đừng quên file 0 byte và file khổng lồ.

Bước 6 — Test security. Path traversal trong tên file, null byte, script trong tên file, kiểm tra thư mục uploads có thực thi được mã không. Đây là phần cần phối hợp với security tester nếu có.

Bước 7 — Test edge case & concurrency. Upload nhiều file trùng tên, upload đồng thời, ngắt mạng giữa chừng, hủy upload, upload lại file vừa xóa.

Bước 8 — Test UX. Progress bar, thông báo lỗi rõ ràng, khả năng xóa/thay file, giới hạn số lượng file.

Bước 9 — Ghi nhận & báo cáo. Với mỗi lỗi, ghi rõ file dùng để tái hiện, request gửi lên, kết quả mong đợi vs thực tế. Đính kèm file mẫu vào bug report để dev tái hiện ngay.

Lỗi thường gặp & mẹo

Lỗi 1 — Chỉ test bằng chuột trên trình duyệt. Trình duyệt tự lọc bớt định dạng qua thuộc tính accept của thẻ input, nên bạn tưởng hệ thống an toàn. Nhưng kẻ tấn công không dùng trình duyệt — họ gửi HTTP request trực tiếp. Mẹo: luôn test bằng Postman hoặc Burp Suite để bỏ qua ràng buộc phía client, đánh thẳng vào API.

Lỗi 2 — Quên test file rỗng và file corrupted. Nhiều hệ thống báo "thành công" với file 0 byte. Mẹo: thêm file 0 byte và file hỏng vào bộ data chuẩn, chạy mỗi lần.

Lỗi 3 — Bỏ qua tên file tiếng Việt và ký tự đặc biệt. Đây là điểm mù kinh điển ở dự án Việt Nam. Mẹo: luôn có file tên Tài liệu (bản chính) #1.pdf trong bộ test — vừa kiểm tra dấu, vừa kiểm tra khoảng trắng, ngoặc, ký tự đặc biệt.

Lỗi 4 — Chỉ nhìn kết quả upload, không kiểm tra nơi lưu. File upload "thành công" nhưng lưu sai thư mục, sai quyền, hoặc URL public lộ đường dẫn nhạy cảm. Mẹo: sau upload, kiểm tra file thật sự nằm ở đâu và ai truy cập được.

Lỗi 5 — Không test lại sau khi xóa. Xóa file nhưng bản lưu vẫn còn trên CDN/S3 (do cache), hoặc metadata trong DB vẫn trỏ tới file cũ. Mẹo: test trọn vòng đời file: upload → hiển thị → thay thế → xóa → xác nhận không còn truy cập được.

Mẹo tổng quát: Với file upload, tư duy như một kẻ tấn công. Đừng hỏi "người dùng bình thường làm gì" mà hỏi "kẻ xấu có thể lách bằng cách nào". Chính tư duy này phân biệt một tester giỏi.

Bài tập thực hành

  • Xây bộ test data chuẩn. Tạo một thư mục gồm ít nhất 12 file: mỗi định dạng hợp lệ một file, một file 0 byte, một file vượt ngưỡng, một file .php đổi đuôi thành .jpg, một file double extension, một file tên tiếng Việt có dấu và ký tự đặc biệt, một ảnh corrupted. Ghi chú mục đích test của từng file.
  • Thiết kế test suite. Với một form upload ảnh đại diện giả định (chỉ nhận jpg/png, tối đa 2 MB, 1 file), hãy viết đầy đủ test case cho cả sáu nhóm đã học. Đặt mục tiêu tối thiểu 20 test case, đánh dấu rõ đâu là test bảo mật.
  • Thực hành với công cụ. Dùng Postman hoặc Burp Suite tự dựng một request upload, thử đổi Content-Type và tên file thành ../../test.jpg. Ghi lại điều bạn quan sát được và giải thích vì sao path traversal nguy hiểm.
  • Viết bug report. Chọn một lỗi bạn tìm ra (ví dụ file 0 byte báo thành công), viết bug report hoàn chỉnh: tiêu đề, bước tái hiện, file mẫu đính kèm, kết quả mong đợi vs thực tế, và đề xuất mức severity.

Tóm tắt

File upload là tính năng "nhỏ mà không nhỏ" — điểm giao giữa chức năng, trải nghiệm và bảo mật. Một tester QA giỏi kiểm thử file upload theo sáu nhóm: định dạng (không chỉ tin extension mà phải kiểm magic byte), kích thước (áp dụng BVA quanh mọi ngưỡng, nhớ file 0 byte và file khổng lồ), nội dung và tính toàn vẹn, bảo mật (path traversal, tên file độc hại, SVG chứa script, thư mục uploads không được thực thi), đồng thời và lưu trữ, cuối cùng là trải nghiệm người dùng.

Ba bài học cốt lõi từ các tình huống thực tế: (1) đừng bao giờ chỉ tin phần mở rộng file — luôn kiểm magic byte và test double extension; (2) phải chặn kích thước ở cả client lẫn server để tránh crash; (3) với bối cảnh Việt Nam, tên file có dấu và file rỗng là kịch bản bắt buộc. Hãy tư duy như một kẻ tấn công, chuẩn bị sẵn bộ test data dùng lại được, và luôn test bằng công cụ API để vượt qua ràng buộc phía trình duyệt. Khi bạn thành thạo kiểm thử file upload, bạn đã nắm được một trong những kỹ năng thực chiến giá trị nhất trong nghề QA.