External control of file name or path - TypeScript

External control of file name or path - TypeScript

Need

Prevention of unauthorized file access and manipulation

Context

  • Usage of TypeScript for statically typed JavaScript development
  • Usage of Express for building web applications and APIs
  • Usage of multer for handling file uploads in Node.js

Description

Non compliant code

        import express from 'express';
import multer from 'multer';

const app = express();
const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('file'), (req, res) => {
  const { file } = req;

  // Process the uploaded file
  // ...

  res.status(200).send('File uploaded successfully');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
        
        

The vulnerability in this code is that it allows external control of the file name or path during file upload.

In the code, the multer middleware is used to handle file uploads. The upload object is created with a destination folder specified as 'uploads/'. This means that any file uploaded through this endpoint will be saved in the uploads folder.

However, the code does not validate or restrict the file name or path in any way. This means that an attacker can manipulate the file name or path in the request to save the uploaded file to a different location on the server.

The impact of this vulnerability is that an attacker can potentially save files in paths other than the intended destination folder. They can overwrite important files within the system by referring to a path where the upload is performed.

To fix this vulnerability, it is recommended to validate the uploaded file names and restrict the storage to the intended folders only. This can be done by implementing checks on the file name or path before saving the file, ensuring that it adheres to the expected format and location.

Steps

  • Create a whitelist of allowed file extensions that can be uploaded.
  • Validate the file name to ensure it does not contain any special characters or path traversal sequences.
  • Use a secure file storage location instead of the 'uploads/' folder.
  • Update the multer configuration to specify the storage location and file name.
  • Implement server-side checks to prevent overwriting existing files.
  • Return appropriate error responses if the file upload fails or if the file name is not valid.

Compliant code

        import express from 'express';
import multer from 'multer';
import { v4 as uuidv4 } from 'uuid';

const app = express();

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'secure_uploads/');
  },
  filename: function (req, file, cb) {
    const fileName = uuidv4() + '-' + file.originalname;
    cb(null, fileName);
  }
});

const upload = multer({
  storage: storage,
  fileFilter: function (req, file, cb) {
    const allowedExtensions = ['.jpg', '.jpeg', '.png'];
    const fileExtension = file.originalname.toLowerCase().slice(file.originalname.lastIndexOf('.'));
    if (allowedExtensions.includes(fileExtension)) {
      cb(null, true);
    } else {
      cb(new Error('Invalid file extension'));
    }
  }
});

app.post('/upload', upload.single('file'), (req, res, next) => {
  const { file } = req;

  if (!file) {
    return res.status(400).send('No file uploaded');
  }

  // Process the uploaded file
  // ...

  res.status(200).send('File uploaded successfully');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
        
        

The fixed code addresses the vulnerability by implementing several security measures.

1. The code uses the multer library to handle file uploads securely. It sets up a disk storage configuration using multer.diskStorage, which specifies the destination folder for uploaded files. In this case, the destination folder is set to secure_uploads/.

2. The code generates a unique filename for each uploaded file using the uuidv4 function from the uuid library. This helps to prevent any potential conflicts or overwriting of files.

3. The code includes a file filter function that checks the file extension of the uploaded file. It only allows files with extensions .jpg, .jpeg, and .png to be uploaded. Any file with an invalid extension will result in an error.

4. The code handles the file upload in the /upload route using upload.single('file'). This ensures that only one file is uploaded at a time. If no file is uploaded, it returns a 400 Bad Request response.

5. After the file is uploaded, the code can process the file as needed. This part is not shown in the code snippet and should be implemented separately.

6. Finally, the code listens for incoming requests on port 3000 and logs a message to indicate that the server is running.

By implementing these measures, the code ensures that uploaded files are saved in the designated secure_uploads/ folder, uses unique filenames to prevent overwriting, and restricts the file types that can be uploaded.

References