API8 мин чтения

Как правильно кодировать параметры запроса в REST API

Параметры запроса в REST API должны быть закодированы через percent-encoding согласно RFC 3986, чтобы безопасно передавать специальные символы, такие как &, =, пробелы и Unicode. Используйте encodeURIComponent() в JavaScript, urllib.parse.quote() в Python или URLEncoder.encode() в Java.

Зачем параметрам запроса нужно кодирование

Строки запроса в REST API используют структурированный формат, где пары «ключ-значение» разделяются символом &, а ключи отделяются от значений символом =. Если значение параметра содержит любой из этих символов-разделителей или другие зарезервированные символы, такие как #, + или пробелы, структура строки запроса нарушается, и сервер получает некорректные данные.

Рассмотрим такой пример: вы хотите найти «salt & pepper» через API. Без кодирования URL /search?q=salt & pepper сообщает серверу, что есть два параметра: q=salt и pepper (без значения). При правильном кодировании /search?q=salt%20%26%20pepper корректно передаёт единственный параметр q со значением «salt & pepper».

Помимо корректности, правильное кодирование также предотвращает уязвимости безопасности. Незакодированный пользовательский ввод в URL может привести к атакам через инъекции, отравлению кеша и другим эксплойтам. Всегда кодируйте значения параметров, прежде чем включать их в URL.

Кодирование в разных языках

Каждый крупный язык программирования предоставляет встроенные функции для URL-кодирования. Вот как правильно кодировать параметры запроса в самых популярных языках.

// JavaScript
// Используйте encodeURIComponent() для отдельных значений параметров
const query = 'price >= 100 & category = books';
const url = 'https://api.example.com/search?q=' + encodeURIComponent(query);
// "https://api.example.com/search?q=price%20%3E%3D%20100%20%26%20category%20%3D%20books"

// Либо используйте URLSearchParams для нескольких параметров
const params = new URLSearchParams({
  q: 'price >= 100 & category = books',
  page: '1',
  sort: 'price'
});
const url2 = 'https://api.example.com/search?' + params.toString();
# Python
from urllib.parse import urlencode, quote

# Использование urlencode для нескольких параметров
params = {
    'q': 'price >= 100 & category = books',
    'page': 1,
    'sort': 'price'
}
query_string = urlencode(params)
url = f'https://api.example.com/search?{query_string}'

# Использование quote для одного значения
value = quote('price >= 100 & category = books', safe='')
url = f'https://api.example.com/search?q={value}'
// Java
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

String query = "price >= 100 & category = books";
String encoded = URLEncoder.encode(query, StandardCharsets.UTF_8);
// "price+%3E%3D+100+%26+category+%3D+books"
// Обратите внимание: URLEncoder использует + для пробелов (form encoding)

// Для кодирования по RFC 3986 замените + на %20
String rfc3986 = encoded.replace("+", "%20");
String url = "https://api.example.com/search?q=" + rfc3986;
// C#
using System.Net;
using System.Web;

string query = "price >= 100 & category = books";

// Uri.EscapeDataString следует RFC 3986
string encoded = Uri.EscapeDataString(query);
// "price%20%3E%3D%20100%20%26%20category%20%3D%20books"

// HttpUtility.UrlEncode использует form encoding (+ для пробелов)
string formEncoded = HttpUtility.UrlEncode(query);
// "price+%3e%3d+100+%26+category+%3d+books"
// Go
package main

import (
    "fmt"
    "net/url"
)

func main() {
    // url.QueryEscape кодирует для строк запроса (+ для пробелов)
    encoded := url.QueryEscape("price >= 100 & category = books")
    // "price+%3E%3D+100+%26+category+%3D+books"

    // url.PathEscape кодирует для сегментов пути (%20 для пробелов)
    pathEncoded := url.PathEscape("my file name.pdf")
    // "my%20file%20name.pdf"

    // Использование url.Values для построения строк запроса
    params := url.Values{}
    params.Set("q", "price >= 100 & category = books")
    params.Set("page", "1")
    queryString := params.Encode()
    fmt.Println(queryString)
}

Кодирование массивов и вложенных объектов

