HTTP-запросы содержат заголовки, такие как User-Agent или Content-Type. Помимо заголовков, добавляемых браузерами, приложения для Android могут добавлять дополнительные заголовки, например Cookie или Referrer, через дополнительный параметр Intent EXTRA_HEADERS. В целях безопасности Chrome фильтрует некоторые дополнительные заголовки в зависимости от того, как и где запускается намерение.
Для междоменных запросов требуется дополнительный уровень безопасности, поскольку клиент и сервер не принадлежат одной стороне. В этом руководстве рассказывается, как запускать такие запросы через специальные вкладки Chrome, то есть намерения, запущенные из приложений, которые открывают URL в вкладке браузера. До выхода Chrome 83 разработчики могли добавлять любые заголовки при запуске пользовательской вкладки. Начиная с версии 83 Chrome фильтрует все заголовки из разных источников, кроме внесенных в белый список, поскольку они представляют угрозу безопасности. Начиная с версии Chrome 86, к междоменным запросам можно прикреплять заголовки, не включенные в белый список, если сервер и клиент связаны с помощью Digital Asset Links. В таблице ниже показано, как это работает.
| Версия Chrome | Разрешенные заголовки CORS |
|---|---|
| до Chrome 83 | в белом списке, не в белом списке |
| Chrome 83–85 | в белом списке |
| Chrome 86 и более поздних версий | в белом списке, не в белом списке, если настроена ссылка на цифровой объект |
Таблица 1. Фильтрация заголовков CORS, не включенных в белый список.
В этой статье рассказывается, как настроить подтвержденное подключение между сервером и клиентом и использовать его для отправки HTTP-заголовков, как включенных, так и не включенных в белый список. Вы можете перейти к разделу Добавление дополнительных заголовков в намерения пользовательских вкладок, чтобы посмотреть код.
Фон
Заголовки запросов CORS, включенные и не включенные в белый список
Совместное использование ресурсов между разными источниками (CORS) позволяет веб-приложению из одного источника запрашивать ресурсы из другого источника. Список заголовков, одобренных CORS, приведен в стандарте HTML. Примеры заголовков из белого списка приведены в таблице ниже.
| Заголовок | Описание |
|---|---|
| accept-language | рекламирует естественные языки, которые понимает клиент |
| content-language | описывает язык, предназначенный для текущей аудитории |
| content-type | Указывает тип медиаконтента ресурса. |
Таблица 2. Примеры заголовков CORS из белого списка.
Заголовки из белого списка считаются безопасными, поскольку они не содержат конфиденциальной информации о пользователях и вряд ли приведут к выполнению сервером потенциально опасных операций.
Примеры заголовков, не включенных в белый список, приведены в таблице ниже.
| Заголовок | Описание |
|---|---|
| токен на предъявителя | выполняет аутентификацию клиента на сервере; |
| происхождение | Указывает источник запроса. |
| печенье | содержит файлы cookie, заданные сервером |
Таблица 3. Примеры заголовков CORS, не включенных в белый список.
Стандарт HTML не рекомендует добавлять в запросы CORS заголовки, не включенные в белый список, и серверы предполагают, что междоменные запросы содержат только заголовки из белого списка. Если бы отправка заголовков, не включенных в белый список, из доменов разных источников была разрешена, вредоносные сторонние приложения могли бы создавать заголовки, которые бы использовали файлы cookie, хранящиеся в Chrome (или другом браузере) и прикрепляемые к запросам. Файлы cookie могут аутентифицировать транзакции вредоносного сервера, которые в противном случае были бы невозможны.
Как прикреплять заголовки из белого списка CORS к запросам пользовательских вкладок
Специальные вкладки – это особый способ запуска веб-страниц в настроенной вкладке браузера. Намерения для пользовательских вкладок можно создавать с помощью CustomTabsIntent.Builder(). Вы также можете прикрепить заголовки к этим намерениям, используя Bundle с флагом Browser.EXTRA_HEADERS:
CustomTabsIntent intent = new CustomTabsIntent.Builder(session).build();
Bundle headers = new Bundle();
headers.putString("bearer-token", "Some token");
headers.putString("redirect-url", "Some redirect url");
intent.intent.putExtra(Browser.EXTRA_HEADERS, headers);
intent.launchUrl(Activity.this, Uri.parse("http://www.google.com"));
Мы всегда можем прикрепить заголовки из белого списка к запросам CORS пользовательских вкладок. Однако по умолчанию Chrome фильтрует заголовки, не включенные в белый список. Хотя другие браузеры могут вести себя иначе, разработчикам следует ожидать, что заголовки, не включенные в белый список, будут заблокированы.
Чтобы добавить в пользовательские вкладки заголовки, не включенные в белый список, сначала подтвердите перекрестное подключение с помощью ссылки на цифровой доступ. В следующем разделе показано, как настроить эти заголовки и запустить намерение пользовательских вкладок с необходимыми заголовками.
Как добавить дополнительные заголовки в намерения пользовательских вкладок
Как настроить ссылки на цифровые объекты
Чтобы разрешить передачу заголовков, не включенных в белый список, через интенты пользовательских вкладок, необходимо настроить связь цифровых объектов между приложением Android и веб-приложением. Это позволит подтвердить, что автор владеет обоими приложениями.
Чтобы настроить связь цифровых объектов, следуйте официальному руководству. Для связи используйте значение "delegate_permission/common.use_as_origin", которое указывает, что после подтверждения связи оба приложения будут относиться к одному источнику.
Как создать намерение для пользовательской вкладки с дополнительными заголовками
Создать намерение для пользовательских вкладок можно несколькими способами. Вы можете использовать конструктор, доступный в androidX, добавив библиотеку в зависимости сборки:
implementation 'androidx.browser:browser:1.2.0'
Создайте намерение и добавьте дополнительные заголовки:
CustomTabsIntent constructExtraHeadersIntent(CustomTabsSession session) {
CustomTabsIntent intent = new CustomTabsIntent.Builder(session).build();
// Example non-cors-approvelisted headers.
Bundle headers = new Bundle();
headers.putString("bearer-token", "Some token");
headers.putString("redirect-url", "Some redirect url");
intent.intent.putExtra(Browser.EXTRA_HEADERS, headers);
return intent;
}
Как настроить подключение пользовательских вкладок для проверки связи с объектом
Подключение пользовательских вкладок используется для настройки CustomTabsSession между приложением и вкладкой Chrome. Это нужно, чтобы убедиться, что приложение и веб-приложение принадлежат одному источнику.
Проверка будет пройдена, только если ссылки на цифровые объекты настроены правильно.
Рекомендуется позвонить по номеру CustomTabsClient.warmup(). Это позволяет браузеру предварительно инициализироваться в фоновом режиме и ускоряет открытие URL.
// Set up a connection that warms up and validates a session.
CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(@NonNull ComponentName name,
@NonNull CustomTabsClient client) {
// Create session after service connected.
mSession = client.newSession(callback);
client.warmup(0);
// Validate the session as the same origin to allow cross origin headers.
mSession.validateRelationship(CustomTabsService.RELATION_USE_AS_ORIGIN,
Uri.parse(url), null);
}
@Override
public void onServiceDisconnected(ComponentName componentName) { }
};
Настройте обратный вызов, который запускает намерение после проверки
В сеанс передано значение CustomTabsCallback. Мы настроили его onRelationshipValidationResult() так, чтобы после успешной проверки источника запускалось ранее созданное действие CustomTabsIntent.
// Set up a callback that launches the intent after session validated.
CustomTabsCallback callback = new CustomTabsCallback() {
@Override
public void onRelationshipValidationResult(int relation, @NonNull Uri requestedOrigin,
boolean result, @Nullable Bundle extras) {
// Launch custom tabs intent after session was validated as the same origin.
CustomTabsIntent intent = constructExtraHeadersIntent(mSession);
intent.launchUrl(MainActivity.this, Uri.parse(url));
}
};
Привязка подключения к сервису пользовательских вкладок
Привязка сервиса запускает его, и в конечном итоге будет вызван метод onCustomTabsServiceConnected() подключения. Не забудьте отменить привязку сервиса. Привязка и отмена привязки обычно выполняются в методах жизненного цикла объекта activity onStart() и onStop().
// Bind the custom tabs service connection.
// Call this in onStart()
CustomTabsClient.bindCustomTabsService(this,
CustomTabsClient.getPackageName(MainActivity.this, null), connection);
// …
// Unbind the custom tabs service.
// Call this in onStop().
unbindService(connection);
Код демонстрационного приложения
Подробнее о сервисе специальных вкладок… Пример работающего приложения можно найти в репозитории android-browser-helper на GitHub.
Сводка
В этом руководстве показано, как добавлять произвольные заголовки в запросы CORS пользовательских вкладок. Заголовки из белого списка можно прикреплять к каждому запросу CORS пользовательских вкладок. Заголовки, не включенные в белый список, обычно считаются небезопасными в запросах CORS, и Chrome по умолчанию их фильтрует. Прикреплять их можно только к клиентам и серверам одного происхождения, подтвержденным с помощью Digital Asset Links.