Как устранить ошибки, безопасность и надежность: искусственный интеллект для веб-разработчиков

Как устранить ошибки, безопасность и надежность: искусственный интеллект для веб-разработчиков

2 февраля 2024 г.

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

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

  1. Введение и amp; Настройка
  2. Ваша первая подсказка ИИ
  3. Потоковая передача ответов
  4. Как работает ИИ< /ли>
  5. Быстрое проектирование
  6. Изображения, созданные с помощью искусственного интеллекта
  7. Безопасность и безопасность; Надежность
  8. Развертывание
  9. В этом посте мы поговорим о том, что происходит, когда дела идут не по счастливому пути, учитывая обработку ошибок и проблемы безопасности.

    https://www.youtube.com/watch?v=DpOTSKtpEHI&embedable=true

    Работа с неверными HTTP-запросами

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

    const response = await jsFormSubmit(form)
    
    // Do something with response
    

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

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

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

    const response = await jsFormSubmit(form)
    
    if (!response.ok) {
      state.isLoading = false
      alert("The request experienced an issue.")
      return
    }
    

    Приведенный выше код учитывает только HTTP-запрос между клиентом и сервером. Не забывайте, у нас есть еще один запрос между сервером и OpenAI.

    Рассмотрим сценарий, в котором OpenAI возвращает неверный код состояния. Как мы должны сообщить об этом конечному пользователю на клиенте? Это также сложно и уникально для каждого приложения. Для удобства мы можем выполнить аналогичную проверку для свойства response.ok.

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

    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      // ... fetch options
    })
    if (!response.ok) {
      reportError(response)
      throw error(response.status, 'ERROR: Service unavailable');
    }
    

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

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

    А что происходит, когда пользователи намеренно пытаются что-то сломать…?

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

    Помимо того, что мы следовали по счастливому пути, где мы предполагали, что каждый HTTP-запрос всегда будет работать, мы предполагали, что каждый пользователь настроен доброжелательно. Это еще одна ошибка. Иногда пользователи действуют злонамеренно. Зачастую они просто глупы. Мы должны учитывать и то, и другое.

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

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

    Это хорошо для пользовательского опыта, поскольку обеспечивает некоторую раннюю обратную связь, но проверку на стороне клиента легко обойти, поэтому нам также приходится проверять данные на сервере. К счастью, есть несколько очень хороших проверочных библиотек, которые могут в этом помочь. Моего любимого зовут Зод. Мы можем установить его с помощью npm install zod.

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

    В нашем приложении мы получаем пользовательский ввод через метод requestEvent.parseBody(), который возвращает отправленные данные формы в виде объекта, содержащего option1 и oppondent2<. /код> свойства. Итак, нам нужно создать схему проверки, а затем передать данные формы в один из методов проверки схемы.

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

    Внутри моего onPost промежуточного программного обеспечения, прежде чем выполнять слишком большую работу, давайте удостоверимся, что у нас есть правильные данные:

    import { z } from 'zod'
    // ...
    
    export const onPost: RequestHandler = async (requestEvent) => {
      const formData = await requestEvent.parseBody()
    
      const schema = z.object({
        opponent1: z.string().min(1),
        opponent2: z.string().min(1),
      })
      const validation = schema.safeParse(formData)
    
      if (!validation.success) {
        requestEvent.json(400, {
          errors: validation.error.issues
        })
        return 
      }
    
      // Continue with OpenAI API request and response
    }
    

    В приведенном выше коде я создаю схему объекта, которая должна иметь два свойства: option1 и oppondent2. Оба свойства являются обязательными, должны быть строками и не могут быть пустыми. Передача данных формы в метод safeParse() схемы вернет объект, который может сообщить мне, прошла ли проверка успешно, в чем заключалась ошибка, если таковая имеется, а также проверенные данные.

    В случае неверных данных я досрочно возвращаюсь из обработчика запроса с помощью HTTP. Ответ об ошибке 400 с объяснением ошибок. 400 — код статуса неверного запроса.

    Еще одна вещь, которую мне хотелось бы изменить, — это то, как я использую данные формы после их проверки. Zod также предоставляет свойство data для возвращаемого объекта из safeParse.

    const prompt = await promptTemplate.format({
      opponent1: validation.data.opponent1,
      opponent2: validation.data.opponent2
    })
    

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

    Данные формы и параметры запроса почти всегда принимаются в виде строк, но если ваша схема Zod ожидала число, она попытается получить его за вас, превратив что-то вроде строки "420" в число < код>420.

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

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

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

    Мы можем добавить максимальную длину как к схеме проверки на стороне сервера, так и к атрибутам проверки на стороне клиента.

    // Reusable constant
    const MAX_INPUT_LENGTH = 50
    
    // In our schema
    const schema = z.object({
      opponent1: z.string().min(1).max(MAX_INPUT_LENGTH),
      opponent2: z.string().min(1).max(MAX_INPUT_LENGTH),
    })
    
    // In our template
    <Input
      maxLength={MAX_INPUT_LENGTH}
    />
    

    Уменьшая объем данных, которые может предоставить пользователь, мы уменьшаем количество токенов, которые потенциально может использовать наш запрос API.

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

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

    Борьба с инъекционными атаками

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

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

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

    const query = "SELECT * FROM Users WHERE UserId = " + inputId;
    

    Злоумышленник может предоставить строку '1 OR 1=1' в качестве входных данных и вернуть информацию обо всех пользователях. Это плохо, но этого можно избежать, используя параметризованные запросы, хранимые процедуры или экранируя пользовательский ввод. Если вы не пишете необработанные SQL-запросы, большинство инструментов защищают от внедрения. Если вам интересно, вот подробнее о предотвращении с помощью OWASP.

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

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

    Это может выглядеть так:

    Translate the text after the delimiting characters "~~~~~":
    
    ~~~~~
    
    [text to be translated]
    

    Это большое улучшение, поскольку оно обеспечивает более четкое разделение между системой и пользовательским вводом. И все же не обошлось без пробелов.

    Саймон Уиллисон указал на несколько примеров атак с быстрым внедрением и объяснил, почему эта проблема может никогда не стать решенной:

    * Атаки с быстрым внедрением на GPT-3 * Я не знаю, как решить проблему быстрого внедрения< /п>

    Это немного пугает и должно заставить вас дважды подумать о создании приложений на базе искусственного интеллекта. Но это своего рода ящик Пандоры. Даже несмотря на присущие ему уязвимости, ИИ никуда не денется. Я советую всегда быть в курсе направлений атак и использовать несколько уровней безопасности.

    Заключение

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

    В этом посте мы обсудили:

    • Что происходит, когда в нашем приложении возникают ошибки HTTP?
    • Как мы проверяем вводимые пользователем данные?
    • Каковы некоторые проблемы безопасности конкретно для приложений ИИ?

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

    1. Введение и amp; Настройка
    2. Ваша первая подсказка ИИ
    3. Потоковая передача ответов
    4. Как работает ИИ< /ли>
    5. Быстрое проектирование
    6. Изображения, созданные с помощью искусственного интеллекта
    7. Безопасность и безопасность; Надежность
    8. Развертывание
    9. Большое спасибо за чтение. Если вам понравилась эта статья и вы хотите поддержать меня, лучший способ сделать это — поделиться ею< /strong>, подпишитесь на мою рассылку и следуйте за мной в Твиттере.


      Первоначально опубликовано на austingil.com.


      Оригинал