RE:NODE

Роли, права и API-токены Strapi: как закрыть доступ

Автор Команда RE:NODE
20 мин чтения


Свежая установка Strapi по умолчанию закрыта, и это хорошая новость: у роли Public в начале нет ни одного разрешения, поэтому новый тип контента отвечает анонимным запросам 403 Forbidden, пока вы не решите иначе. Опасны не настройки по умолчанию, а то, что люди делают, чтобы фронтенд заработал в пятницу вечером: отмечают все галочки у Public, создают API-токен Full access и вставляют его в браузерный бандл, оставляют регистрацию открытой и выдают каждому редактору роль Super Admin. В этом руководстве разобраны четыре отдельные системы прав в Strapi, то, чем управляет каждая из них, и порядок, в котором стоит закрывать новую установку. После каждого шага есть проверка через curl, чтобы вы знали наверняка, что изменение сработало так, как вы думаете.

Речь идёт о Strapi 5, текущей основной версии. Названия пунктов меню слегка меняются между релизами 5.x, поэтому там, где подпись важна, она дана так, как выглядит в Settings; общая схема верна и для Strapi 4.

Четыре разных «пользователя» в Strapi#

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

ИдентичностьГде настраиваетсяК чему имеет доступ
Админ-пользовательSettings, Administration Panel, UsersАдмин-панель по адресу /admin
Конечный пользовательContent Manager, User (Users and Permissions)Content API через JWT
API-токенSettings, Global settings, API TokensContent API через bearer-токен
Анонимный посетительНастраивать нечегоContent API в роли Public

Админ-пользователи - это ваши редакторы и разработчики. Они входят в админ-панель по email и паролю, а что им там можно, определяется админ-ролью. Через этот вход доступа к content API у них нет: админ-панель обращается к собственным внутренним маршрутам под /admin.

Конечные пользователи относятся к плагину Users and Permissions: это люди, которые регистрируются на вашем сайте, если вы им это позволяете. Это обычные записи типа User, они проходят аутентификацию на /api/auth/local и получают JWT. Их права определяются ролью Users and Permissions - Authenticated или той, которую вы создали.

API-токены предназначены для программ, а не для людей: генератор статического сайта во время сборки, бэкенд мобильного приложения, скрипт. Токен несёт собственный список разрешений и передаётся в заголовке Authorization.

Анонимный посетитель приходит вообще без учётных данных. Всё, что вы ему разрешите, разрешено всему интернету, включая поисковых роботов и скрейперов. Strapi относит такой запрос к роли Public.

роль Publicроль Authenticatedправа токенаадмин-рольАнонимный гостьбез учётных данныхКонечный пользовательJWT с /api/auth/localПрограммаAPI-токенEditorContent API/api/...Админ-панель/admin
Три пути к content API и админ-панель рядом

Если держать эту картину в голове, на большинство вопросов «почему у меня не получается...» ответ находится сам. API-токен не откроет админ-панель. Пароль админа не подходит к /api. Разрешение для Public - это разрешение для всех, включая обладателей остальных трёх видов учётных данных, потому что они могут просто не передавать данные и будут обработаны как роль Public.

Роль Public: начинайте с нуля#

Откройте Settings, затем в разделе Users and Permissions plugin выберите Roles. Встроенных ролей две: Public и Authenticated. Для каждого типа контента редактор роли показывает доступные действия: find (список), findOne (одна запись), create, update и delete. У single types свой набор действий. У нового типа контента для Public не отмечено ничего.

