5 глупых ошибок сайтов, из-за которых утекают твои данные

Привет! Я баг-хантер, и за несколько месяцев работы я видел такое, что хочется то ли плакать, то ли смеяться. Разработчики делают ошибки, которые настолько очевидны, что мне стыдно за них. Буквально: один параметр в URL, одна забытая проверка - и вся база пользователей течёт как из дырявого ведра.

Сегодня расскажу про пять самых тупых, но самых распространённых ошибок, которые я нахожу снова и снова. Это не какие-то сложные 0-day уязвимости - это базовые косяки, которых можно избежать за 10 минут работы. Но их не избегают. И твои данные утекают. 💀

Если ты разработчик - проверь свой код прямо сейчас. Если обычный пользователь - узнаешь, как тебя сливают. Поехали! 🚀

Ошибка #1: IDOR — "А давайте не будем проверять права доступа!"

Что это такое:

IDOR (Insecure Direct Object Reference) - это когда сайт не проверяет, имеешь ли ты право видеть запрошенные данные. Меняешь цифру в URL — получаешь доступ к чужому профилю, заказу, документу.

Как это выглядит:

Ты заходишь в свой профиль: site.ru/profile?user_id=123
Меняешь на user_id=124 - видишь чужой профиль со всеми данными.

Или открываешь свой заказ: shop.ru/order?id=5001
Меняешь на id=5002 - видишь чужой заказ с адресом доставки, телефоном, ФИО.

Почему это происходит:

Разработчик подумал: "Ну никто же не догадается менять цифры в URL!" Догадаются, чувак. Я догадываюсь каждый раз. 🤦

Мой реальный кейс:

Проверял сайт интернет-магазина электроники (сеть по всей России). Оформил заказ, получил ссылку на отслеживание: site.ru/track?order_id=47853

Из любопытства изменил последнюю цифру на 47854. Бац - чужой заказ! Вижу:

  • ФИО покупателя
  • Полный адрес доставки
  • Номер телефона
  • Состав заказа (два iPhone 15 Pro)
  • Время доставки

Я попробовал ещё несколько ID - все открываются! База из тысяч заказов с личными данными - доступна любому.

Зарепортил. Они пофиксили за 2 дня - добавили проверку, что order_id принадлежит текущему пользователю. Буквально 5 строк кода. А до этого слили всю базу клиентов. 😱

Как проверить себя:

🔍 Зайди в свой профиль/заказ/документ
🔍 Посмотри на URL - есть там id=, user_id=, order=?
🔍 Измени цифру на соседнюю
🔍 Видишь чужие данные? IDOR подтверждён!

Что утекает:

  • Личные данные пользователей (имя, email, телефон, адрес)
  • История заказов
  • Медицинские карты (да, я такое находил!)
  • Финансовые документы
  • Переписки и сообщения

Почему это тупо:

Потому что фикс занимает 5 минут! Проверь на backend: if (order.user_id != current_user.id) return 403. Всё! Но нет, это "потом добавим".

Ошибка #2: API без авторизации — "Зачем проверять токены?"

Что это такое:

Сайт имеет API (для мобильного приложения или внутренних целей), но забыл закрыть его авторизацией. Любой может слать запросы и получать данные.

Как это выглядит:

Открываешь DevTools (F12), смотришь запросы. Видишь:

text

GET https://site.ru/api/v1/users/list Response: [{"id":1, "name":"Иван", "email":"ivan@mail.ru", "phone":"+7999..."}, ...]

Копируешь URL в браузер - работает! Без логина, без токена, без ничего. Просто берёшь и скачиваешь всю базу пользователей. 🎁

Почему это происходит:

API делали для мобильного приложения. "Это же внутренний endpoint, его никто не найдёт!" Я найду. Я всегда нахожу.

Мой легендарный случай:

Крупный образовательный портал (не буду светить, но они в топ-5 по России). Тестировал их сайт, открыл Network в DevTools.

Вижу запрос: https://portal.ru/api/students/export?format=json

Копирую URL, открываю в новой вкладке. И что я вижу? JSON-файл с данными 50,000+ студентов:

[
{
"id": 1,
"full_name": "Иванов Иван Иванович",
"email": "ivanov@example.com",
"phone": "+79991234567",
"birth_date": "2005-03-15",
"address": "г. Москва, ул. Ленина, д. 10, кв. 5",
"course": "Backend разработка",
"payment_status": "paid"
},
...
]

Никакой авторизации! Никаких токенов! Просто открытый endpoint, который возвращает ВСЮ базу студентов с персональными данными!

Представляешь, сколько времени этот endpoint висел открытым? Может, месяцы? Может, кто-то уже слил эти данные и продал? Страшно подумать. 😨

Как проверить:

