Tải lên tệp là một tính năng phổ biến trong các ứng dụng web hiện đại. Người dùng có thể gửi các loại tệp khác nhau như hình ảnh, video, tài liệu, v.v. để tương tác với Dịch vụ. Tuy nhiên, tải lên tệp cũng đi kèm với rủi ro bảo mật tiềm ẩn. Bài viết này cung cấp hướng dẫn trực quan, chi tiết về cách tải tệp lên một cách an toàn trong ứng dụng Node.js Express của bạn. Làm theo điều này để đảm bảo ứng dụng của bạn an toàn và hiệu quả.
1: Hiểu những rủi ro khi tải tệp lên
Trước khi bắt tay vào các biện pháp bảo mật, điều quan trọng là phải hiểu những rủi ro tiềm ẩn liên quan đến việc tải tệp lên. Những rủi ro này bao gồm:
- Tải lên tệp độc hại : Đôi khi, những kẻ tấn công tải lên các tệp chứa tập lệnh độc hại có thể xâm phạm ứng dụng hoặc máy chủ của bạn.
- Tấn công từ chối dịch vụ (DoS) : Tải lên hàng loạt tệp có thể làm cạn kiệt tài nguyên máy chủ và khiến ứng dụng của bạn không phản hồi.
- Rò rỉ dữ liệu nhạy cảm : Người dùng trái phép có thể có quyền truy cập vào các tệp chứa thông tin bí mật.
2: Thiết lập ứng dụng Node.js Express cơ bản
Để trình bày cách bảo mật tệp tải lên, hãy thiết lập một ứng dụng Node.js Express cơ bản. Đầu tiên, cài đặt các gói cần thiết.
1 2 3 | <span class="token function">npm</span> init -y <span class="token function">npm</span> <span class="token function">install</span> express multer |
Tiếp theo, tạo tệp app.js
và nhập các mô-đun cần thiết.
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'express'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> multer <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'multer'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> port <span class="token operator">=</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">PORT</span> <span class="token operator">||</span> <span class="token number">3000</span> <span class="token punctuation">;</span> app <span class="token punctuation">.</span> <span class="token function">listen</span> <span class="token punctuation">(</span> port <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token template-punctuation string">`</span> <span class="token string">Server running at http://localhost:</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> port <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token template-punctuation string">`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
3: Triển khai tải tệp lên bằng Multer
Multer là một phần mềm trung gian phổ biến để xử lý các tệp tải lên trong Express. Đầu tiên, định cấu hình Multer và định cấu hình công cụ lưu trữ.
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">const</span> storage <span class="token operator">=</span> multer <span class="token punctuation">.</span> <span class="token function">diskStorage</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token function-variable function">destination</span> <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> file <span class="token punctuation">,</span> cb</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">cb</span> <span class="token punctuation">(</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> <span class="token string">'./uploads'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token function-variable function">filename</span> <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> file <span class="token punctuation">,</span> cb</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> uniqueSuffix <span class="token operator">=</span> Date <span class="token punctuation">.</span> <span class="token function">now</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'-'</span> <span class="token operator">+</span> Math <span class="token punctuation">.</span> <span class="token function">round</span> <span class="token punctuation">(</span> Math <span class="token punctuation">.</span> <span class="token function">random</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">1e9</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">cb</span> <span class="token punctuation">(</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> file <span class="token punctuation">.</span> fieldname <span class="token operator">+</span> <span class="token string">'-'</span> <span class="token operator">+</span> uniqueSuffix <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> upload <span class="token operator">=</span> <span class="token function">multer</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> storage <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Tiếp theo, tạo một tuyến đường để tải lên tệp.
1 2 3 4 | app <span class="token punctuation">.</span> <span class="token function">post</span> <span class="token punctuation">(</span> <span class="token string">'/upload'</span> <span class="token punctuation">,</span> upload <span class="token punctuation">.</span> <span class="token function">single</span> <span class="token punctuation">(</span> <span class="token string">'file'</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">200</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> message <span class="token operator">:</span> <span class="token string">'File uploaded successfully.'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
4: Bảo mật tệp tải lên
4.1 Giới hạn kích thước tệp
Biện pháp bảo mật đầu tiên là giới hạn kích thước tệp. Điều này giúp ngăn chặn các cuộc tấn công DoS và giảm nguy cơ cạn kiệt tài nguyên máy chủ. Đặt giới hạn kích thước tệp khi định cấu hình Multer.
1 2 3 4 5 | <span class="token keyword">const</span> upload <span class="token operator">=</span> <span class="token function">multer</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> storage <span class="token punctuation">,</span> limits <span class="token operator">:</span> <span class="token punctuation">{</span> fileSize <span class="token operator">:</span> <span class="token number">2</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token comment">// 2MB</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
4.2 Xác minh loại tệp
Chỉ cho phép một số loại tệp nhất định. Điều này làm giảm nguy cơ tải lên tệp độc hại. Thêm chức năng lọc tập tin vào cài đặt multer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">const</span> allowedFileTypes <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">'image/jpeg'</span> <span class="token punctuation">,</span> <span class="token string">'image/png'</span> <span class="token punctuation">,</span> <span class="token string">'image/gif'</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token function-variable function">fileFilter</span> <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> file <span class="token punctuation">,</span> cb</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> allowedFileTypes <span class="token punctuation">.</span> <span class="token function">includes</span> <span class="token punctuation">(</span> file <span class="token punctuation">.</span> mimetype <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">cb</span> <span class="token punctuation">(</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> <span class="token boolean">true</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">cb</span> <span class="token punctuation">(</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> upload <span class="token operator">=</span> <span class="token function">multer</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> storage <span class="token punctuation">,</span> limits <span class="token operator">:</span> <span class="token punctuation">{</span> fileSize <span class="token operator">:</span> <span class="token number">2</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> fileFilter <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
4.3 Xử lý tệp bị từ chối
Điều quan trọng là cung cấp cho người dùng thông báo lỗi thích hợp khi tệp bị từ chối. Cập nhật tuyến /upload
để xử lý các tệp bị từ chối.
1 2 3 4 5 6 7 | app <span class="token punctuation">.</span> <span class="token function">post</span> <span class="token punctuation">(</span> <span class="token string">'/upload'</span> <span class="token punctuation">,</span> upload <span class="token punctuation">.</span> <span class="token function">single</span> <span class="token punctuation">(</span> <span class="token string">'file'</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> req <span class="token punctuation">.</span> file <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">400</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> message <span class="token operator">:</span> <span class="token string">'Invalid file type or file too large.'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">200</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> message <span class="token operator">:</span> <span class="token string">'File uploaded successfully.'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
4.4 Quét tệp để tìm phần mềm độc hại
Quét các tệp đã tải lên để tìm phần mềm độc hại để bảo vệ ứng dụng của bạn hơn nữa. Bạn có thể sử dụng công cụ chống vi-rút ClamAV. Cài đặt gói clamscan
.
1 2 | npm install clamscan |
Sau đó nhập và định cấu hình mô-đun ClamScan.
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">const</span> <span class="token punctuation">{</span> NodeClam <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'clamscan'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> clamscan <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">NodeClam</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> clamscan <span class="token punctuation">.</span> <span class="token function">init</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> clamdscan <span class="token operator">:</span> <span class="token punctuation">{</span> path <span class="token operator">:</span> <span class="token string">'/usr/bin/clamdscan'</span> <span class="token punctuation">,</span> <span class="token comment">// お使いのサーバー上のclamdscanバイナリへのパス</span> config_file <span class="token operator">:</span> <span class="token string">'/etc/clamd.d/scan.conf'</span> <span class="token punctuation">,</span> <span class="token comment">// お使いのサーバー上のClamAV設定ファイルへのパス</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> preference <span class="token operator">:</span> <span class="token string">'clamdscan'</span> <span class="token punctuation">,</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Quét các tệp được tải lên qua đường /upload
để tìm phần mềm độc hại.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | app <span class="token punctuation">.</span> <span class="token function">post</span> <span class="token punctuation">(</span> <span class="token string">'/upload'</span> <span class="token punctuation">,</span> upload <span class="token punctuation">.</span> <span class="token function">single</span> <span class="token punctuation">(</span> <span class="token string">'file'</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> req <span class="token punctuation">.</span> file <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">400</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> message <span class="token operator">:</span> <span class="token string">'Invalid file type or file too large.'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> scanResult <span class="token operator">=</span> <span class="token keyword">await</span> clamscan <span class="token punctuation">.</span> <span class="token function">scan_file</span> <span class="token punctuation">(</span> req <span class="token punctuation">.</span> file <span class="token punctuation">.</span> path <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> scanResult <span class="token punctuation">.</span> is_infected <span class="token punctuation">)</span> <span class="token punctuation">{</span> fs <span class="token punctuation">.</span> <span class="token function">unlinkSync</span> <span class="token punctuation">(</span> req <span class="token punctuation">.</span> file <span class="token punctuation">.</span> path <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// 感染したファイルを削除する</span> <span class="token keyword">return</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">400</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> message <span class="token operator">:</span> <span class="token string">'File is infected with malware.'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">200</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> message <span class="token operator">:</span> <span class="token string">'File uploaded successfully.'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> error <span class="token punctuation">)</span> <span class="token punctuation">{</span> res <span class="token punctuation">.</span> <span class="token function">status</span> <span class="token punctuation">(</span> <span class="token number">500</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> message <span class="token operator">:</span> <span class="token string">'Error scanning file for malware.'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Đừng quên nhập mô-đun fs
.
1 2 | <span class="token keyword">const</span> fs <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'fs'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
4.5 Lưu trữ tệp bên ngoài web root
Lưu trữ các tệp đã tải lên bên ngoài thư mục gốc của web sẽ ngăn truy cập trực tiếp vào các tệp đó. Trong ví dụ này, chúng tôi sẽ sử dụng thư mục uploads
và đặt nó bên ngoài thư mục gốc của web.
1 2 3 4 5 6 7 | <span class="token keyword">const</span> storage <span class="token operator">=</span> multer <span class="token punctuation">.</span> <span class="token function">diskStorage</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token function-variable function">destination</span> <span class="token operator">:</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> file <span class="token punctuation">,</span> cb</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">cb</span> <span class="token punctuation">(</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> <span class="token string">'../uploads'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
4.6 Cung cấp tệp một cách an toàn
Để cung cấp tệp một cách an toàn, chúng tôi kiểm tra xác thực của người dùng và tạo một tuyến mới phục vụ tệp bằng phương thức res.sendFile().
1 2 3 4 5 6 7 | app <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token string">'/files/:filename'</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// ユーザーの認証をここで確認する</span> <span class="token keyword">const</span> filename <span class="token operator">=</span> req <span class="token punctuation">.</span> params <span class="token punctuation">.</span> filename <span class="token punctuation">;</span> <span class="token keyword">const</span> filePath <span class="token operator">=</span> path <span class="token punctuation">.</span> <span class="token function">join</span> <span class="token punctuation">(</span> <span class="token string">'../uploads'</span> <span class="token punctuation">,</span> filename <span class="token punctuation">)</span> <span class="token punctuation">;</span> res <span class="token punctuation">.</span> <span class="token function">sendFile</span> <span class="token punctuation">(</span> filePath <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Đừng quên nhập mô-đun path
.
1 2 | <span class="token keyword">const</span> path <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'path'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Phần kết luận
Bằng cách làm theo hướng dẫn toàn diện này, bạn có thể tạo một hệ thống tải lên tệp an toàn trong ứng dụng Node.js Express của mình. Bằng cách triển khai các biện pháp bảo mật thích hợp như giới hạn kích thước tệp, xác thực loại tệp, quét tệp để tìm phần mềm độc hại và phân phối tệp an toàn, bạn có thể bảo vệ ứng dụng của mình khỏi nhiều rủi ro liên quan đến việc tải tệp lên.
cuối cùng
Tôi luôn mắc nợ. Tôi hy vọng bạn thích bài viết này và học được một cái gì đó mới.
Hẹn gặp lại các bạn trong bài viết tiếp theo! Nếu bạn thích bài viết này, hãy nhấn “THÍCH” và đăng ký để ủng hộ tôi. Cảm ơn rất nhiều.