Правило формулируется просто и при этом редко соблюдается: включайте минимум действий на минимуме типов контента и никогда не включайте ничего, что записывает данные.

  • Для блога или рекламного сайта обычно достаточно find и findOne для тех типов контента, которые реально выводятся на публичных страницах.
  • Не включайте create, update и delete для Public. Если форме обратной связи нужно создавать записи, поставьте перед ней небольшой серверный маршрут или serverless-функцию, либо смиритесь с тем, что в один конкретный тип контента может писать кто угодно, и относитесь ко всему, что туда попадает, как к враждебному вводу.
  • Посмотрите, что ещё есть в списке. В разделе Users and Permissions есть user.find, user.findOne, user.count и другие. Если отметить user.find для Public, список ваших зарегистрированных пользователей вместе с их адресами email окажется в интернете.
  • В разделе Upload среди прочих есть upload.find, upload.findOne и upload.destroy. Файлы, на которые ссылается ваш контент, отдаются как обычная статика по своему URL, и эти разрешения им не нужны, а публичный destroy стал бы катастрофой.

У Public также по умолчанию включён набор действий auth (вход, регистрация, сброс пароля), потому что без них маршруты входа не работали бы. Именно поэтому важен следующий раздел.

Проверьте, что у вас получилось. С машины без токена, подставив вместо cms.example.com свой хост:

bash
$ curl -i https://cms.example.com/api/articles$ curl -i https://cms.example.com/api/pages$ curl -i https://cms.example.com/api/users

Тип контента, который вы не открывали, ответит HTTP/1.1 403 Forbidden и JSON-телом, где error.name равно ForbiddenError. Открытый тип ответит 200 и массивом data. Если вместо 403 пришёл 404, значит, такого маршрута нет: множественное имя типа контента отличается от того, что вы набрали. Это имя показано в Content-Type Builder как API ID (plural), и именно оно идёт в пути после /api/.

Что видит Public помимо самой записи

То, сколько графа связей вернётся вместе с записью, определяют два параметра запроса. По умолчанию Strapi 5 возвращает только собственные поля записи; связи, медиа и компоненты появляются, когда запрос просит их через populate. Запрос с ?populate=* просит один уровень всего. Используемая роль должна также иметь право читать связанный тип контента, поэтому открытие article не открывает author автоматически, но по неосторожности легко раскрыть больше задуманного, если открыть оба.

Второй параметр - пагинация. Если ничего не менять, клиент может запросить любой размер страницы. Ограничьте его в config/api.js:

config/api.js
module.exports = {  rest: {    defaultLimit: 25,    maxLimit: 100,    withCount: true,  },};

defaultLimit действует, когда запрос не указывает размер, а maxLimit - потолок, который запрос превысить не может. Небольшой maxLimit не остановит терпеливого скрейпера, но убирает выгрузку всей таблицы одним запросом. Если вы используете плагин GraphQL, у него есть аналогичные настройки (defaultLimit, maxLimit, depthLimit, amountLimit) в config/plugins.js, а песочницей Apollo управляет playgroundAlways; в продакшене оставьте её выключенной.

Роль Authenticated и регистрация#

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

Если на вашем сайте нет личного кабинета, ответ «нет», и для этого нужен один переключатель. В Users and Permissions plugin откройте Advanced settings и выключите Enable sign-ups. Когда он выключен, POST /api/auth/local/register перестаёт создавать аккаунты. Заодно проверьте остальные настройки на этой же странице:

НастройкаЧто сделать
Default role for authenticated usersОставьте Authenticated, никогда не роль с правом записи
Enable sign-upsВыключено, если люди на самом деле не регистрируются
One account per email addressВключено
Enable email confirmationВключить, если регистрация открыта и почта работает
Reset password pageУкажите URL сброса пароля вашего фронтенда, а не значение по умолчанию

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

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

У JWT, который выдаёт Users and Permissions, есть срок жизни и секрет подписи. Секрет - это jwtSecret, в сгенерированном проекте он берётся из JWT_SECRET. Срок жизни задаётся параметром jwt.expiresIn в config/plugins.js, и по умолчанию он большой (на момент написания 30 дней). Для чего-либо серьёзнее домашнего сайта его стоит сократить:

config/plugins.js
module.exports = ({ env }) => ({  'users-permissions': {    config: {      jwtSecret: env('JWT_SECRET'),      jwt: {        expiresIn: '7d',      },    },  },});

