Forms, File Uploads và Security với NodeJS- phần 2


1054

Security

Nếu đang làm việc với các form  và session trên Internet, bạn cần lưu ý các lỗ hổng bảo mật phổ biến trong các ứng dụng web NodeJS. Lời khuyên bảo mật tốt nhất mà tôi nhận được là “Đừng bao giờ tin tưởng khách hàng!”

TLS qua HTTPS

Luôn sử dụng mã hóa TLS qua https: // khi làm việc với các form để dữ liệu đã gửi được mã hóa khi được gửi qua Internet. Nếu bạn gửi dữ liệu form qua http: //, dữ liệu đó sẽ được gửi ở dạng văn bản thuần túy và có thể hiển thị cho bất kỳ ai nghe trộm các gói đó khi họ di chuyển trên Web.

Luôn phải có Helmet

Có một middleware  nhỏ gọn gàng được gọi là helmet  bổ sung một số bảo mật từ các headers HTTP. Tốt nhất là nên include  ngay ở đầu middlewares  của bạn và rất dễ dàng include:

const helmet = require('helmet');
middlewares = [
  helmet(),
  // ...
];

Cross-site Request Forgery (CSRF)

Bạn có thể tự bảo vệ mình trước sự giả mạo request trên nhiều trang web bằng cách tạo token duy nhất khi người dùng được hiển thị với form NodeJS và sau đó xác thực token  đó trước khi dữ liệu POST được xử lý. Có một middleware  để giúp bạn ở đây:

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

Trong request GET, chúng ta tạo một token:

router.get('/contact', csrfProtection, (req, res) => {
  res.render('contact', {
    data: {},
    errors: {},
    csrfToken: req.csrfToken()
  });
});

Và cũng trong response  lỗi xác thực:

router.post('/contact', csrfProtection, [
  // validations ...
], (req, res) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.render('contact', {
      data: req.body,
      errors: errors.mapped(),
      csrfToken: req.csrfToken()
    });
  }
  // ...
});

Sau đó, chúng ta chỉ cần đưa token vào một input ẩn:

<form method="post" action="/contact" novalidate>
  <input type="hidden" name="_csrf" value="<%= csrfToken %>">
  <!-- ... -->
</form>

Đó là tất cả những gì cần thiết.

Chúng ta không cần sửa đổi trình xử lý request  POST của mình, vì tất cả các request  POST bây giờ sẽ yêu cầu token hợp lệ bởi middleware csurf. Nếu token  CSRF hợp lệ không được cung cấp, lỗi ForbiddenError sẽ được thrown. Lỗi này có thể được xử lý bởi trình xử lý lỗi được xác định ở file server.js.

Bạn có thể tự mình kiểm tra điều này bằng cách chỉnh sửa hoặc xóa token khỏi form NodeJS bằng các công cụ dành cho developer của trình duyệt và gửi.

Cross-site Scripting (XSS)

Bạn cần phải cẩn thận khi hiển thị dữ liệu do người dùng gửi trong HTML vì nó có thể giúp bạn tiếp cận với cross-site scripting(XSS)). Tất cả các ngôn ngữ template cung cấp các method  khác nhau để xuất giá trị. EJS <% = value%> xuất ra giá trị escaped HTML để bảo vệ bạn từ XSS, trong khi <% – value%> xuất ra một chuỗi raw.

Luôn sử dụng output  escaped <% = value%> khi xử lý các giá trị do người dùng gửi. Chỉ sử dụng output raw khi bạn chắc chắn rằng làm như vậy là an toàn.

File Uploads

Uploading files trong forms  HTML là một trường hợp đặc biệt yêu cầu loại mã hóa “multipart/form-data”. Xem hướng dẫn gửi dữ liệu form của MDN để biết thêm chi tiết về những gì sẽ xảy ra với việc gửi form NodeJS nhiều phần.

Bạn sẽ cần middleware  bổ sung để xử lý tải lên multipart . Có một package Express có tên multer mà chúng ta sẽ sử dụng ở đây:

const multer = require('multer');
const upload = multer({ storage: multer.memoryStorage() });
router.post('/contact', upload.single('photo'), csrfProtection, [
  // validation ...
], (req, res) => {
  // error handling ...
  if (req.file) {
    console.log('Uploaded: ', req.file);
    // Homework: Upload file to S3
  }
  req.flash('success', 'Thanks for the message! I’ll be in touch :)');
  res.redirect('/');
});

Code này hướng dẫn multer tải tệp trong field  “photo” vào bộ nhớ và hiển thị đối tượng File trong req.file, đối tượng này chúng ta có thể kiểm tra hoặc xử lý thêm.

Điều cuối cùng chúng ta cần là thêm thuộc tính enctype và đầu vào tệp của chúng ta:

<form method="post" action="/contact?_csrf=<%= csrfToken %>" novalidate enctype="multipart/form-data">
  <input type="hidden" name="_csrf" value="<%= csrfToken %>">
  <div class="form-field <%= errors.message ? 'form-field-invalid' : '' %>">
    <label for="message">Message</label>
    <textarea class="input" id="message" name="message" rows="4" autofocus><%= data.message %></textarea>
    <% if (errors.message) { %>
      <div class="error"><%= errors.message.msg %></div>
    <% } %>
  </div>
  <div class="form-field <%= errors.email ? 'form-field-invalid' : '' %>">
    <label for="email">Email</label>
    <input class="input" id="email" name="email" type="email" value="<%= data.email %>" />
    <% if (errors.email) { %>
      <div class="error"><%= errors.email.msg %></div>
    <% } %>
  </div>
  <div class="form-field">
    <label for="photo">Photo</label>
    <input class="input" id="photo" name="photo" type="file" />
  </div>
  <div class="form-actions">
    <button class="btn" type="submit">Send</button>
  </div>
</form>

Hãy thử tải lên một tệp. Bạn sẽ thấy các đối tượng File được đăng nhập trong console.

Populating File Inputs

Trong trường hợp xảy ra lỗi xác thực, chúng ta không thể điền lại đầu vào tệp như đã làm đối với input văn bản. Một cách tiếp cận phổ biến để giải quyết vấn đề này bao gồm các bước sau:

  • tải tệp lên vị trí tạm thời trên máy chủ 
  • hiển thị hình thu nhỏ và tên tệp của tệp đính kèm
  •  thêm JavaScript vào form để cho phép mọi người xóa tệp đã chọn hoặc tải tệp mới lên
  • di chuyển tệp đến vị trí cố định khi mọi thứ hợp lệ.

Do sự phức tạp bổ sung khi làm việc với multipart  và tải lên tệp, chúng thường được giữ ở các dạng riêng biệt.

Uploading Files với NodeJS

Cuối cùng, bạn sẽ nhận thấy rằng nó được để cho người đọc triển khai fuction upload file. Điều này không quá khó vì nó có thể được thực hiện bằng cách sử dụng các gói khác nhau, chẳng hạn như Form domains hoặc express-fileupload.

Kết luận

Tôi hy vọng bạn thích tìm hiểu về các biểu mẫu HTML. Và cách làm việc với chúng trong Express và NodeJS

Tham khảo thêm về NodeJS tại đây : Viết API Covid realtime bằng NodeJS

Tham khảo thêm về React tại đây: Hướng dẫn tạo login Form React Sử dụng Formik


Like it? Share with your friends!

1054