Skip to content

Вопросы на собеседовании: Инженер по безопасности приложений

100 реальных вопросов с образцовыми ответами и пояснениями для уровня Junior.

Смотреть пример резюме: Инженер по безопасности приложений

Тренировка флешкарточками

Интервальное повторение · Hunter Pass

Вопросы

code-reviewhttpqueries

Я прослежу недоверенное значение от источника через все преобразования до точки исходящего сетевого запроса, прежде чем решать, есть ли уязвимость.

  • Отмечу `req.query.url` как источник, потому что клиент может менять query-параметры в обход обычного интерфейса.
  • Прослежу присваивания, вызовы помощников, парсинг, валидацию и редиректы до `fetch`, Axios или другого API исходящих запросов.
  • Зафиксирую защитные проверки на пути, затем проверю, ограничивают ли они конечный адрес после DNS-разрешения и редиректов, а не только исходную строку.

Зачем это спрашивают: Интервьюер проверяет, умеете ли вы разбирать конкретный путь от источника до sink, а не судить об одной строке изолированно.

code-review

Я буду считать недоверенным любое значение под внешним контролем, даже если фреймворк уже его распарсил.

  • Пути запроса, query-параметры, тело, заголовки и cookie напрямую контролируются клиентом.
  • Строки базы, сообщения очереди, загруженные файлы и ответы партнерского API остаются недоверенными, если на них может влиять другой пользователь или система.
  • TypeScript-тип или JSON-парсер меняет представление, но не доказывает безопасность или авторизованность значения.

Зачем это спрашивают: Сильный ответ учитывает косвенные источники и не путает парсинг или типизацию с доверием.

code-reviewsecure-code-review

Sink представляет собой операцию, при которой подконтрольные атакующему данные могут получить опасную интерпретацию или повлиять на защищенный ресурс.

  • Выполнение SQL, запуск shell-процесса, рендеринг шаблона и `innerHTML` могут интерпретировать данные как код или синтаксис.
  • Запись в файловую систему, редиректы, исходящие HTTP-запросы и десериализаторы могут пересекать важные границы приложения.
  • Одно значение может быть безвредным в логе и опасным в shell-команде, поэтому я классифицирую sink по реальному API и контексту.

Зачем это спрашивают: Интервьюер оценивает, находите ли вы опасные операции по поведению и контексту, а не по именам переменных.

validation

Валидация решает, соответствуют ли данные правилам приложения, а кодирование вывода не дает допустимым данным стать синтаксисом в месте назначения.

  • Комментарий может законно содержать `<`, кавычки или амперсанды, поэтому запрет всех специальных символов испортит обычный текст.
  • При выводе в HTML-текст шаблонизатор должен кодировать символы для этого HTML-контекста.
  • Если то же значение попадает в URL или JavaScript-строку, ему нужен кодировщик или безопасный API для другого контекста.

Зачем это спрашивают: Сильный ответ отделяет бизнес-валидацию от контекстной защиты в sink.

sqlqueriesschema

Я сопоставлю небольшой набор публичных вариантов сортировки с фиксированными SQL-идентификаторами вместо вставки строки клиента в запрос.

  • Например, `date` сопоставлю с `created_at`, а `total` с `total_cents`, отклоняя остальные значения.
  • Плейсхолдеры базы связывают значения, но не имена таблиц или столбцов, поэтому одной параметризации здесь недостаточно.
  • Такое сопоставление также закрывает случайный доступ к внутренним столбцам с допустимыми SQL-именами.

Зачем это спрашивают: Интервьюер проверяет, понимаете ли вы уместность allowlist и ограничения привязки значений.

reactformsapi

API должен сам применять правило ролей, потому что клиент может обойти React-форму и отправить запрос напрямую.

  • На сервере нужно сверять значение с точным набором ролей для этой операции, а не со всеми известными системе ролями.
  • Отдельно нужно авторизовать назначающего роль, потому что допустимое значение `admin` все равно может быть запрещено этому пользователю.
  • Следует добавить API-тест, где обычный пользователь отправляет `admin` и неизвестное значение и получает отказ.

