Здесь, в Upstart, наша инженерная архитектура становится все более сложной как со стороны продукта, так и внутри компании. Это включает в себя как внутреннюю связь приложений, так и интеграцию внешних приложений. С программным обеспечением все рвутся к тому, чтобы написать этот удивительный инструмент для решения проблем X, Y и Z, поскольку возможности для решения этих проблем растут.

Например, приятно, наконец, закончить этот фрагмент кода, чтобы автоматизировать бессмысленную, трудоемкую ручную задачу, которая уже какое-то время съедает вас. Но прежде чем вы поделитесь этим инструментом, в вашем животе возникнет чувство, от которого, кажется, невозможно избавиться. Вы протестировали локально, работая в условиях неявного доверия от одной системы к другой, но как обрабатываются доверительные отношения, когда ваш конвейер выполняется компьютером, размещенным в облаке? Авторизация, это была яма в вашем желудке! К счастью, авторизация («authz») может быть как простой, так и самообслуживаемой, с учетом безопасности, с использованием нескольких простых инструментов: Okta, Terraform и хранилище секретов (в данном случае AWS Secrets Manager).

Современное состояние и его проблемы

Мы все это видели; этот сверхпривилегированный (надеюсь, зашифрованный) ключ API, который ожидает доступа и использования в заголовках авторизации для ваших вызовов API. В большинстве случаев при взаимодействии с внешними сервисами у вас есть возможность вставить сгенерированный ключ API в поле формы, которое будет использоваться для HTTP-запросов. Это очень удобно и просто, но вот пара проблем, которые могут возникнуть при использовании этой опции:

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

Добро пожаловать, OAuth

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

  1. Служба запрашивает токен доступа, ограниченный определенными областями, с сервера авторизации (в данном случае с потоком учетных данных клиента).
  2. Сервер авторизации проверяет запрос токена на соответствие определенным политикам доступа для клиента OAuth. Если клиент проходит проверку, возвращается токен доступа, ограниченный необходимыми ресурсами.
  3. Служба использует токен (обычно в качестве носителя) для выполнения последующих запросов к защищенным ресурсам.
  4. Каждый запрос, полученный сервером, проверяется. В этом случае сервер использует конечную точку самоанализа токена, чтобы определить, действителен ли токен (срок его действия не истек, правильные области для запрошенных ресурсов и операций). Если токен действителен, HTTP-запрос выполнен успешно, в противном случае запрос должен вызвать ошибку.

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

Преимущества OAuth

Теперь, когда у нас есть представление об общем потоке OAuth, давайте обрисуем его преимущества по сравнению со статическими учетными данными API. Из вышеприведенного объяснения может показаться, что мы просто создаем еще одно статическое удостоверение и называем его иначе, чем «ключ API». В буквальном смысле да, мы! Несмотря на более высокие первоначальные затраты на настройку OAuth, у нас все еще гораздо более высокий уровень безопасности для авторизации на основе следующего неполного списка причин:

  • Хотя учетные данные клиента являются статическими, токен доступа, предоставленный вам через успешную транзакцию OAuth, таковым не является. Как правило, поставщик OAuth настраивает токены доступа, созданные для их серверов, как динамические и с истекающим сроком действия. Поэтому, если токен Bearer скомпрометирован, ваш злоумышленник будет иметь доступ к вашим ресурсам только в течение периода времени, в течение которого ваш токен настроен на активность. После этого ваша служба должна возвращать ошибки HTTP (поскольку служба постоянно выполняет самоанализ токена для каждого запроса).
  • Теперь вас может интересовать сценарий скомпрометированной пары учетных данных клиента. Давайте проделаем это упражнение и представим, что злоумышленник получил доступ к учетным данным клиента вашего приложения. О, нет! Если бы они получили ваш ключ API, они могли бы попробовать несколько разных способов передать заголовки авторизации на ваш сервер и, вероятно, довольно быстро выяснить, как сделать так, чтобы дела вашей команды шли плохо. Вот в чем преимущество OAuth! Если хакер получает учетные данные вашего клиента, ему нужно выяснить, какие политики доступа вы настроили в своем поставщике OAuth, чтобы получить токен доступа. Как правило, при отправке запроса токена на сервер OAuth вы должны указать область(и) для сервера авторизации, чтобы сервер авторизации мог проверить, есть ли у учетных данных вашего клиента возможность получить токен доступа с этой областью. Эти области хороши тем, что они абсолютно произвольны! Вы определяете их как разработчика в своем приложении и настраиваете их в своем поставщике OAuth. Хакеру будет сложнее выяснить, какой клиент может получить доступ к настраиваемым областям, которые вы определили, по сравнению с выяснением того, следует ли передавать статический ключ API в заголовке авторизации посредством единого входа для веб-систем (SSWS) или Bearer.

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

# Bearer authorization
curl https://my-very-secure-service.com/databases/123/delete \
  -H “Authorization: Bearer your-hacked-key-xyz”

# Basic authorization
curl https://my-very-secure-service.com/databases/123/delete \
  -H “Authorization: Basic your-hacked-key-xyz”

# SSWS Authorization (Okta’s API token authorization method
curl https://my-very-secure-service.com/databases/123/delete \
  -H “Authorization: SSWS your-hacked-key-xyz”

# custom api key header, can be found in AWS API gateways, for example
curl https://my-very-secure-service.com/databases/123/delete \
  -H “x-api-key: your-hacked-key-xyz”

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

# Token request without scopes parameter
curl \
https://my-very-secure-auth-server/token?grant_type=client_credentials&client_id=your-hacked-id&client_secret=your-hacked-secret

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