REST API часто должны принимать массивы или вложенные объекты в качестве параметров запроса. Единого стандарта для кодирования таких сложных типов не существует, поэтому разные API используют разные соглашения.

// Повторяющиеся ключи (наиболее широко поддерживаемый вариант)
// GET /api/items?tag=javascript&tag=python&tag=go
const params = new URLSearchParams();
['javascript', 'python', 'go'].forEach(tag => params.append('tag', tag));

// Скобочная нотация (распространена в PHP, Rails, Express с qs)
// GET /api/items?tags[]=javascript&tags[]=python&tags[]=go
// Требует ручного кодирования:
const tags = ['javascript', 'python', 'go'];
const qs = tags.map(t => 'tags[]=' + encodeURIComponent(t)).join('&');

// Через запятую (просто, но ограниченно)
// GET /api/items?tags=javascript,python,go
const tagList = encodeURIComponent('javascript,python,go');

// Вложенные объекты (скобочная нотация)
// GET /api/search?filter[status]=active&filter[min_price]=10
// В Python:
params = {
    'filter[status]': 'active',
    'filter[min_price]': '10'
}

Всегда сверяйтесь с документацией вашего API на предмет ожидаемого формата. При проектировании нового API подход с повторяющимися ключами наиболее переносим между языками программирования и HTTP-библиотеками.

Рекомендации по декодированию на стороне сервера

При обработке входящих запросов на стороне сервера большинство веб-фреймворков автоматически декодируют параметры запроса за вас. Тем не менее есть важные моменты, которые следует учитывать для надёжной обработки на стороне сервера.

  • Большинство фреймворков (Express, Django, Spring, ASP.NET) декодируют параметры запроса автоматически. Не декодируйте их вручную повторно, иначе вы получите двойное декодирование.
  • Проверяйте декодированные значения на соответствие ожидаемым типам и диапазонам, прежде чем использовать их.
  • Устанавливайте ограничения на максимальную длину URL на уровне веб-сервера (обычно 2048 или 8192 символов).
  • Учитывайте случай, когда + может означать либо пробел, либо буквальный знак плюс — в зависимости от типа контента.
  • Логируйте исходный (закодированный) URL для отладки, поскольку декодированные значения в логах могут вводить в заблуждение.
// Express.js — параметры декодируются автоматически
app.get('/search', (req, res) => {
  const query = req.query.q; // Уже декодировано
  // НЕ делайте так: decodeURIComponent(req.query.q)

  // Проверьте ввод
  if (typeof query !== 'string' || query.length > 200) {
    return res.status(400).json({ error: 'Invalid query parameter' });
  }

  // Безопасно используйте декодированное значение
  const results = search(query);
  res.json(results);
});

Вопросы безопасности

Правильное кодирование параметров запроса — ключевая часть безопасности API. Вот важнейшие практики безопасности, которых следует придерживаться.

  • Никогда не доверяйте кодированию на стороне клиента. Всегда проверяйте и очищайте декодированные параметры на сервере, даже если клиент закодировал их правильно.
  • Остерегайтесь атак с двойным кодированием. Злоумышленник может закодировать вредоносные символы дважды, чтобы обойти входные фильтры, которые декодируют только один раз. Убедитесь, что ваши фильтры безопасности видят полностью декодированные значения.
  • Предотвращайте обход каталогов (path traversal). Закодированные последовательности вроде %2e%2e%2f (../) в параметрах пути могут использоваться для доступа к файлам за пределами предусмотренного каталога. Декодируйте и проверяйте пути перед операциями с файлами.
  • Используйте параметризованные запросы. Даже после URL-декодирования никогда не подставляйте пользовательский ввод напрямую в SQL- или NoSQL-запросы. Используйте параметризованные запросы или ORM.
  • Ограничивайте длину URL. Чрезмерно длинные строки запроса могут вызвать отказ в обслуживании. Настройте веб-сервер так, чтобы он отклонял URL, превышающие разумную длину.
  • Кодируйте и вывод. Когда вы отражаете значения параметров запроса обратно в HTML-ответах, применяйте HTML-кодирование сущностей, чтобы предотвратить атаки межсайтового скриптинга (XSS).

Похожие статьи

Попробуйте наши бесплатные инструменты