Читать 8 мин

В тестах побочных временных каналов отказ нужно считать результатом

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

В тестах побочных временных каналов отказ нужно считать результатом

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

Этой информации достаточно, чтобы изменить поведение агента. Агент для программирования, узнав о существовании учетной записи, может продолжить запрашивать доступ к этому сервису. Скомпрометированный агент использует результат для разведки. Один вызов может выглядеть безобидно. Ситуация меняется, когда вызывающая сторона управляет входными данными, повторяет запросы и может точно измерять время.

MITRE относит этот класс к CWE-208, Observable Timing Discrepancy, то есть к наблюдаемому расхождению во времени. Определение достаточно широкое: внутреннее состояние, важное для безопасности, может выйти наружу, если операции занимают заметно разное время. Обычный пример связан с проверкой пароля. В шлюзе для агентов есть менее очевидная версия той же проблемы: вызывающая сторона уже находится внутри рабочего процесса разработчика и может выполнять множество структурированных вызовов инструментов, не уставая.

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

Считайте состояние хранилища данными, которые нельзя раскрывать вызывающей стороне

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

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

Для шлюза действий разделите следующие вопросы:

  • Может ли этот процесс вообще запросить действие?
  • Доступно ли сейчас хранилище для его выполнения?
  • Есть ли для этого действия сопоставление с учетной записью?
  • Принял ли удаленный сервис учетные данные?
  • Может ли вызывающая сторона напрямую увидеть какой-либо из этих ответов или получает только результат действия?

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

Блокировка хранилища в Sallyport намеренно абсолютна: пока хранилище закрыто, каждое действие отклоняется. Это четкая граница безопасности, но для нее все равно нужны тесты времени, потому что вызывающая сторона может наблюдать, как именно формируется отказ. Цель не в том, чтобы делать вид, будто заблокированное хранилище способно выполнить удаленный запрос. Нужно убедиться, что локальная обработка отказа не создает простой повторяемый признак, раскрывающий больше, чем необходимо вызывающей стороне.

OWASP формулирует ту же мысль в Authentication Cheat Sheet. Один только общий текст ошибки не устраняет утечку при перечислении, если один путь отказа выполняет больше работы, чем другой. Рекомендация относится к аутентификации, но инженерный вывод шире: одинаковое сообщение, обернутое вокруг разных путей выполнения, все равно выдает информацию через прошедшее время.

Для четырех результатов нужны два разных временных контракта

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

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

Второй контракт охватывает отправленные результаты. Корректные и недействительные учетные данные могут дойти до одного и того же тестового HTTP-сервера или SSH-хоста. Их общее время включает транспорт, повторное использование соединения, работу сервера и доставку ответа. Невозможно сделать публичный интернет постоянным по времени. Но можно провести добавление, отправку, регистрацию и преобразование результатов для обоих случаев по сопоставимым путям, а удаленную часть стабилизировать с помощью контролируемого сервера.

Получается полезная матрица:

РезультатЗапрос покидает шлюз?С чем сравнивать его во временном тесте?
Хранилище заблокированоНетС другими локальными отказами, которые не должны раскрывать больше состояния
Авторизация отклоненаНетС другими локальными отказами, измеряя время до взаимодействия с человеком
Учетные данные отсутствуютНетС локальной ошибкой при доступном хранилище, используя тот же конверт ответа, если этого требует политика раскрытия
Недействительные учетные данныеДаС корректными учетными данными против одного и того же контролируемого сервера
Корректные учетные данныеДаС недействительными учетными данными и вариантами удаленной ошибки

Не сравнивайте отсутствие учетных данных напрямую с успешным вызовом стороннего API и не объявляйте тест проваленным только потому, что первый случай быстрее. Такой тест лишь доказывает, что запрос, который не покинул машину, быстрее интернет-запроса. Это и так было известно.

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

Есть жесткое ограничение: если вызывающая сторона получает подробную ошибку «учетные данные отсутствуют», время уже не нужно, чтобы узнать об их отсутствии. Защита от временных утечек не спасет API, которое открыто сообщает состояние секрета. Сначала определите допустимую семантику ошибок.

Быстрый отказ становится оракулом, если его можно повторять

Утечка времени редко проявляется как огромная разница. Чаще всего при рефакторинге кто-то добавляет разумный ранний выход.

Представьте шлюз со следующим порядком действий:

  1. Разобрать запрос агента.
  2. Найти названные учетные данные в индексе хранилища.
  3. Проверить, есть ли у процесса агента разрешение сессии.
  4. Сформировать и отправить запрос.