# Token request with invalid scopes
curl \
https://my-very-secure-auth-server/token?grant_type=client_credentials&client_id=your-hacked-id&client_secret=your-hacked-secret&scopes=fake.scope.guess

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

  • Маркеры доступа являются динамическими как по сроку действия, так и по области действия. Еще одним преимуществом OAuth является гибкость для доступа с наименьшими привилегиями для каждого запроса! Например, для приложения с операциями чтения и записи вполне вероятно, что вы будете разделять привилегии чтения и записи, чтобы свести к минимуму риск, назначая каждую привилегию отдельному набору средств доступа. Именно здесь проявляется гибкость OAuth. Все, что вам нужно сделать, это запросить область read с вашими учетными данными клиента, и тогда вам не придется беспокоиться о случайных (или злоумышленниках) операциях записи. Если бы кто-то скомпрометировал этот токен, у него не было бы доступа для записи, что уменьшило площадь поверхности угрозы. И наоборот, если кто-то скомпрометирует ваш статический ключ API, в котором заложены все разрешения, он сможет делать все, что захочет.

Поставщик Okta OAuth с Terraform

Если я не убедил вас, что OAuth лучше, чем ключ API, или вы считаете, что накладные расходы на его настройку слишком велики, давайте перейдем к самообслуживанию OAuth.

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

Если вам посчастливилось работать с Okta, AWS и Terraform, это очень просто! Недавно мы выполнили настройку приложений OAuth, областей действия, политик доступа и правил политики доступа в Okta. Хотя это относительно простая задача для опытного разработчика Okta, почему разработчик Okta должен быть узким местом для разработки продукта? Я считаю, что нет причин быть узким местом, поэтому я решил написать модуль самообслуживания для OAuth, используя Okta в качестве поставщика OAuth. Terraform действует как часть самообслуживания, а AWS Secrets Manager хранит учетные данные клиентов.

Все, что нужно сделать разработчику, — это создать секрет, содержащий учетные данные как для клиентских, так и для серверных приложений, а затем использовать этот модуль Terraform для предоставления необходимых ресурсов Okta. Имея пару строк кода и немного знаний о конечных точках OAuth API Okta, разработчики могут начать работу с OAuth всего за пару минут! В этом модуле заключено множество настраиваемых вещей, а именно имя вашего сервиса, области действия и время жизни токена. Для сравнения, сквозной рабочий процесс для этого без самообслуживания будет следующим:

  • Разработчик отправляет запрос команде Okta на предоставление ресурсов OAuth
  • Анкета туда и обратно между командой Okta и разработчиком
  • Команда Okta нажимает, кажется, миллион кнопок, чтобы выделить ресурсы
  • Команда Okta делится учетными данными клиента с разработчиком

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

Это самое простое объявление модуля, если вам не требуется такая большая настройка.

module "example_app" {
  source = "github.com/lukasindre/terraform-okta-oauth-generator.git?ref=v2.0.0"
  label  = "Test App"
  client_credentials = {
    client_client_id     = local.creds["client_client_id"]
    client_client_secret = local.creds["client_client_secret"]
    server_client_id     = local.creds["server_client_id"]
    server_client_secret = local.creds["server_client_secret"]
  }
}

data "aws_secretsmanager_secret_version" "creds" {
  secret_id = local.secret_id
}

locals {
  secret_id          = "my-secret-id"
  credentials_object = jsondecode(data.aws_secretsmanager_secret_version.creds.secret_string)
}

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

module "easy_bake_service" {
  source = "github.com/lukasindre/terraform-okta-oauth-generator.git?ref=v2.0.0"
  label  = "Easy Bake Oven"
  scopes = [
    {
      "name" : "dough.cookie.mix",
      "description" : "grant permission to mix cookie dough"
    },
    {
      "name" : "dough.cookie.bake",
      "description" : "grant permission to bake the cookie dough"
    },
    {
      "name" : "cupcake.batter.mix",
      "description" : "grant permission to mix cupcake batter"
    }
  ]
  access_token_lifetime_minutes = 60
  client_credentials = {
    client_client_id     = local.creds["client_client_id"]
    client_client_secret = local.creds["client_client_secret"]
    server_client_id     = local.creds["server_client_id"]
    server_client_secret = local.creds["server_client_secret"]
  }
  policy_description = "This policy restricts what the OAuth app is allowed to do with the easy bake oven service."
}

data "aws_secretsmanager_secret_version" "creds" {
  secret_id = local.secret_id
}

locals {
  secret_id          = "my-secret-id"
  credentials_object = jsondecode(data.aws_secretsmanager_secret_version.creds.secret_string)
}

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

Модуль

Этот модуль Terraform позаботится обо всех мельчайших нюансах Okta, налагаемых API Okta, поэтому разработчикам не нужно изучать совершенно новую структуру для взаимодействия с ними для предоставления своих ресурсов. Для интересов и потребностей разработчика он создает клиентов OAuth и определяемые разработчиком области и позволяет им самостоятельно вносить изменения, ускоряя разработку. В интересах безопасности он определяет политику и правила доступа, чтобы только клиенты, подготовленные для каждого модуля, могли получить доступ к определенным областям, а не сценарий доверия «Все клиенты», что минимизирует риск. Кроме того, использование этого модуля высвобождает ресурсы команды разработчиков Okta для улучшения нашей корпоративной идентичности другими замечательными способами. В будущем использование и развитие этого модуля гарантирует, что как разработчики Okta, так и разработчики программного обеспечения будут тратить свои ресурсы и время на разработку, в то же время поддерживая нашу команду информационной безопасности, используя как OAuth, так и шаблонную структуру авторизации.