JWT не хранятся на сервере, поэтому списка активных сессий, которые можно отозвать, нет. Смягчить риск помогает уменьшение expiresIn, а смена JWT_SECRET - аварийный стоп-кран, который разом обесценивает все токены ценой выхода из системы всех конечных пользователей. В руководстве по Strapi в продакшене перечислены остальные секреты и сказано, что ломается при смене каждого из них.

API-токены: ограниченные, со сроком действия, по одному на потребителя#

API-токен - правильные учётные данные для программы. В Settings, Global settings, API Tokens выберите Create new API Token. Нужно заполнить четыре вещи.

  • Имя и описание: используйте имя потребителя, например marketing-site-build. Когда токен утечёт, по имени вы поймёте, какую систему чинить.
  • Token duration: 7 days, 30 days, 90 days или Unlimited. Выбирайте самый короткий срок, который вы готовы продлевать. Unlimited должен быть осознанным решением, а не способом побыстрее закрыть диалог.
  • Token type: Read-only, Full access или Custom.

Три типа сильно различаются по масштабу ущерба:

ТипЧто разрешаетДля чего
Read-onlyfind и findOne для всех типов контентаПолучение всего при сборке
Full accessВсе действия для всех типов контентаПочти ни для чего
CustomТолько отмеченные действия по типам контентаВыбор по умолчанию

Full access лучше избегать. Токен, который может сделать delete для каждой записи, в скрипте сборки, который к тому же выполняется на CI-сервере, доступном другим людям, - очень дорогая вещь, чтобы её потерять. Read-only безопасен для записи, но всё равно охватывает все типы контента, в том числе ненужные потребителю. Custom позволяет отметить ровно find и findOne для двух типов контента, которые выводит фронтенд, и ничего больше, и именно с него стоит начинать.

При сохранении значение токена показывается на экране. Strapi хранит хеш токена, вычисленный с солью API_TOKEN_SALT, поэтому в стандартной конфигурации скопировать значение можно только в этот момент. В новых релизах 5.x токен можно показать повторно, если настроен ключ шифрования (ENCRYPTION_KEY, который config/admin.js в сгенерированном проекте читает как secrets.encryptionKey); если такой ключ есть в вашем .env, держите его неизменным так же, как остальные секреты. В любом случае сразу положите значение в хранилище секретов того, кто будет им пользоваться.

Используйте его как bearer-токен:

bash
$ curl -i https://cms.example.com/api/articles \    -H "Authorization: Bearer $STRAPI_TOKEN"

Проверяйте не только успех, но и границу. Для Custom-токена с find только для articles:

bash
$ curl -i https://cms.example.com/api/pages -H "Authorization: Bearer $STRAPI_TOKEN"$ curl -i -X DELETE https://cms.example.com/api/articles/abc123 \    -H "Authorization: Bearer $STRAPI_TOKEN"

Оба запроса должны вернуть 403. Неверный или просроченный токен даёт 401 Unauthorized; действительный токен, которому это действие не разрешено, даёт 403. Умение различать эти два кода экономит много времени на отладке: 401 - про то, кто вы, 403 - про то, что вам можно.

Несколько привычек помогают держать токены в порядке:

  1. Один токен на потребителя. Если стейджинг и продакшен делят один токен, отозвать один из них, не сломав другой, невозможно.
  2. Никогда не отправляйте токен в браузер. Всё, что попало в клиентский бандл, публично. Токен в NEXT_PUBLIC_ или аналогичной переменной - это разрешение Public с лишними шагами. Если данные нужны браузеру, сделайте их публичными через роль Public (только чтение, несколько типов контента), а токен оставьте для серверного кода.
  3. Перевыпускайте при подозрении. На странице редактирования токена есть кнопка Regenerate, которая выдаёт новое значение и делает недействительным старое. Разрешения сохраняются.
  4. Меняйте по календарю. Для 90-дневных токенов запись в календаре за две недели до окончания дешевле простоя.
  5. Храните токены в окружении хоста, а не в репозитории. О том, почему так, рассказано в материале переменные окружения и секреты.