Зачем это спрашивают: Интервьюер проверяет, размещаете ли вы валидацию и авторизацию на серверной границе.

databaseapi

Граница доверия появляется при каждом переходе между компонентами с разными идентичностями, правами или контролем над данными.

  • Ввод браузера переходит в API, поэтому API независимо от интерфейса аутентифицирует, валидирует и авторизует его.
  • Задание из очереди переходит от API к worker, поэтому worker проверяет форму сообщения и не исходит из того, что каждый отправитель всегда формирует корректные сообщения.
  • API и worker входят в базу под сервисными учетными записями, права которых должны соответствовать их разным задачам.

Зачем это спрашивают: Сильный ответ размещает конкретные проверки и идентичности на внутренних и внешних границах.

least-privilegeaccess-control

Я разделю возможности так, чтобы код отчетов не мог удалять аккаунты или менять посторонние записи.

  • Для отчетов дам отдельную учетную запись базы с доступом только к нужным представлениям или операциям `SELECT`.
  • Удаление помещу за отдельный сервисный метод и проверку авторизации, не выдавая каждому модулю универсальный клиент базы.
  • Фоновые конвертеры и парсеры запущу под непривилегированным пользователем ОС с доступом только к рабочему каталогу.

Зачем это спрашивают: Интервьюер оценивает, переводите ли вы наименьшие привилегии в конкретные границы приложения, базы и процесса.

sqlormapi

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

  • Ответ не должен содержать SQL, имена таблиц, пути файловой системы, кадры стека и значения окружения.
  • Исключение нужно залогировать с correlation ID, маршрутом запроса и безопасным контекстом для расследования разработчиками.
  • Ожидаемые ошибки вроде повторного email следует преобразовать в отдельный безопасный ответ вместо утечки исходного исключения базы.

Зачем это спрашивают: Сильный ответ сохраняет возможность отладки, не отдавая внутренности приложения пользователю.

formsvalidation

Проверка безопасности должна применяться к тому же нормализованному значению, которое использует чувствительная операция.

  • Проверка пути до URL-декодирования может пропустить `%2e%2e/`, хотя файловая система позже получит `../`.
  • Нужно один раз распарсить и нормализовать значение библиотекой платформы, отклонить неверные или неоднозначные формы и использовать проверенный результат.
  • Повторного декодирования следует избегать, потому что двойная кодировка может изменить смысл уже после первой проверки.

Зачем это спрашивают: Интервьюер проверяет понимание ошибок порядка валидации из-за разных представлений данных.

queries

Код смешивает подконтрольные клиенту данные с SQL-синтаксисом, поэтому email может изменить структуру запроса.

  • Конкатенацию нужно заменить плейсхолдером драйвера, например `WHERE email = $1`, и передать `email` в массиве значений.
  • Не следует чинить это ручным экранированием кавычек, потому что режимы базы и крайние случаи делают его ненадежным.
  • Регрессионный тест с email, содержащим кавычку, должен подтвердить, что он остается одним связанным значением и не меняет выражение.

Зачем это спрашивают: Интервьюер ожидает исправление через параметризацию для конкретного языка и тест разделения кода и данных.

mongodbnosql-injectionrisk-management

Клиент может внедрить операторы MongoDB вместо простых значений полей, ожидаемых приложением.

  • Ввод вроде `{"password": {"$ne": null}}` меняет смысл запроса, если произвольный объект попадает в `find`.
  • Следует построить новый фильтр из проверенных скалярных полей, например `{ email: validatedEmail }`, а не пересылать объект запроса.
  • Ключи с `$` стоит отклонять как дополнительную защиту, но не вместо явной схемы.

Зачем это спрашивают: Сильный ответ узнает инъекцию операторов и строит безопасный объект запроса вместо расплывчатой очистки JSON.

python

Я уберу shell и передам в `subprocess.run` фиксированный исполняемый файл с отдельными аргументами.

  • Список вроде `["convert", input_path, output_path]` с `shell=False` оставляет метасимволы внутри одного аргумента имени файла.
  • Пути должен генерировать сервер, применяя правила файлов до запуска процесса.
  • Конвертер нужно запускать с низкими правами ОС, таймаутом и лимитами ресурсов для сдерживания сбоев парсера.