🔍 Открой DevTools → Network
🔍 Поищи запросы к /api/
🔍 Скопируй URL любого запроса
🔍 Открой в режиме инкогнито (без твоих cookies)
🔍 Работает? API не защищён!

Что утекает:

  • База пользователей целиком
  • Финансовые данные
  • Внутренняя статистика
  • Админские функции (если API позволяет POST/DELETE)

Почему это тупо:

Потому что авторизация API - это база основ! Middleware с проверкой JWT-токена - 10 строк кода. Но "некогда было, дедлайн горел".

Ошибка #3: Пароли в открытом виде — "Зачем нам шифрование?"

Что это такое:

Сайт хранит пароли в базе данных открытым текстом или использует слабое шифрование (MD5 без соли, SHA1). При утечке БД все пароли сразу известны.

Как это обнаруживается:

Обычно при утечке базы данных или через функцию "Восстановить пароль". Если сайт присылает тебе твой старый пароль на email - значит, он его знает. Значит, хранит открытым. 🚨

Почему это происходит:

"Зачем усложнять? Просто сохраним пароль как есть!" Или: "MD5 же тоже хеш, пойдёт". Нет, не пойдёт. MD5 взламывается мгновенно.

Мой случай (не совсем баг-хантинг, но показательно):

Регистрировался на сайте онлайн-курсов. Придумал сложный пароль: Kz8#mP2$qL9@wX4!. Через 10 минут получаю email:

Добро пожаловать!
Ваш логин: myemail@mail.ru
Ваш пароль: Kz8#mP2$qL9@wX4!

Я в шоке. Они ОТПРАВИЛИ МНЕ ПАРОЛЬ НА EMAIL! Значит:

  1. Они хранят пароли открытым текстом
  2. Их база данных - это склад паролей для хакеров
  3. При утечке - все пароли компрометированы

Я сразу удалил аккаунт и написал им гневное письмо. Ответили: "Мы так делаем для удобства пользователей". Удобство? СЕРЬЁЗНО? 🤬

Правильный подход:

Пароли нужно хешировать с солью алгоритмом bcrypt, argon2 или минимум PBKDF2. При восстановлении пароля присылать ТОКЕН для создания НОВОГО пароля, а не старый пароль.

Как проверить:

🔍 Используй функцию "Забыли пароль?"
🔍 Если сайт прислал твой СТАРЫЙ пароль — беги оттуда
🔍 Если прислал ссылку для создания нового — норм

Что утекает при взломе БД:

  • Все пароли пользователей
  • Доступ к их аккаунтам на других сайтах (если пароли одинаковые)
  • Возможность credential stuffing атак

Почему это тупо:

Это 2025 год! Хеширование паролей - это стандарт безопасности с 1990-х! Не делать этого - это как строить дом без фундамента.

Ошибка #4: Отсутствие Rate Limiting — "Пусть хоть миллион раз попробует!"

Что это такое:

Сайт не ограничивает количество попыток выполнить действие. Можно пробовать пароли, коды подтверждения, промокоды - хоть миллион раз подряд.

Как это выглядит:

Форма логина позволяет пробовать пароли бесконечно. Или форма ввода SMS-кода позволяет перебирать все 10,000 комбинаций (0000-9999).

Почему это происходит:

"Ну какова вероятность, что кто-то будет перебирать?" Высокая. Я перебираю постоянно.

Мой жёсткий кейс:

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

Я проверил - нет rate limiting! Можно отправлять запросы с разными кодами бесконечно. Написал простой скрипт:

for code in range(0000, 10000):
response = requests.post(url, json={"phone": "+79991234567", "code": str(code).zfill(4)})
if "success" in response.text:
print(f"Код найден: {code}")
break

Запустил. За 3 минуты перебрал все 10,000 комбинаций и нашёл правильный код. Я получил доступ к чужому аккаунту, не имея доступа к его телефону!

Ещё один пример:

Сайт с промокодами. Промокоды типа SALE2025, NEWYEAR, DISCOUNT. Я написал скрипт, который перебирает популярные слова и комбинации.

За час нашёл рабочие промокоды на скидку 50%, 70%, даже 100% (бесплатная доставка + товар). Сайт не ограничивал попытки, промокоды были простые и предсказуемые.

Зарепортил. Они поняли, что так делать нельзя. Сделали rate limiting и усложнили промокоды (теперь случайные буквы/цифры).

Как проверить:

🔍 Найди форму с подтверждением (SMS-код, пароль, промокод)
🔍 Попробуй ввести неправильные данные 10-20 раз подряд
🔍 Если форма не блокируется - rate limiting отсутствует

Что утекает:

  • Доступ к аккаунтам через перебор SMS-кодов
  • Взлом паролей через brute force
  • Бесплатные промокоды и скидки
  • Массовая регистрация ботов