API-токены действуют только для content API. Они не могут войти в админ-панель и не могут менять типы контента, роли или настройки.

Transfer-токены - это другое

В тех же Global settings есть Transfer Tokens. Они разрешают strapi transfer - прямое копирование данных между двумя экземплярами Strapi - и выдаются с направлением Push или Pull. Их соль - TRANSFER_TOKEN_SALT. Transfer-токен с Pull может прочитать весь ваш контент и медиафайлы. Создавайте такой токен только на тот момент, когда он нужен, задавайте минимальный срок и удаляйте его после использования.

Админ-пользователи и админ-роли#

Админ-пользователи - это люди, которые входят на /admin. В Settings, Administration Panel, Roles на свежей установке три роли:

РольЧто может
Super AdminВсё, включая настройки, роли, токены и плагины
EditorСоздавать, читать, изменять, удалять и публиковать весь контент
AuthorСоздавать, читать, изменять и удалять только собственный контент

Первый человек, открывший /admin на свежей установке, создаёт Super Admin. Сделайте это сами, сразу, до того как направите на сервер публичный домен. После этого действует правило: Super Admin почти никому не нужен. Это единственная роль, которая может создавать API-токены, менять роль Public, устанавливать плагины и приглашать других админов, и её нельзя ни изменить, ни ограничить.

Приглашайте людей в Settings, Administration Panel, Users, кнопкой Invite new user. Если на сервере есть почтовый провайдер, человек получит ссылку. Если нет, Strapi покажет ссылку регистрации, которую вы скопируете и отправите вручную; она перестаёт действовать после использования, и вставлять её в публичный канал не нужно. Новый админ выбирает пароль сам по этой ссылке.

Встроенным ролям не хватает детализации. Дублируйте Editor или Author и правьте копию. Каждая роль - это сетка из типов контента и действий (Create, Read, Update, Delete, Publish), плюс поля внутри каждого типа, плюс разделы Plugins и Settings. Два механизма делают это практичным:

  • Права на уровне полей. Внутри типа контента раскройте поля и снимите галочки у тех, которые роль не должна видеть или менять. Хороший пример - поле цены, которое правят только менеджеры.
  • Условия. Разрешение можно ограничить условием вроде «Is creator», тогда Author может править созданные им записи и ни одной чужой. Без условия разрешение действует на каждую запись.

К разрешениям в Settings стоит присмотреться отдельно. Роли можно разрешить читать или создавать API-токены либо читать роли, не давая права трогать что-то ещё. Выдавайте такие права только тем, кто управляет сайтом.

Что стоит сделать на любой установке:

  1. Создайте именной Super Admin для себя и второй, запасной, на аварийный случай, а не один общий логин.
  2. Используйте настоящий адрес email каждого человека. Общий ящик admin@ делает журнал действий бесполезным, а каждый сброс пароля - переговорами.
  3. Когда человек уходит, деактивируйте его аккаунт (в списке Users есть переключатель активности), а не ждите, пока он истечёт сам.
  4. Используйте длинные уникальные пароли из менеджера паролей. В community-редакции Strapi нет встроенной двухфакторной авторизации для админ-панели; единый вход (SSO) входит в платные редакции. Если это важно, проверьте документацию Strapi для вашей версии.

Если вы заблокировали себя, трогать базу данных не обязательно. Из каталога проекта CLI может создать нового Super Admin или сбросить пароль:

bash
$ npm run strapi admin:create-user -- --firstname=Ada --lastname=Admin \    --email=ada@example.com --password='a-long-unique-password'$ npm run strapi admin:reset-user-password -- --email=ada@example.com --password='another-one'

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

Права из кода вместо щелчков мышью#

Роли и разрешения - это данные. Они живут в базе данных, а не в проекте, и из этого следует неожиданное для многих: деплой из Git на второе окружение их не переносит. Ваш стейджинг может оказаться широко открытым, а продакшен - наглухо закрытым при том, что код, которому нужно разрешение, уже работает. Проверяйте после каждой миграции и подумайте о том, чтобы записать разрешения Public в коде, чтобы пересозданное окружение начинало с известного состояния.