При отсутствии учетных данных выполнение завершается на втором шаге. Если учетные данные существуют, но процесс не одобрен, оно продолжается до третьего шага. Внешний ответ в обоих случаях может быть одинаковым: «действие недоступно». Однако второй путь включает попадание в индекс хранилища, поиск авторизации, подготовку аудита и, возможно, создание карточки подтверждения. Вызывающая сторона может много раз выполнить каждый вариант входных данных и упорядочить результаты.

Утечка становится серьезнее, если агент может выбирать псевдонимы учетных данных. Он может проверять список имен вроде staging, production, deploy или имен, найденных в репозитории. Успешное действие ему не нужно. Достаточно разделить результаты на быструю и медленную группы.

Защитный порядок зависит от политики раскрытия, но более безопасная схема выглядит так:

  1. Проверить запрос без ветвлений, зависящих от секретов.
  2. Применить абсолютную блокировку хранилища.
  3. Применить авторизацию процесса или сессии.
  4. Определить действие и учетные данные только после прохождения вызывающей стороной проверок, которые должны предшествовать получению этой информации.
  5. Провести локальные ошибки, которые должны оставаться неразличимыми, через одинаковые журналирование, формирование ошибки и завершение ответа.

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

Распространенный плохой патч выглядит как sleep(100ms) перед каждым отказом. На демонстрационном графике он делает все красивее, но создает три проблемы. После шума планировщика и поведения кэша длительность все равно часто отличается. Задержка обременяет легитимных пользователей. И главное, повторяющая вызовы сторона может усреднить случайное или фиксированное дополнение, если исходные пути все еще различаются. Надежнее выровнять необходимую работу, чем добавлять задержку поверх разной работы.

Измеряйте на границе, которую видит агент

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

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

Практическая запись должна содержать больше одного числа:

{
  "case": "vault_locked",
  "sequence": 184,
  "elapsed_us": 12746,
  "result_class": "local_denial",
  "connection_mode": "fresh",
  "run_id": "test-run-7"
}

Используйте монотонные часы. Настенные часы скачут при синхронизации времени и плохо подходят для сравнений менее секунды. Записывайте микросекунды или наносекунды, если платформа это позволяет, а при отображении результатов переводите их в миллисекунды. Точность записи не означает точность измерения, но так вы не потеряете данные до анализа.

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

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

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

Создайте контролируемый удаленный сервер, отделяющий смысл результата от задержки

Сделайте блокировку хранилища абсолютной
Пока хранилище заблокировано, Sallyport отклоняет любые действия через HTTP API и SSH.

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

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

Например, достаточно такого контракта заглушки:

Request header: Authorization: Bearer test-good
Response: 200 {"fixture":"accepted"}

Request header: Authorization: Bearer test-bad
Response: 401 {"fixture":"rejected"}

Both requests: wait until the same server-side target duration has elapsed

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

Для HTTP запускайте заглушку на loopback, если хотите изолировать шлюз и клиента. В отдельной задаче запускайте ее на контролируемом удаленном хосте, если хотите увидеть, как обычный сетевой шум влияет на обнаружение. Отчеты храните раздельно. Результат на loopback показывает, изменились ли локальные детали реализации. Удаленный результат показывает, сохраняет ли тест чувствительность при реалистичных транспортных колебаниях.

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

Sallyport отправляет HTTP-запросы с bearer-, basic- или пользовательскими заголовками, добавляя учетные данные, а для SSH использует встроенный stateless-помощник sp-ssh. Это разные каналы, и для них нужны разные заглушки. Результат, показывающий одинаковое время для HTTP-заголовка, ничего не говорит о пути SSH, который ищет ключи, запускает помощник и согласует соединение.

Сравнивайте распределения, а затем пытайтесь классифицировать состояние

Средние значения скрывают временные утечки. Если 90 процентов вызовов занимают 12 миллисекунд, а 10 процентов занимают 80 миллисекунд из-за промаха кэша только для существующей учетной записи, среднее может выглядеть достаточно близким к другому случаю, хотя агент способен использовать быстрый кластер.

Для каждого случая сохраняйте полный набор измерений. Отчитывайтесь как минимум о медиане, нижнем и верхнем процентилях, минимуме, максимуме и размере выборки. Гистограмма часто информативнее таблицы, потому что сразу показывает два кластера.

