HTTP-заголовки используются для передачи метаданных запроса: информации об авторизации, типе содержимого, токенах, языке, пользовательском агенте и других параметрах. Некорректные или отсутствующие заголовки могут привести к ошибкам обработки запросов, уязвимостям безопасности и нарушениям API-контрактов.
Библиотека Validator.js позволяет выполнять валидацию значений заголовков при помощи набора встроенных функций.
Для установки используется пакетный менеджер npm:
npm install validator
Подключение библиотеки:
const validator = require('validator');
Для ES-модулей:
import validator from 'validator';
Заголовок Content-Type определяет тип передаваемых
данных.
Пример запроса:
Content-Type: application/json
Проверка значения:
const contentType = 'application/json';
const allowedTypes = [
'application/json',
'multipart/form-data',
'text/plain'
];
const isValid = allowedTypes.includes(contentType);
console.log(isValid);
Проверка на пустое значение:
const contentType = '';
if (validator.isEmpty(contentType)) {
console.log('Заголовок пуст');
}
Проверка строкового типа:
const contentType = 'application/json';
if (validator.isString(contentType)) {
console.log('Корректная строка');
}
Заголовок Authorization обычно содержит токены
доступа.
Пример:
Authorization: Bearer eyJhbGciOi...
const authHeader = 'Bearer abc123token';
const bearerRegex = /^Bearer\s[\w-]+$/;
const isValid = bearerRegex.test(authHeader);
console.log(isValid);
Validator.js содержит метод isJWT().
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9';
const isValid = validator.isJWT(token);
console.log(isValid);
const authHeader = 'Bearer my_secure_token';
const token = authHeader.split(' ')[1];
console.log(token);
const authHeader = 'Bearer abc123';
if (validator.isAscii(authHeader)) {
console.log('ASCII-символы допустимы');
}
Заголовок Host указывает доменное имя сервера.
Пример:
Host: api.example.com
Проверка домена:
const host = 'api.example.com';
const isValid = validator.isFQDN(host);
console.log(isValid);
Проверка IP-адреса:
const host = '192.168.0.1';
const isValid = validator.isIP(host);
console.log(isValid);
Проверка localhost:
const host = 'localhost';
const isValid =
host === 'localhost' ||
validator.isIP(host) ||
validator.isFQDN(host);
console.log(isValid);
Заголовок User-Agent содержит сведения о клиенте.
Пример:
User-Agent: Mozilla/5.0
Слишком длинные значения могут использоваться в атаках.
const userAgent = 'Mozilla/5.0';
const isValid = validator.isLength(userAgent, {
min: 3,
max: 300
});
console.log(isValid);
const userAgent = 'Mozilla/5.0';
const hasInvalidChars = /[\r\n\t]/.test(userAgent);
console.log(!hasInvalidChars);
const userAgent = 'Mozilla/5.0';
console.log(validator.isAscii(userAgent));
Заголовок Origin активно используется в CORS.
Пример:
Origin: https://example.com
const origin = 'https://example.com';
const isValid = validator.isURL(origin, {
protocols: ['http', 'https'],
require_protocol: true
});
console.log(isValid);
const allowedOrigins = [
'https://example.com',
'https://api.example.com'
];
const origin = 'https://example.com';
const isAllowed = allowedOrigins.includes(origin);
console.log(isAllowed);
Заголовок Referer содержит URL страницы-источника.
const referer = 'https://example.com/page';
const isValid = validator.isURL(referer);
console.log(isValid);
Проверка HTTPS:
const referer = 'https://example.com/page';
const isSecure = referer.startsWith('https://');
console.log(isSecure);
Некоторые заголовки содержат числа:
Content-Length: 2048
const contentLength = '2048';
const isValid = validator.isInt(contentLength, {
min: 0,
max: 10000000
});
console.log(isValid);
const length = parseInt(contentLength, 10);
console.log(length);
Пример:
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
Проверка даты:
const dateHeader = 'Wed, 21 Oct 2015 07:28:00 GMT';
const isValid = validator.isDate(dateHeader);
console.log(isValid);
Пример:
Accept-Language: en-US
Проверка локали:
const locale = 'en-US';
const isValid = validator.isLocale(locale);
console.log(isValid);
Проверка списка языков:
const languages = 'en-US,en;q=0.9,ru;q=0.8';
const items = languages.split(',');
for (const item of items) {
console.log(item.trim());
}
ETag используется для кеширования.
Пример:
ETag: "abc123"
Проверка строки:
const etag = '"abc123"';
const isValid = validator.isLength(etag, {
min: 3,
max: 100
});
console.log(isValid);
Проверка формата:
const etagRegex = /^W\/"[\w.-]+"$|^"[\w.-]+"$/;
console.log(etagRegex.test(etag));
Пример:
X-API-Key: sk_test_123456
const apiKey = 'sk_test_123456';
const isValid = validator.isLength(apiKey, {
min: 10,
max: 128
});
console.log(isValid);
const apiKey = 'sk_test_123456';
const isValid = validator.matches(
apiKey,
/^[a-zA-Z0-9_-]+$/
);
console.log(isValid);
Пользовательские заголовки обычно имеют префикс X-.
Пример:
X-Client-Version: 1.4.2
Проверка семантической версии:
const version = '1.4.2';
const isValid = validator.isSemVer(version);
console.log(isValid);
const validator = require('validator');
function validateHeaders(req, res, next) {
const auth = req.headers.authorization;
const origin = req.headers.origin;
const contentType = req.headers['content-type'];
if (!auth || !/^Bearer\s[\w-]+$/.test(auth)) {
return res.status(400).json({
error: 'Некорректный Authorization'
});
}
if (
origin &&
!validator.isURL(origin, {
require_protocol: true
})
) {
return res.status(400).json({
error: 'Некорректный Origin'
});
}
const allowedTypes = [
'application/json'
];
if (!allowedTypes.includes(contentType)) {
return res.status(400).json({
error: 'Недопустимый Content-Type'
});
}
next();
}
Использование middleware:
const express = require('express');
const app = express();
app.use(validateHeaders);
Validator.js содержит методы очистки строк.
const header = ' application/json ';
const clean = validator.trim(header);
console.log(clean);
const header = '<script>alert(1)</script>';
const safe = validator.escape(header);
console.log(safe);
const header = 'APPLICATION/JSON';
const normalized = header.toLowerCase();
console.log(normalized);
Header Injection — атака, связанная с внедрением управляющих символов.
Опасный пример:
const value = 'test\r\nX-Injected: true';
Проверка:
function hasHeaderInjection(value) {
return /[\r\n]/.test(value);
}
console.log(hasHeaderInjection(value));
Безопасная обработка:
function sanitizeHeader(value) {
return value.replace(/[\r\n]+/g, '');
}
console.log(sanitizeHeader(value));
const requiredHeaders = [
'authorization',
'content-type'
];
function validateRequiredHeaders(headers) {
return requiredHeaders.every(
header => headers[header]
);
}
Пример использования:
const headers = {
authorization: 'Bearer token',
'content-type': 'application/json'
};
console.log(validateRequiredHeaders(headers));
const validators = {
authorization: value =>
/^Bearer\s[\w-]+$/.test(value),
origin: value =>
validator.isURL(value, {
require_protocol: true
}),
'content-type': value =>
['application/json'].includes(value),
'content-length': value =>
validator.isInt(value, {
min: 0
})
};
Проверка всех заголовков:
function validateHeaders(headers) {
const errors = [];
for (const key in validators) {
const validate = validators[key];
const value = headers[key];
if (value && !validate(value)) {
errors.push(`Ошибка заголовка: ${key}`);
}
}
return errors;
}
Использование:
const result = validateHeaders({
authorization: 'Bearer token',
origin: 'https://example.com',
'content-type': 'application/json',
'content-length': '120'
});
console.log(result);
Слишком большие заголовки могут приводить к переполнению памяти и DoS-атакам.
function validateSize(value) {
return validator.isLength(value, {
max: 1024
});
}
Предпочтительно явно разрешать допустимые значения:
const allowed = [
'application/json',
'text/plain'
];
allowed.includes(value);
validator.isAscii(headerValue);
HTTP-заголовки нечувствительны к регистру.
const normalizedHeaders = {};
for (const key in headers) {
normalizedHeaders[key.toLowerCase()] =
headers[key];
}
try {
validateHeaders(req.headers);
} catch (error) {
console.error(error.message);
}
Полный пример:
const express = require('express');
const validator = require('validator');
const app = express();
app.use((req, res, next) => {
const auth = req.headers.authorization;
if (!auth) {
return res.status(401).json({
error: 'Authorization required'
});
}
if (!/^Bearer\s[\w-]+$/.test(auth)) {
return res.status(400).json({
error: 'Invalid token format'
});
}
const userAgent = req.headers['user-agent'];
if (
userAgent &&
!validator.isLength(userAgent, {
min: 3,
max: 300
})
) {
return res.status(400).json({
error: 'Invalid User-Agent'
});
}
next();
});
app.get('/', (req, res) => {
res.send('OK');
});
app.listen(3000);