Зачем это спрашивают: Интервьюер проверяет, убираете ли вы интерпретацию команд и добавляете практическое ограничение дочернего процесса.

frameworks

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

  • Шаблон нужно оставить фиксированным в коде и передавать пользовательское значение как данные, например `render_template("message.html", message=value)`.
  • Автоэкранирование Jinja для HTML нужно оставить включенным, но оно не защищает от server-side template injection.
  • Тест со значением в форме выражения должен подтвердить, что оно выводится текстом, а не вычисляется.

Зачем это спрашивают: Сильный ответ отличает недоверенный исходник шаблона от обычных данных в фиксированном шаблоне.

injectionldap-injection

Неэкранированный ввод может добавить операторы LDAP-фильтра и изменить набор найденных записей каталога.

  • Конкатенация имени в `(&(uid=` плюс ввод плюс `)(active=true))` позволяет символам `*`, `(` и `)` влиять на синтаксис.
  • Нужно применять API LDAP-библиотеки для экранирования значения фильтра или построения фильтра, а не общий replace обратных слешей.
  • Учетную запись каталога следует оставить только для чтения и ограничить нужным поддеревом, чтобы снизить последствия ошибочного запроса.

Зачем это спрашивают: Интервьюер проверяет применение LDAP-специфичной сборки и понимание контекстности экранирования.

joinsendpointspython

Соединенный путь все еще может выйти из каталога отчетов, особенно если `name` абсолютный или содержит переходы к родителю.

  • Лучше принимать ID отчета и получать серверное имя файла, а не открывать клиентам имена файловой системы.
  • Если имена необходимы, оба пути нужно разрешить через `pathlib.Path.resolve` и потребовать, чтобы цель была относительной к разрешенному корню отчетов.
  • Следует проверить обработку symlink и по возможности открывать проверенную цель без перехода по ссылкам, затем протестировать абсолютные и закодированные родительские пути.

Зачем это спрашивают: Сильный ответ находит path traversal в конкретном Python API и учитывает и канонические пути, и symlink.

archive-traversal

До записи он должен убедиться, что каждая запись архива разрешается внутри выбранного каталога распаковки.

  • Абсолютные имена и выход к родителю нужно отклонять, затем сравнивать каждую нормализованную цель с нормализованным корнем.
  • Созданные архивом symlink опасны, потому что следующая запись может пройти через них за пределы каталога.
  • Нужны лимиты числа записей и общего распакованного размера, чтобы правильная проверка пути не оставила decompression DoS.

Зачем это спрашивают: Интервьюер оценивает, проверяете ли вы распаковку глубже поверхностной проверки имени файла.

Я буду считать загруженные байты враждебными и отдельно ограничу валидацию, хранение и выполнение парсера.

  • Нужно задать лимиты запроса и распакованного размера, проверить сигнатуру поддерживаемым парсером и отклонять поврежденные или зашифрованные файлы, которые функция не поддерживает.
  • Файл следует хранить под сгенерированным именем в приватном карантине, а не в веб-доступном или исполняемом каталоге.
  • Парсинг нужно запускать в изолированном worker с низкими правами, лимитами времени и памяти, а результат выдавать только после авторизации.

Зачем это спрашивают: Интервьюер проверяет, защищаете ли вы загрузку перед парсером глубже доверия к имени и media type.

java-deserialization

Нативная Java-десериализация может создать выбранный атакующим граф объектов, методы которого запускают опасную gadget-цепочку.

  • Подходящая gadget-цепочка в classpath может превратить `readObject` в выполнение кода до валидации приложения.
  • Лучше использовать простой формат вроде JSON, отображать его в узкий data class и проверять поля по схеме.
  • Если legacy-сериализацию пока нельзя убрать, нужен строгий allowlist `ObjectInputFilter` и изоляция операции.

Зачем это спрашивают: Интервьюер проверяет понимание gadget-цепочек и умение назвать более безопасную Java-альтернативу.

yamlyaml-deserialization

В современном 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