Затем сделайте тест враждебным. Дайте намеренно простой классификатор, которому доступна только длительность, и попросите угадать скрытую метку. Начните с классификатора по порогу. Он выбирает одну границу, например «меньше X микросекунд означает отсутствие учетных данных», и сообщает точность на данных, которые не использовались для выбора этой границы. Если классификатор с одним порогом стабильно заметно превосходит наивный базовый уровень, перед вами есть наблюдаемый сигнал, который стоит изучить.

Небольшой скрипт анализа на Python поможет увидеть регрессию, не создавая видимость доказательства криптографического постоянства времени:

from statistics import median

samples = {
    "vault_locked": [...],
    "credential_missing": [...],
}

for name, values in samples.items():
    ordered = sorted(values)
    p10 = ordered[int(len(ordered) * 0.10)]
    p90 = ordered[int(len(ordered) * 0.90)]
    print(name, {"n": len(values), "p10": p10,
                 "median": median(values), "p90": p90})

best = None
all_values = sorted(set(samples["vault_locked"] + samples["credential_missing"]))
for cutoff in all_values:
    correct = 0
    total = 0
    for label, values in samples.items():
        for value in values:
            guess = "vault_locked" if value <= cutoff else "credential_missing"
            correct += (guess == label)
            total += 1
    score = correct / total
    if best is None or score > best[0]:
        best = (score, cutoff)

print({"best_training_accuracy": best[0], "cutoff_us": best[1]})

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

Не задавайте универсальное условие вроде «все медианы должны отличаться не более чем на пять миллисекунд». Такой порог ничего не значит для разных машин и тестовых конфигураций. Используйте базовую линию, записанную в контролируемой среде, проверяйте ожидаемое перекрытие распределений и проваливайте тест, если изменение создает устойчивое разделение состояний, которые вы хотели скрыть.

Сравнение за постоянное время решает более узкую задачу

Проводите действия через единый шлюз
Агенты обращаются к действиям через встроенный шлюз sp mcp, а не получают учетные данные напрямую.

Услышав выражение «атака по времени», разработчики часто сразу выбирают функцию сравнения за постоянное время. Такая функция важна для сравнения секретов одинаковой длины, подписей, MAC и токенов. Но она не делает весь путь запроса в шлюзе постоянным по времени.

Пакет Go crypto/subtle предоставляет ConstantTimeCompare для срезов байтов с одинаковым содержимым. Это подходящий примитив, когда само сравнение секрета должно выполняться независимо от данных. Но он не выровняет ветку, которая возвращает результат до открытия хранилища, ветку, которая отображает карточку подтверждения, или ветку, которая открывает сетевое соединение.

Используйте сравнение за постоянное время для чувствительных значений фиксированного формата. Затем проверьте окружающий поток управления. Чистое сравнение внутри пути, который немедленно возвращает результат при отсутствии учетных данных, все равно оставляет оракул наличия учетной записи.

Поэтому не стоит называть всю проблему «постоянным временем». Для настольного шлюза, который может выполнять HTTP- и SSH-вызовы, постоянное время от начала до конца недостижимо и не требуется. Требование уже: чувствительные локальные состояния не должны создавать для вызывающей стороны удобно классифицируемый сигнал времени.

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

Для потоков подтверждения нужна четко заданная граница измерения

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

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

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

Нужен и тестовый режим, который устанавливает стабильное состояние подтверждения до выборки отправленных вызовов. Иначе в распределение «корректных учетных данных» один раз попадет карточка подтверждения, а в остальной части сессии ее не будет. Это создает огромный выброс первого измерения и скрывает более тонкие различия.

Хороший набор тестов должен отдельно отвечать на такие вопросы:

  • Отказывает ли заблокированное хранилище до любого зависящего от подтверждения поиска учетных данных?
  • Получает ли неодобренный процесс одинаковую обработку до запроса подтверждения независимо от того, настроены ли учетные данные для действия?
  • После одобрения сессии проходят ли корректные и недействительные тестовые учетные данные сопоставимые пути шлюза к контролируемому серверу?
  • Раскрывает ли отказ в подтверждении отдельного вызова дополнительную работу, зависящую от учетных данных, до показа запроса?

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

Работа с аудитом не должна создавать быстрый путь, зависящий от секрета

Не добавляйте еще один движок политик
Sallyport использует фиксированную лестницу из трех проверок, а не язык политик или движок правил.

