Вопросы на собеседовании: Инженер по безопасности приложений
100 реальных вопросов с образцовыми ответами и пояснениями для уровня Junior.
Смотреть пример резюме: Инженер по безопасности приложений →Тренировка флешкарточками
Интервальное повторение · Hunter Pass
Вопросы
Я прослежу недоверенное значение от источника через все преобразования до точки исходящего сетевого запроса, прежде чем решать, есть ли уязвимость.
- Отмечу `req.query.url` как источник, потому что клиент может менять query-параметры в обход обычного интерфейса.
- Прослежу присваивания, вызовы помощников, парсинг, валидацию и редиректы до `fetch`, Axios или другого API исходящих запросов.
- Зафиксирую защитные проверки на пути, затем проверю, ограничивают ли они конечный адрес после DNS-разрешения и редиректов, а не только исходную строку.
Зачем это спрашивают: Интервьюер проверяет, умеете ли вы разбирать конкретный путь от источника до sink, а не судить об одной строке изолированно.
Я буду считать недоверенным любое значение под внешним контролем, даже если фреймворк уже его распарсил.
- Пути запроса, query-параметры, тело, заголовки и cookie напрямую контролируются клиентом.
- Строки базы, сообщения очереди, загруженные файлы и ответы партнерского API остаются недоверенными, если на них может влиять другой пользователь или система.
- TypeScript-тип или JSON-парсер меняет представление, но не доказывает безопасность или авторизованность значения.
Зачем это спрашивают: Сильный ответ учитывает косвенные источники и не путает парсинг или типизацию с доверием.
Sink представляет собой операцию, при которой подконтрольные атакующему данные могут получить опасную интерпретацию или повлиять на защищенный ресурс.
- Выполнение SQL, запуск shell-процесса, рендеринг шаблона и `innerHTML` могут интерпретировать данные как код или синтаксис.
- Запись в файловую систему, редиректы, исходящие HTTP-запросы и десериализаторы могут пересекать важные границы приложения.
- Одно значение может быть безвредным в логе и опасным в shell-команде, поэтому я классифицирую sink по реальному API и контексту.
Зачем это спрашивают: Интервьюер оценивает, находите ли вы опасные операции по поведению и контексту, а не по именам переменных.
Валидация решает, соответствуют ли данные правилам приложения, а кодирование вывода не дает допустимым данным стать синтаксисом в месте назначения.
- Комментарий может законно содержать `<`, кавычки или амперсанды, поэтому запрет всех специальных символов испортит обычный текст.
- При выводе в HTML-текст шаблонизатор должен кодировать символы для этого HTML-контекста.
- Если то же значение попадает в URL или JavaScript-строку, ему нужен кодировщик или безопасный API для другого контекста.
Зачем это спрашивают: Сильный ответ отделяет бизнес-валидацию от контекстной защиты в sink.
Я сопоставлю небольшой набор публичных вариантов сортировки с фиксированными SQL-идентификаторами вместо вставки строки клиента в запрос.
- Например, `date` сопоставлю с `created_at`, а `total` с `total_cents`, отклоняя остальные значения.
- Плейсхолдеры базы связывают значения, но не имена таблиц или столбцов, поэтому одной параметризации здесь недостаточно.
- Такое сопоставление также закрывает случайный доступ к внутренним столбцам с допустимыми SQL-именами.
Зачем это спрашивают: Интервьюер проверяет, понимаете ли вы уместность allowlist и ограничения привязки значений.
API должен сам применять правило ролей, потому что клиент может обойти React-форму и отправить запрос напрямую.
- На сервере нужно сверять значение с точным набором ролей для этой операции, а не со всеми известными системе ролями.
- Отдельно нужно авторизовать назначающего роль, потому что допустимое значение `admin` все равно может быть запрещено этому пользователю.
- Следует добавить API-тест, где обычный пользователь отправляет `admin` и неизвестное значение и получает отказ.
Зачем это спрашивают: Интервьюер проверяет, размещаете ли вы валидацию и авторизацию на серверной границе.
Граница доверия появляется при каждом переходе между компонентами с разными идентичностями, правами или контролем над данными.
- Ввод браузера переходит в API, поэтому API независимо от интерфейса аутентифицирует, валидирует и авторизует его.
- Задание из очереди переходит от API к worker, поэтому worker проверяет форму сообщения и не исходит из того, что каждый отправитель всегда формирует корректные сообщения.
- API и worker входят в базу под сервисными учетными записями, права которых должны соответствовать их разным задачам.
Зачем это спрашивают: Сильный ответ размещает конкретные проверки и идентичности на внутренних и внешних границах.
Я разделю возможности так, чтобы код отчетов не мог удалять аккаунты или менять посторонние записи.
- Для отчетов дам отдельную учетную запись базы с доступом только к нужным представлениям или операциям `SELECT`.
- Удаление помещу за отдельный сервисный метод и проверку авторизации, не выдавая каждому модулю универсальный клиент базы.
- Фоновые конвертеры и парсеры запущу под непривилегированным пользователем ОС с доступом только к рабочему каталогу.
Зачем это спрашивают: Интервьюер оценивает, переводите ли вы наименьшие привилегии в конкретные границы приложения, базы и процесса.
Я верну клиенту стабильную общую ошибку, а полезные детали оставлю в закрытых серверных логах.
- Ответ не должен содержать SQL, имена таблиц, пути файловой системы, кадры стека и значения окружения.
- Исключение нужно залогировать с correlation ID, маршрутом запроса и безопасным контекстом для расследования разработчиками.
- Ожидаемые ошибки вроде повторного email следует преобразовать в отдельный безопасный ответ вместо утечки исходного исключения базы.
Зачем это спрашивают: Сильный ответ сохраняет возможность отладки, не отдавая внутренности приложения пользователю.
Проверка безопасности должна применяться к тому же нормализованному значению, которое использует чувствительная операция.
- Проверка пути до URL-декодирования может пропустить `%2e%2e/`, хотя файловая система позже получит `../`.
- Нужно один раз распарсить и нормализовать значение библиотекой платформы, отклонить неверные или неоднозначные формы и использовать проверенный результат.
- Повторного декодирования следует избегать, потому что двойная кодировка может изменить смысл уже после первой проверки.
Зачем это спрашивают: Интервьюер проверяет понимание ошибок порядка валидации из-за разных представлений данных.
Код смешивает подконтрольные клиенту данные с SQL-синтаксисом, поэтому email может изменить структуру запроса.
- Конкатенацию нужно заменить плейсхолдером драйвера, например `WHERE email = $1`, и передать `email` в массиве значений.
- Не следует чинить это ручным экранированием кавычек, потому что режимы базы и крайние случаи делают его ненадежным.
- Регрессионный тест с email, содержащим кавычку, должен подтвердить, что он остается одним связанным значением и не меняет выражение.
Зачем это спрашивают: Интервьюер ожидает исправление через параметризацию для конкретного языка и тест разделения кода и данных.
Клиент может внедрить операторы MongoDB вместо простых значений полей, ожидаемых приложением.
- Ввод вроде `{"password": {"$ne": null}}` меняет смысл запроса, если произвольный объект попадает в `find`.
- Следует построить новый фильтр из проверенных скалярных полей, например `{ email: validatedEmail }`, а не пересылать объект запроса.
- Ключи с `$` стоит отклонять как дополнительную защиту, но не вместо явной схемы.
Зачем это спрашивают: Сильный ответ узнает инъекцию операторов и строит безопасный объект запроса вместо расплывчатой очистки JSON.
Я уберу shell и передам в `subprocess.run` фиксированный исполняемый файл с отдельными аргументами.
- Список вроде `["convert", input_path, output_path]` с `shell=False` оставляет метасимволы внутри одного аргумента имени файла.
- Пути должен генерировать сервер, применяя правила файлов до запуска процесса.
- Конвертер нужно запускать с низкими правами ОС, таймаутом и лимитами ресурсов для сдерживания сбоев парсера.
Зачем это спрашивают: Интервьюер проверяет, убираете ли вы интерпретацию команд и добавляете практическое ограничение дочернего процесса.
Приложение считает пользовательский ввод исходником Jinja-шаблона, поэтому опасные выражения шаблона могут раскрыть данные или выполниться.
- Шаблон нужно оставить фиксированным в коде и передавать пользовательское значение как данные, например `render_template("message.html", message=value)`.
- Автоэкранирование Jinja для HTML нужно оставить включенным, но оно не защищает от server-side template injection.
- Тест со значением в форме выражения должен подтвердить, что оно выводится текстом, а не вычисляется.
Зачем это спрашивают: Сильный ответ отличает недоверенный исходник шаблона от обычных данных в фиксированном шаблоне.
Неэкранированный ввод может добавить операторы LDAP-фильтра и изменить набор найденных записей каталога.
- Конкатенация имени в `(&(uid=` плюс ввод плюс `)(active=true))` позволяет символам `*`, `(` и `)` влиять на синтаксис.
- Нужно применять API LDAP-библиотеки для экранирования значения фильтра или построения фильтра, а не общий replace обратных слешей.
- Учетную запись каталога следует оставить только для чтения и ограничить нужным поддеревом, чтобы снизить последствия ошибочного запроса.
Зачем это спрашивают: Интервьюер проверяет применение LDAP-специфичной сборки и понимание контекстности экранирования.
Соединенный путь все еще может выйти из каталога отчетов, особенно если `name` абсолютный или содержит переходы к родителю.
- Лучше принимать ID отчета и получать серверное имя файла, а не открывать клиентам имена файловой системы.
- Если имена необходимы, оба пути нужно разрешить через `pathlib.Path.resolve` и потребовать, чтобы цель была относительной к разрешенному корню отчетов.
- Следует проверить обработку symlink и по возможности открывать проверенную цель без перехода по ссылкам, затем протестировать абсолютные и закодированные родительские пути.
Зачем это спрашивают: Сильный ответ находит path traversal в конкретном Python API и учитывает и канонические пути, и symlink.
До записи он должен убедиться, что каждая запись архива разрешается внутри выбранного каталога распаковки.
- Абсолютные имена и выход к родителю нужно отклонять, затем сравнивать каждую нормализованную цель с нормализованным корнем.
- Созданные архивом symlink опасны, потому что следующая запись может пройти через них за пределы каталога.
- Нужны лимиты числа записей и общего распакованного размера, чтобы правильная проверка пути не оставила decompression DoS.
Зачем это спрашивают: Интервьюер оценивает, проверяете ли вы распаковку глубже поверхностной проверки имени файла.
Я буду считать загруженные байты враждебными и отдельно ограничу валидацию, хранение и выполнение парсера.
- Нужно задать лимиты запроса и распакованного размера, проверить сигнатуру поддерживаемым парсером и отклонять поврежденные или зашифрованные файлы, которые функция не поддерживает.
- Файл следует хранить под сгенерированным именем в приватном карантине, а не в веб-доступном или исполняемом каталоге.
- Парсинг нужно запускать в изолированном worker с низкими правами, лимитами времени и памяти, а результат выдавать только после авторизации.
Зачем это спрашивают: Интервьюер проверяет, защищаете ли вы загрузку перед парсером глубже доверия к имени и media type.
Нативная Java-десериализация может создать выбранный атакующим граф объектов, методы которого запускают опасную gadget-цепочку.
- Подходящая gadget-цепочка в classpath может превратить `readObject` в выполнение кода до валидации приложения.
- Лучше использовать простой формат вроде JSON, отображать его в узкий data class и проверять поля по схеме.
- Если legacy-сериализацию пока нельзя убрать, нужен строгий allowlist `ObjectInputFilter` и изоляция операции.
Зачем это спрашивают: Интервьюер проверяет понимание gadget-цепочек и умение назвать более безопасную Java-альтернативу.
В современном PyYAML `yaml.load` принимает явно указанный `Loader`, и именно выбранный загрузчик определяет, какие YAML-теги и Python-типы можно создавать; `yaml.safe_load` использует `SafeLoader`.
- Для недоверенного YAML используйте `yaml.safe_load` или `yaml.load(..., Loader=yaml.SafeLoader)`, затем проверьте полученные данные по ожидаемой схеме.
- Не используйте `yaml.unsafe_load` или `UnsafeLoader` для недоверенных данных: они допускают создание Python-объектов, которое может привести к опасному поведению.
- Нельзя одинаково оценивать все вызовы `yaml.load`: проверяйте указанный загрузчик и пользовательские конструкторы, а также ограничивайте размер входных данных.
Зачем это спрашивают: Сильный ответ показывает, что безопасность определяет явно выбранный загрузчик, и не смешивает безопасный парсинг с проверкой схемы.
Закрытые вопросы
- 21
Express-маршрут предпросмотра вызывает `res.send(`<p>${req.query.message}</p>`)`. Какую уязвимость вы видите?
queriesvulnerabilities - 22
Заголовок support ticket хранится в базе и позже передается в React `dangerouslySetInnerHTML` на панели сотрудников. Что вы измените?
reactreact-xssdatabase - 23
Одностраничное приложение читает `location.hash` и передает его в `insertAdjacentHTML` как подпись навигации. Почему это DOM XSS?
domdom-xssxss - 24
Почему один общий `escapeHtml` небезопасен для всех браузерных контекстов вывода?
generics - 25
Что базовая Content Security Policy добавляет приложению с правильной защитой от XSS?
xsscsp - 26
Профиль на cookie-аутентификации принимает POST без CSRF-токена. Почему это риск?
csrftokenscookies - 27
Всегда ли API с заголовком `Authorization: Bearer` нужны CSRF-токены?
authcsrftokens - 28
Как ревьюить CORS-код, отражающий любой `Origin` запроса и включающий credentials?
cors - 29
Страница загружает закрепленную версию стороннего скрипта из CDN без атрибута `integrity`. Какую браузерную защиту вы добавите?
dependencies - 30
На ревью Express видно, что Helmet отключает `frameguard`, а в CSP нет `frame-ancestors`. Какой риск вы оформите?
clickjackingrisk-management - 31
Маршрут проверяет наличие `req.user`, а затем удаляет любой project ID. Какой проверки не хватает?
- 32
Как построить обработчик входа, чтобы внутренние причины отказа не позволяли определять существующие аккаунты?
- 33
Обработчик входа возвращает `user not found` до хеширования присланного пароля. Какую проблему вы отметите?
passwordscryptography - 34
Продукт разрешает не больше трех устройств с активным входом на пользователя. Как реализовать отзыв лишних сессий?
sessionsconcurrency - 35
Когда код приложения должен ротировать идентификатор сессии кроме первоначального входа?
sessions - 36
Что не так с использованием `jwt.decode(token)` как доказательства аутентификации клиента API?
jwttokensjwt-verification - 37
Express-роутер администратора вызывает `router.use(requireAdmin)` после регистрации одного маршрута. Что вы измените?
- 38
Как построить проверки авторизации, чтобы новый маршрут случайно не разрешил доступ всем?
authidentity-access - 39
GraphQL resolver возвращает `db.document.findUnique({ where: { id: args.id } })` любому вошедшему пользователю. Чего не хватает?
graphqlgraphql-authorization - 40
Чем отличаются broken object-level и broken function-level authorization при ревью API?
authidentity-accessapi - 41
Как выглядит ваш базовый процесс ревью небольшого pull request на ошибки безопасности приложения?
code-review - 42
Как написать простое Semgrep-правило для Python-вызовов `subprocess.run` с `shell=True`?
semgreppython - 43
Когда taint-правило Semgrep лучше правила с одним шаблоном?
semgrep - 44
Что дает CodeQL, когда уязвимость проходит через несколько функций?
vulnerabilitiescodeql - 45
Чем отличаются SAST, DAST, IAST и SCA при проверке приложения?
sastdastsca - 46
Почему SCA должен проверять lockfile зависимостей и собранный артефакт, а не только `package.json`?
dependenciesnpmpackaging - 47
Вы нашли API-ключ, зашитый в репозитории. Почему недостаточно удалить строку?
api - 48
Как junior-инженеру использовать OWASP ASVS при ревью новой функции входа?
owaspasvs - 49
Каким должен быть полезный security regression test для исправленной ошибки авторизации?
authidentity-accessregression - 50
SAST сообщает, что `req.headers.authorization` достигает общего помощника логирования. Как проверить находку?
authvalidationsast - 51
Маршрут регистрации на Node.js проверяет имя пользователя выражением `/^([a-z]+)+$/` на данных клиента. Что вы измените?
formsvalidation - 52
Java-эндпоинт загрузки разбирает пользовательский XML через `DocumentBuilderFactory` с настройками по умолчанию. Что вы проверите?
endpoints - 53
Инструмент поддержки записывает темы обращений прямо в CSV-ячейки, которые сотрудники открывают в таблицах. Какую проблему вы укажете?
spreadsheetsspread - 54
Обработчик скачивания на Go проверяет strings.HasPrefix(filepath.Clean(path), uploadRoot). Почему обход каталогов может сохраниться?
- 55
Маршрут аватара FastAPI принимает любой UploadFile, у которого content_type начинается с image/. Что вы исправите?
frameworks - 56
Маршрут профиля на Node.js вызывает `lodash.set(profile, req.body.path, req.body.value)`. Что вы будете искать?
- 57
Маршрут отчетов Express вызывает exec("wkhtmltopdf " + req.body.url + " out.pdf"). Какой минимальный безопасный редизайн вы предложите?
- 58
Маршрут входа FastAPI записывает `f"login failed for {username}"` в текстовый audit log. Что вы проверите и измените?
frameworks - 59
Маршрут сброса пароля на Python создает шестизначный токен через `random.randint` и принимает его 30 минут. Что вы измените?
passwordstokenspython - 60
Go-сервис задает `tls.Config{InsecureSkipVerify: true}` для клиента платежного API. Что вы порекомендуете?
tlscryptographyapi - 61
Burp показывает поисковую строку внутри value="..." на staging. Как безопасно проверить XSS в контексте атрибута?
xssvalidationappsec-tools - 62
Как через Caido подтвердить отчет о stored XSS в поле заметки поддержки?
xss - 63
WebSocket с cookie-аутентификацией принимает подключение без проверки `Origin`. Как безопасно подтвердить риск?
validationcookiesrisk-management - 64
Эндпоинт GET /profile/email/confirm меняет состояние, а сессионная cookie имеет SameSite=Lax. Как вы проверите CSRF?
csrfsessionscookies - 65
Аутентифицированный ответ на staging проходит через CDN без явной политики кеширования. Как проверить утечку кеша между пользователями?
caching - 66
В тестере вебхуков возможен SSRF. Как проверить его без обращения к внутренним хостам?
validationssrfwebhooks - 67
Тестер вебхуков блокирует `127.0.0.1`, но следует редиректам. Какой ограниченный дополнительный тест вы проведете?
networkingwebhooks - 68
Страница входа принимает returnUrl. Как проверить открытый редирект через Burp и браузер?
appsec-tools - 69
Iframe оформления заказа слушает postMessage и проверяет только event.data.type. Как проверить браузерный риск?
validationrisk-management - 70
Вас попросили запустить аутентифицированное сканирование ZAP на staging. Как сделать его безопасным и полезным?
- 71
Форма входа по-разному обрабатывает Alice@example.com и alice@example.com. Какие проверки безопасности вы проведете?
forms - 72
Письмо сброса пароля строит ссылку из заголовка Host. Как вы это проверите и исправите?
passwords - 73
TOTP-код дважды принимается в одном 30-секундном окне на staging. Как проверить и исправить повторное использование?
- 74
Выход реализован через GET и может запускаться внешним изображением. Стоит ли сообщать об этом?
endpoints - 75
JWT-верификатор на Java принимает RS256 и HS256 через одну переменную ключа. Что вы проверите?
jwt - 76
Два внутренних API используют токены одного провайдера идентификации. Как проверить валидацию audience?
tokensvalidationapi - 77
Обычный пользователь может вызвать `POST /admin/users/bulk-disable`, хотя кнопка администратора скрыта. Как проверить и исправить эту BFLA?
- 78
После отключения staging-пользователя администратором его существующий bearer token все еще вызывает `GET /api/profile`. Как вы это проверите и исправите?
tokensapi - 79
Тип User в GraphQL раскрывает salary, но resolver проверяет только вход. Как проверить авторизацию поля?
authidentity-accessgraphql - 80
API обновления профиля связывает все JSON-тело с моделью User. Какой тест авторизации вы добавите?
authidentity-accessapi - 81
Semgrep отмечает `hashlib.md5` в Python-помощнике ETag для статических файлов. Как вы классифицируете находку?
semgreppython - 82
Опишите scope правила Semgrep для поиска Express child_process.exec с данными запроса. Что вы включите?
semgrep - 83
Запрос CodeQL пропускает обертку getHeader, используемую перед редиректом. Какое ограниченное изменение вы сделаете?
queriescodeql - 84
Snyk сообщает о критическом CVE парсера, но сервис не импортирует парсер напрямую. Как определить приоритет?
vuln-management - 85
Dependabot открывает 14 отдельных patch-PR, меняющих один npm lockfile. Как снизить риск и шум?
npmpackagingdependency-resolution - 86
Trivy отмечает CVE-2026-1234 в пакете Alpine, а Alpine считает свою сборку незатронутой. Что вы сделаете?
vuln-management - 87
OSV-Scanner сообщает о Go-модуле, который виден только в go.sum. Как проверить находку?
validation - 88
Какой регрессионный тест вы добавите после исправления SQL-инъекции в Express getOrder?
sqlsql-injectioninjection - 89
Где проходит граница BOLA в `GET /accounts/{accountId}/keys` и какой интеграционный тест докажет исправление?
integrationobject-authorization - 90
Новое правило Semgrep дает 180 результатов, включая сгенерированные клиенты и тесты. Как настроить его до блокировки CI?
semgrep - 91
Отчет HackerOne совпадает с ошибкой, исправленной на прошлой неделе, но еще не развернутой. Это дубликат?
deployment - 92
Bounty-исследователь сообщает о stored XSS, видимом только одному администратору поддержки. Как определить severity?
xss - 93
Исследователь заявляет о командной инъекции, но дает только задержку ответа на 10 секунд. Какое безопасное доказательство вы запросите?
injection - 94
Как написать разработчику тикет о подтвержденном IDOR в GET /documents/{id}?
- 95
После bug bounty-отчета об открытых картах исходников и тестовых учетных данных API патч закрыл публичный доступ к `.map`-файлам. Как его перепроверить?
source-mapsapi - 96
Bug-bounty отчет использует 5000 запросов для обхода rate limit вопреки правилам программы. Как вы поступите?
- 97
Где разместить Semgrep, CodeQL и ZAP в CI для команды с 20 репозиториями?
semgrepcodeql - 98
Сервису нужно временное исключение Snyk для одного CVE. Что оно должно содержать?
vuln-managementerror-handling - 99
Разработчик говорит, что находка CodeQL невозможна, потому что маршрут внутренний. Как вы ответите?
codeql - 100
Какие доказательства нужны перед закрытием исправленного SSRF-отчета?
ssrf