File uploading is a common feature in modern web applications. Users may submit various types of files such as images, videos, documents, etc. to interact with the Service. However, file uploads also come with potential security risks. This article provides a detailed, visual guide on how to securely upload files in your Node.js Express application. Follow this to make sure your app is safe and efficient.
1: Understand the risks of file uploads
Before embarking on security measures, it is important to understand the potential risks associated with uploading files. These risks include:
- Malicious file uploads : Attackers sometimes upload files containing malicious scripts that can compromise your application or server.
- Denial of Service (DoS) Attacks : Massive file uploads can exhaust server resources and make your app unresponsive.
- Leakage of Sensitive Data : Unauthorized users may gain access to files containing confidential information.
2: Setting up a basic Node.js Express application
To demonstrate how to secure file uploads, let’s set up a basic Node.js Express application. First, install the required packages.
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 |
Next, create an app.js
file and import the required modules.
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: Implementing file upload using Multer
Multer is a popular middleware for handling file uploads in Express. First, configure Multer and configure the storage engine.
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> |
Next, create a route for file upload.
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: File upload security
4.1 File size limit
The first security measure is to limit the file size. This helps prevent DoS attacks and reduces the risk of server resource exhaustion. Set a file size limit when configuring 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 File type verification
Allow only certain file types. This reduces the risk of malicious file uploads. Add file filter function to multer settings.
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 Handling Rejected Files
It is important to provide the user with an appropriate error message when the file is rejected. Update the /upload
route to handle rejected files.
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 Scan files for malware
Scan uploaded files for malware to further protect your application. You can use the ClamAV antivirus engine. Install the clamscan
package.
1 2 | npm install clamscan |
Then import and configure the ClamScan module.
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> |
Scans files uploaded via /upload
route for malware.
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> |
Don’t forget to import fs
module.
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 Storing files outside the web root
Storing uploaded files outside the web root prevents direct access to those files. In this example, we will use uploads
folder and place it outside the web root directory.
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 Serving Files Securely
To serve the file securely, we check the user’s authentication and create a new route that serves the file using the res.sendFile() method.
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> |
Don’t forget to import path
module.
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> |
Conclusion
By following this comprehensive guide, you can create a secure file upload system in your Node.js Express application. By implementing appropriate security measures such as file size limits, file type validation, file scanning for malware, and secure file serving, you can protect your app from various risks associated with file uploads.
last
I am always indebted. I hope you enjoyed this article and learned something new.
See you in the next article! If you like this article, please hit “LIKE” and subscribe to support me. thank you very much.