As solicitações HTTP contêm cabeçalhos como User-Agent ou Content-Type. Além dos cabeçalhos anexados pelos navegadores, os apps Android podem adicionar outros cabeçalhos, como Cookie ou Referrer, usando o extra de intent EXTRA_HEADERS. Por motivos de segurança, o Chrome filtra alguns dos cabeçalhos extras
dependendo de como e onde uma intent é iniciada.
As solicitações entre origens exigem uma camada extra de segurança, já que o cliente e o servidor não pertencem à mesma parte. Este guia discute o lançamento dessas solicitações pelas guias personalizadas do Chrome, ou seja, intents iniciados por apps que abrem um URL na guia do navegador. Até o Chrome 83, os desenvolvedores podiam adicionar qualquer cabeçalho ao iniciar uma guia personalizada. A partir da versão 83, o Chrome começou a filtrar todos os cabeçalhos entre origens, exceto os permitidos, já que os cabeçalhos não permitidos representavam um risco de segurança. A partir do Chrome 86, é possível anexar cabeçalhos não permitidos a solicitações de origem cruzada quando o servidor e o cliente estão relacionados usando um link de recurso digital. Esse comportamento está resumido na tabela a seguir:
| Versão do Chrome | Cabeçalhos CORS permitidos |
|---|---|
| antes do Chrome 83 | approvelisted, non-approvelisted |
| Chrome 83 a 85 | na lista de permissão |
| no Chrome 86 e em versões mais recentes | na lista de permissões e não na lista de permissões quando um link de ativo digital é configurado |
Tabela 1: Filtragem de cabeçalhos CORS não incluídos na lista de permissões.
Este artigo mostra como configurar uma conexão verificada entre o servidor e o cliente e usar isso para enviar cabeçalhos HTTP na lista de permissões e fora dela. Você pode pular para Como adicionar cabeçalhos extras a intents de guias personalizadas para conferir o código.
Contexto
Cabeçalhos de solicitação CORS na lista de aprovação x cabeçalhos de solicitação CORS fora da lista de aprovação
O Compartilhamento de recursos entre origens (CORS) permite que um aplicativo da Web de uma origem solicite recursos de uma origem diferente. A lista de cabeçalhos CORS-approvelisted é mantida no HTML Standard. Exemplos de cabeçalhos na lista de permissões são mostrados na próxima tabela:
| Cabeçalho | Descrição |
|---|---|
| accept-language | anuncia idiomas naturais que o cliente entende |
| content-language | descreve a linguagem destinada ao público atual |
| content-type | indica o tipo de mídia do recurso |
Tabela 2: Exemplo de cabeçalhos CORS na lista de permissões.
Os cabeçalhos na lista de permissões são considerados seguros porque não contêm informações sensíveis do usuário e não causam operações potencialmente prejudiciais no servidor.
Confira exemplos de cabeçalhos não aprovados na tabela a seguir:
| Cabeçalho | Descrição |
|---|---|
| bearer-token | autentica o cliente em um servidor |
| origem | indica a origem da solicitação |
| biscoito | contém cookies definidos pelo servidor |
Tabela 3: Exemplo de cabeçalhos CORS não aprovados.
O padrão HTML desencoraja a anexação de cabeçalhos não aprovados a solicitações de CORS, e os servidores presumem que as solicitações entre origens contêm apenas cabeçalhos aprovados. O envio de cabeçalhos não aprovados de domínios de origem cruzada permitiria que apps maliciosos de terceiros criassem cabeçalhos que usam cookies de usuário armazenados e anexados a solicitações pelo Chrome (ou outro navegador). Os cookies podem autenticar transações maliciosas do servidor que não seriam possíveis de outra forma.
Como anexar cabeçalhos aprovados pelo CORS a solicitações de guias personalizadas
As guias personalizadas são uma maneira especial de abrir páginas da Web em uma guia do navegador personalizada. É possível criar intents de guia personalizada usando CustomTabsIntent.Builder(). Também é possível anexar cabeçalhos a essas intents usando um Bundle com a flag 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"));
Sempre podemos anexar cabeçalhos na lista de permissões a solicitações de CORS de guias personalizadas. No entanto, o Chrome filtra os cabeçalhos não aprovados por padrão. Embora outros navegadores possam ter comportamentos diferentes, os desenvolvedores devem esperar que os cabeçalhos não aprovados sejam bloqueados em geral.
A maneira aceita de incluir cabeçalhos não aprovados em guias personalizadas é primeiro verificar a conexão entre origens usando um link de acesso digital. A próxima seção mostra como configurar esses cabeçalhos e iniciar uma intent do Custom Tabs com eles.
Como adicionar cabeçalhos extras a intents de guias personalizadas
Configurar Digital Asset Links
Para permitir que cabeçalhos não aprovados sejam transmitidos por intents de guias personalizadas, é necessário configurar um link de recurso digital entre o aplicativo Android e o aplicativo da Web que verifique se o autor é proprietário dos dois aplicativos.
Siga o guia oficial para configurar uma vinculação de recursos digitais. Para a relação de link, use "delegate_permission/common.use_as_origin", que indica que os dois apps pertencem à mesma origem depois que o link é verificado.
Criar uma intent de guia personalizada com cabeçalhos extras
Há várias maneiras de criar uma intent do Custom Tabs. Você pode usar o builder disponível no AndroidX adicionando a biblioteca às dependências de build:
implementation 'androidx.browser:browser:1.2.0'
Crie a intent e adicione cabeçalhos extras:
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;
}
Configurar uma conexão de guias personalizadas para validar o Asset Link
Uma conexão de guias personalizadas é usada para configurar um CustomTabsSession entre o app e a
guia do Chrome. Precisamos da sessão para verificar se o app e o web app pertencem à mesma origem.
A verificação só será aprovada se os links de recursos digitais estiverem configurados corretamente.
Recomendamos ligar para CustomTabsClient.warmup(). Ele permite que o aplicativo do navegador
faça a pré-inicialização em segundo plano e acelere o processo de abertura de 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) { }
};
Configurar um callback que inicia a intent após a validação
O CustomTabsCallback foi transmitido para a sessão. Configuramos o onRelationshipValidationResult() para iniciar o CustomTabsIntent criado anteriormente
depois que a verificação de origem for concluída.
// 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));
}
};
Vincular a conexão de serviço das guias personalizadas
A vinculação do serviço inicia o serviço, e o onCustomTabsServiceConnected() da conexão
será chamado eventualmente. Não se esqueça de desvincular o serviço adequadamente. A vinculação e a desvinculação
são feitas geralmente nos métodos de ciclo de vida da atividade onStart() e 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);
Código do aplicativo de demonstração
Saiba mais sobre o serviço de guias personalizadas aqui. Consulte o repositório android-browser-helper no GitHub para conferir um exemplo de app funcional.
Resumo
Este guia mostrou como adicionar cabeçalhos arbitrários a solicitações CORS de guias personalizadas. Os cabeçalhos aprovados podem ser anexados a todas as solicitações CORS de guias personalizadas. Os cabeçalhos não incluídos na lista de aprovação geralmente são considerados não seguros em solicitações de CORS, e o Chrome os filtra por padrão. A anexação é permitida apenas para clientes e servidores da mesma origem, verificados por uma vinculação de recursos digitais.