Шаблон, который используют демо-проекты Strapi, - функция bootstrap в src/index.js, которая находит роль и создаёт нужные записи разрешений. Действия называются api::<singular>.<singular>.<action>:

src/index.js
module.exports = {  register() {},  async bootstrap({ strapi }) {    const publicRole = await strapi.db      .query('plugin::users-permissions.role')      .findOne({ where: { type: 'public' } });    const actions = ['api::article.article.find', 'api::article.article.findOne'];    for (const action of actions) {      const existing = await strapi.db        .query('plugin::users-permissions.permission')        .findOne({ where: { action, role: publicRole.id } });      if (!existing) {        await strapi.db          .query('plugin::users-permissions.permission')          .create({ data: { action, role: publicRole.id } });      }    }  },};

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

Защита периметра: прокси, CORS и ограничение частоты#

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

CORS настраивается middleware strapi::cors в config/middlewares.js. По умолчанию разрешён любой origin. CORS - правило браузера и curl не останавливает, но строгий список не даёт чужим сайтам обращаться к вашему API через браузеры своих посетителей:

config/middlewares.js
module.exports = [  'strapi::logger',  'strapi::errors',  'strapi::security',  {    name: 'strapi::cors',    config: {      origin: ['https://www.example.com'],      methods: ['GET', 'HEAD', 'OPTIONS'],      headers: ['Content-Type', 'Authorization'],      keepHeaderOnError: true,    },  },  'strapi::poweredBy',  'strapi::query',  'strapi::body',  'strapi::session',  'strapi::favicon',  'strapi::public',];

Сохраняйте порядок middleware как в сгенерированном файле и меняйте только запись strapi::cors. Админ-панель отдаётся с того же origin, что и API, поэтому список origin для фронтенда на неё не влияет.

Ограничение частоты - задача обратного прокси, для маршрутов, где идёт подбор: вход в админку /admin/login и вход и регистрация конечных пользователей под /api/auth/local. У самого Strapi есть настройки ограничения для входа в админку (блок admin.rateLimit в config/admin.js), и их стоит включить, но лимит на прокси работает одинаково в любой версии Strapi и заодно защищает процесс от нагрузки. В nginx:

