Skip to content

POST /contact has no rate limiting and interpolates unsanitized user input into HTML email body #111

Description

@anshul23102

Summary

POST /contact in server/routes/contactRoutes.js has two related weaknesses: it applies no rate limit, and it directly interpolates all user-supplied fields into an HTML-formatted email without any escaping or sanitization.

Affected code

const mailOptions = {
    from: email,   // caller-controlled
    to: process.env.EMAIL_RECEIVER || process.env.EMAIL_USER,
    html: `
        <p><strong>Name:</strong> ${name}</p>
        <p><strong>Email:</strong> ${email}</p>
        <p><strong>Category:</strong> ${category}</p>
        <p><strong>Message:</strong></p>
        <p>${message}</p>   <!-- unsanitized HTML -->
    `
};

Issues

1. No rate limiting

There is no per-IP or per-email rate limit. Any anonymous caller can POST to this endpoint in an unbounded loop and exhaust the Gmail sending quota, triggering Google's abuse rate limits and blocking legitimate contact submissions.

2. Unsanitized HTML interpolation in email body

The name, email, category, and message fields are used verbatim in an HTML template. A caller who submits:

{ "message": "<img src=x onerror='...'><a href='https://phishing.example.com'>Click here</a>" }

will have that HTML rendered inside the recipient's email client. This enables phishing links, misleading content, or tracking pixels to be delivered via the contact form to the maintainer's inbox.

Suggested Fix

  1. Add an express-rate-limit middleware to cap POST /contact to a small number of requests per IP (e.g., 5 per hour):
import rateLimit from 'express-rate-limit';
const contactLimiter = rateLimit({ windowMs: 60 * 60 * 1000, max: 5 });
router.post('/', contactLimiter, async (req, res) => { ... });
  1. Escape all user-supplied values before inserting them into the HTML template. Use a simple helper that replaces <, >, &, ", and ' with their HTML entities, or switch to a plain-text email body.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions