🛡️ Sentinel: Enhance email security with strict validation and sanitization #39
@@ -17,3 +17,8 @@
|
||||
**Vulnerability:** Standard email regexes and HTML5 validation are often too permissive, allowing XSS vectors (like `<script>`) in email fields if not properly sanitized/rejected.
|
||||
**Learning:** While HTML5 browsers block some invalid emails, relying solely on them is insufficient for defense-in-depth. Application-level validation should explicitly reject dangerous characters (`<`, `>`) to prevent stored XSS or injection if the data is processed by less-secure backends.
|
||||
**Prevention:** Implement strict, reusable validation functions (`isValidEmail`) that reject XSS vectors, and ensure tests verify this logic by bypassing browser validation if necessary.
|
||||
|
||||
## 2026-02-14 - Strict Email Regex vs RFC Compliance
|
||||
**Vulnerability:** Standard email validation often allows characters like quotes (`"`) or backticks (`` ` ``) which can be vectors for XSS in HTML attribute contexts.
|
||||
**Learning:** While RFCs allow quoted local parts (e.g., `"user"@example.com`), these are rare in practice and pose significant security risks in web applications. It is often safer to explicitly reject them. However, apostrophes (`'`) are common in names (O'Connor) and should be allowed, relying on sanitization (Defense in Depth) rather than validation to handle them safely.
|
||||
**Prevention:** Use a hybrid approach: Strict validation (reject quotes/backticks) for high-risk characters, coupled with output sanitization (`sanitizeInput`) for characters that must be allowed but are still risky (apostrophes).
|
||||
|
||||
@@ -93,7 +93,7 @@ export function Contact() {
|
||||
try {
|
||||
const templateParams = {
|
||||
name: sanitizeInput(formData.name),
|
||||
email: formData.email, // Email doesn't typically need HTML sanitization if validated by regex, but good practice to handle it if used in HTML context.
|
||||
email: sanitizeInput(formData.email),
|
||||
title: sanitizeInput(formData.subject),
|
||||
message: sanitizeInput(formData.message),
|
||||
reply_to: formData.email,
|
||||
|
||||
@@ -48,6 +48,16 @@ describe('Security Utils', () => {
|
||||
expect(isValidEmail('user<name>@example.com')).toBe(false);
|
||||
});
|
||||
|
||||
it('rejects emails with double quotes and backticks', () => {
|
||||
expect(isValidEmail('user"name@example.com')).toBe(false);
|
||||
expect(isValidEmail('user`name@example.com')).toBe(false);
|
||||
});
|
||||
|
||||
it('accepts emails with apostrophes', () => {
|
||||
expect(isValidEmail("user'name@example.com")).toBe(true);
|
||||
expect(isValidEmail("o'connor@example.com")).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects emails with whitespace', () => {
|
||||
expect(isValidEmail('user @example.com')).toBe(false);
|
||||
expect(isValidEmail('user@ example.com')).toBe(false);
|
||||
|
||||
@@ -31,6 +31,6 @@ export function isValidEmail(email: string): boolean {
|
||||
// [^\s@<>]+ : Domain part - no whitespace, @, <, or >
|
||||
// \. : Literal .
|
||||
// [^\s@<>]+ : TLD part - no whitespace, @, <, or >
|
||||
const emailRegex = /^[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+$/;
|
||||
const emailRegex = /^[^\s@<>,"`]+@[^\s@<>,"`]+\.[^\s@<>,"`]+$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user