Почему это тупо:

Rate limiting - это одна middleware функция! Библиотеки есть для любого языка/фреймворка. Не добавить это - преступная халатность.

Ошибка #5: Verbose Error Messages — "Давайте расскажем хакеру всё!"

Что это такое:

Сайт выдаёт детальные сообщения об ошибках, которые помогают хакеру понять структуру системы и найти уязвимости.

Как это выглядит:

Пробуешь SQL-инъекцию, и сайт отвечает:

MySQL Error: You have an error in your SQL syntax near 'SELECT * FROM users WHERE id='5'' at line 1
Query: SELECT * FROM users WHERE id='5' LIMIT 1
File: /var/www/html/controllers/UserController.php, line 47

Или вводишь несуществующий email при логине:

Error: User with email 'test@example.com' not found in database

А существующий:

Error: Invalid password for user 'real@example.com'

Теперь хакер знает, какие email'ы есть в базе, а какие нет. Можно составить список реальных пользователей.

Почему это происходит:

Debug-режим забыли выключить на продакшене. "Нам же удобно видеть ошибки!" Да, и хакерам тоже удобно.

Мой кейс:

Тестировал банковский сервис (микрозаймы). Попробовал SQL-инъекцию в параметре поиска. Получил:

Database Error: 
SQLSTATE[42000]: Syntax error or access violation: 1064
You have an error in your SQL syntax;
Query: SELECT * FROM clients WHERE phone LIKE '%+7999%' AND status='active'
Database: production_db
Host: 10.0.5.23
User: db_admin

Это ЗОЛОТО для хакера! Я узнал:

  • Название базы данных: production_db
  • Внутренний IP: 10.0.5.23
  • Username для БД: db_admin
  • Структуру SQL-запроса
  • Названия таблиц и колонок

Правильный подход:

Пользователю показывать: Something went wrong. Please try again later.

В логи писать: детальную ошибку, стек трейс, все параметры - но только в логи, недоступные извне.

Как проверить:

🔍 Попробуй намеренно вызвать ошибку (несуществующая страница, некорректные данные в форме)
🔍 Смотри, что сайт отвечает
🔍 Если видишь SQL-запросы, пути к файлам, стек трейсы - verbose errors включены

Что утекает:

  • Структура базы данных
  • Пути к файлам на сервере
  • Используемые технологии и версии
  • Внутренние IP-адреса
  • Список реальных пользователей

Почему это тупо:

Потому что это одна строка в конфиге: DEBUG = False. Одна. Строка. Но её забывают менять. Каждый. Раз.

Бонус: Что делать, если твои данные уже утекли?

Окей, ты прочитал про все эти ошибки. Возможно, сайты, которыми ты пользуешься, имеют эти косяки. Что делать?

Проверь, утекали ли твои данные:

🔍 Зайди на haveibeenpwned.com
🔍 Введи свой email
🔍 Посмотри, в каких утечках он засветился

У меня, например, 12 утечек. Из них 5 - российские сайты. LinkedIn, Adobe, старые форумы... Грустно, но такова реальность.

Что делать после обнаружения утечки:

✅ Смени пароли на всех сайтах, где использовал этот email
✅ Используй менеджер паролей (Bitwarden, 1Password)
✅ Включи 2FA везде, где возможно
✅ Мониторь свои банковские карты на подозрительные операции
✅ Будь бдителен к фишингу (утекшие данные используют для таргетированных атак)

Заключение (мои мысли после сотен проверок)

Я нашёл эти ошибки в десятках, нет, сотнях сайтов. От небольших интернет-магазинов до федеральных порталов. Большие, маленькие, известные, неизвестные - все косячат.

И знаешь, что меня бесит больше всего? Это всё легко фиксится!

  • IDOR - добавить проверку прав доступа (5 минут)
  • Незащищённое API - добавить middleware с авторизацией (10 минут)
  • Пароли открытым текстом - использовать bcrypt (1 час на рефакторинг)
  • Отсутствие rate limiting - подключить библиотеку (15 минут)
  • Verbose errors - DEBUG = False (10 секунд!)

Но нет. "Потом сделаем", "Некогда", "Работает же". А потом утечка, штраф от Роскомнадзора, репутация в помойке.

Если ты разработчик - проверь свой код прямо сейчас. Серьёзно. Отложи всё и проверь эти пять пунктов. Это важнее новых фич.

Если ты обычный пользователь - будь бдителен. Используй сложные уникальные пароли, включай 2FA, проверяйся на haveibeenpwned. Твои данные - твоя ответственность.

А я продолжу искать эти тупые ошибки. Пока разработчики их делают - я буду зарабатывать. И честно говоря, работы мне хватит на всю жизнь. 😅🔥

13 views·1 share