nginx
limit_req_zone $binary_remote_addr zone=strapi_auth:10m rate=10r/m;server {    # ... TLS and the usual proxy_pass block ...    location = /admin/login {        limit_req zone=strapi_auth burst=5 nodelay;        proxy_pass http://127.0.0.1:1337;    }    location /api/auth/ {        limit_req zone=strapi_auth burst=5 nodelay;        proxy_pass http://127.0.0.1:1337;    }}

Лишние запросы по умолчанию получают 503; добавьте limit_req_status 429;, если предпочитаете более точный код. Остальная настройка прокси описана в руководстве по обратному прокси nginx, а в материале правила файрвола, которые важны рассказано, как не выпускать порт Node в публичный интернет, если прокси работает на той же машине.

Стоит знать ещё один структурный вариант: админ-панель и API не обязаны быть публичными вместе. Если редакторы работают из известных сетей, можно ограничить /admin на прокси по адресу, оставив /api открытым для фронтенда. Это дополнительный слой, а не замена ролям.

Strapi на RE:NODE#

На RE:NODE Strapi - приложение в один клик на странице хостинга приложений. Оно устанавливается и собирается при первом запуске сервера, данные лежат в SQLite на сервере, поэтому закрытие доступа начинается сразу после первого запуска: откройте /admin, создайте Super Admin и пройдите по шагам этого руководства, прежде чем делиться адресом. Слот прокси выпускает сертификат, когда A-запись указывает на показанный адрес, а настоящий IP клиента приходит в X-Forwarded-For, что важно для ограничений частоты выше.

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

Файл .env Strapi лежит в папке проекта, и его можно править в файловом менеджере или по SFTP. Поскольку роли, разрешения и API-токены хранятся в базе данных, защищает их слот резервной копии, созданной вручную или на вкладке Schedules; иногда восстанавливайте копию, чтобы убедиться, что она работает: токены вернутся только в том случае, если соль в .env осталась прежней.

Проверка результата#

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

  1. Аноним, закрытый тип. Выполните curl -i к типу контента, который вы не открывали. Ожидайте 403.
  2. Аноним, открытый тип. Выполните curl -i к открытому. Ожидайте 200 и только те поля, которые вы хотели опубликовать.
  3. Аноним, запись. Выполните curl -i -X POST к открытой коллекции с небольшим JSON-телом вида {"data":{}}. Ожидайте 403.
  4. Аноним, пользователи. curl -i https://cms.example.com/api/users. Ожидайте 403.
  5. Регистрация. curl -i -X POST https://cms.example.com/api/auth/local/register с JSON-телом. Если регистрация выключена, ожидайте ошибку, а не новый аккаунт и JWT.
  6. Токен, в пределах прав. Запросите то, для чего предназначен токен. Ожидайте 200.
  7. Токен, за пределами прав. Запросите то, что ему недоступно. Ожидайте 403.
  8. Неверный токен. Измените один символ. Ожидайте 401.
  9. Админ-аккаунты. Просмотрите Settings, Administration Panel, Users и удалите или деактивируйте тех, кого не узнаёте. Посчитайте Super Admin.
  10. Список API Tokens. Удалите каждый токен с именем, которое никто не может объяснить, и каждый токен Full access, который можно заменить на Custom.

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

Устранение неполадок#

`403 Forbidden` на открытом вами типе. Роль сохранена, но запрос сопоставлен с другой. Проверьте, не отправили ли вы заголовок Authorization: действующий JWT конечного пользователя использует роль Authenticated, у которой своя сетка разрешений, и там может не быть того, что есть у Public. Убедитесь также, что отмечено нужное действие, find или findOne; запись по ID требует findOne.

`401 Unauthorized` с токеном, который выглядел верным. Пробел или перевод строки в начале или конце значения, отсутствие префикса Bearer , токен с истёкшим сроком или изменившийся API_TOKEN_SALT. Создайте новый токен, чтобы исключить соль.

`403` для токена после его редактирования. Вы переключили тип на Custom и сохранили, не отметив ничего. Custom-токен без действий не разрешает ничего.

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

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

Новый тип контента не виден фронтенду. Он начинает закрытым. Откройте find и findOne у нужной роли или у токена и проверьте снова.

В Settings нет страницы Roles. У вашей админ-роли нет права читать роли. Попросите Super Admin.

FAQ#

Чем роль Public отличается от API-токена?

Роль Public действует на любого, кто не передаёт учётные данные, поэтому её разрешения открыты всему интернету. API-токен действует только на запросы, которые его несут, и имеет собственный список разрешений. Public используйте для данных, которые может читать весь мир, а для всего остального, что нужно программам, - Custom-токен.

Безопасно ли вставлять Read-only API-токен во фронтенд?

Не в браузерный бандл. Read-only не даёт записывать, но читает все типы контента, в том числе те, что вы не собирались публиковать, и любой может скопировать его из исходника страницы. Используйте его только в серверном коде или на шаге сборки.

Можно ли сделать API-токен, который читает только один тип контента?

Да. Создайте токен типа Custom и отметьте find и findOne только для этого типа контента. Проверьте на другом типе, что ответ будет 403.

Как не дать посторонним регистрировать аккаунты?

В Settings, Users and Permissions plugin, Advanced settings выключите Enable sign-ups. Затем отправьте POST на /api/auth/local/register и убедитесь, что аккаунт не создан. Если регистрация нужна, оставьте включённым подтверждение email и ограничьте частоту запросов к маршруту.

Почему API-токен Strapi перестаёт работать после повторного деплоя?

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

Переезжают ли роли и разрешения вместе с кодом?

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

Это было полезно?