Добавьте дополнительные заголовки HTTP-запросов

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.