Журналирование аудита часто становится источником последней временной утечки, потому что его считают bookkeeping, а не частью границы безопасности. Отклоненный запрос может записывать только короткое событие. Успешный запрос может создавать подробную запись, хешировать ее в цепочку и сохранять в зашифрованном хранилище. Такая разница может быть допустимой, если успех и так виден. Она опасна, когда два локальных отказа раскрывают разный объем скрытого состояния.

Определите, какие поля можно безопасно записывать для каждого результата, а затем выполняйте одинаковую работу аудита для сопоставимых локальных ошибок. Не нужно записывать вымышленные учетные данные или отправлять фиктивные запросы. Но следует избегать ветки, в которой «учетные данные отсутствуют» обходят журнал, а «хранилище заблокировано» выполняет более тяжелый поиск и запись, если агент не должен различать эти состояния.

Не выполняйте проверку аудита в пути запроса. Это действие оператора с другой целью и другим временным профилем. Цепочку аудита Sallyport можно проверить автономно по шифротексту с помощью sp audit verify, без ключа хранилища. Такая архитектура не требует чтения секрета для проверки, но не отменяет необходимости тестировать поведение журналирования во время запроса.

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

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

Размещайте дифференциальные тесты рядом с кодом, который добавляет ветвления

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

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

Результат теста должен содержать сведения, с которыми рецензент может работать. Это лучше, чем зеленая галочка с необъяснимой общей оценкой:

case pair: vault_locked vs credential_missing
samples: 400 per case, randomized order
median: 12.7 ms vs 13.1 ms
p10-p90: 11.8-14.0 ms vs 11.9-14.3 ms
holdout threshold accuracy: 51.4%
result: within baseline

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

case pair: vault_locked vs credential_missing
samples: 400 per case, randomized order
median: 4.2 ms vs 19.6 ms
p10-p90: 3.9-4.7 ms vs 18.2-22.1 ms
holdout threshold accuracy: 99.1%
result: investigate credential lookup before authorization

Второй отчет не утверждает, что злоумышленник всегда получит точность 99,1 процента на загруженной рабочей станции. Он сообщает инженеру, что локальный код создал четкое повторяемое разделение. Этого достаточно, чтобы заблокировать изменение до исправления порядка ветвлений или конверта ответа.

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

Вопросы и ответы

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

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

Как проверять время выполнения, если для подтверждения нужен клик человека?

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

Достаточно ли сравнивать среднее время ответа в тестах на побочные временные каналы?

Нет. Тест, который сравнивает только среднюю длительность, пропустит несколько кластеров времени, эффекты кэша и короткий путь быстрого отказа, скрытый в шумном среднем значении. Сохраняйте отдельные измерения, сравнивайте процентили и попробуйте простой классификатор, который угадывает состояние по длительности.

Как тестировать недействительные учетные данные, не обращаясь к настоящему API?

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

Устраняет ли случайная задержка побочный временной канал?

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

Какая разница во времени безопасна между результатами авторизации?

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

Следует ли объединять сетевые ошибки с отсутствующими учетными данными?

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

Откуда измерять время в шлюзе для агентов?

Проверяйте границу, которую видит агент: от момента отправки MCP-запроса до получения окончательного результата или ошибки. Добавьте также узкие тесты для поиска в хранилище, авторизации и отправки запроса, чтобы по неудачному дифференциальному тесту понять, какой этап изменился. Один сквозной секундомер находит утечки, а внутренние интервалы помогают объяснить их.

Могут ли тесты времени защитить секреты, если у агента широкий доступ?

Тесты помогают уменьшить случайное раскрытие данных, но не могут скрыть факт, который агенту прямо разрешено узнать. Если агент может перечислять имена учетных данных, просматривать экраны состояния или получать разные подробные ошибки, проблема уже не в основном во времени. Сначала уберите ненужное раскрытие состояния, а затем усложните классификацию оставшихся путей по длительности.

Как часто запускать тесты на побочные временные каналы?

Считайте набор временных проверок регрессионным тестом, а не разовым аудитом. Запускайте короткую версию при каждом изменении авторизации, поиска в хранилище, отображения ошибок или отправки запросов, а перед выпуском собирайте более крупную случайную выборку в контролируемой среде. Утечки времени часто возвращаются, когда кто-то добавляет безобидный на вид ранний выход.

Sallyport

Sallyport выполняет API-вызовы и SSH-команды за вашего ИИ-агента. Ключи остаются в локальном хранилище на вашем Mac; вы подтверждаете каждый запуск, и каждое действие попадает в запечатанный журнал.

© 2026 Sallyport · Открытый код по лицензии Apache-2.0 · Oleg Sotnikov