Local Database

A Navegação segura do Google v5 espera que o cliente mantenha um banco de dados local, exceto quando o cliente escolhe o modo de tempo real sem armazenamento. O formato e o armazenamento desse banco de dados local são de responsabilidade do cliente. Conceitualmente, o conteúdo desse banco de dados local pode ser pensado como uma pasta que contém várias listas como arquivos, e o conteúdo desses arquivos são hashes SHA256 ou os prefixos correspondentes com quatro bytes, que é o comprimento de hash mais usado.

Listas disponíveis

As listas são identificadas por nomes distintos que seguem uma convenção de nomenclatura em que o nome contém um sufixo que indica o comprimento do hash esperado na lista. As listas de hashes com o mesmo tipo de ameaça, mas comprimentos diferentes, serão listas com nomes separados e qualificadas com um sufixo que indica o comprimento do hash.

As listas a seguir estão disponíveis para uso com os métodos de lista de hashes.

Nome da lista Enum ThreatType v4 correspondente Descrição
gc-32b Nenhum Esta lista é uma lista de cache global. É uma lista especial usada apenas no modo de operação em tempo real.
se-4b SOCIAL_ENGINEERING Esta lista contém ameaças do tipo SOCIAL_ENGINEERING.
mw-4b MALWARE Esta lista contém ameaças do tipo MALWARE para plataformas de computador.
uws-4b UNWANTED_SOFTWARE Esta lista contém ameaças do tipo UNWANTED_SOFTWARE para plataformas de computador.
uwsa-4b UNWANTED_SOFTWARE Esta lista contém ameaças do tipo UNWANTED_SOFTWARE para plataformas Android.
pha-4b POTENTIALLY_HARMFUL_APPLICATION Esta lista contém ameaças do tipo POTENTIALLY_HARMFUL_APPLICATION para plataformas Android.

Outras listas podem ficar disponíveis em uma data posterior, quando a tabela acima será expandida, e os resultados do método hashList.list vão mostrar um resultado semelhante com as listas mais atualizadas.

Atualizações do banco de dados

O cliente vai chamar regularmente o método hashList.get ou o método hashLists.batchGet para atualizar o banco de dados. Como o cliente típico vai querer atualizar várias listas de uma vez, é recomendável usar o método hashLists.batchGet.

Os nomes das listas nunca serão renomeados. Além disso, uma lista que já apareceu nunca será removida. Se ela não for mais útil, vai ficar vazia, mas vai continuar existindo. Portanto, é adequado codificar esses nomes no código do cliente da Navegação segura do Google.

O método hashList.get e o método hashLists.batchGet oferecem suporte a atualizações incrementais. O uso de atualizações incrementais economiza largura de banda e melhora o desempenho. As atualizações incrementais funcionam fornecendo um delta entre a versão da lista do cliente e a versão mais recente da lista. Se um cliente for implantado recentemente e não tiver versões disponíveis, uma atualização completa estará disponível. A atualização incremental contém índices de remoção e adições. Espera-se que o cliente primeiro remova as entradas nos índices especificados do banco de dados local e depois aplique as adições.

Por fim, para evitar a corrupção, o cliente precisa verificar os dados armazenados com a soma de verificação fornecida pelo servidor. Sempre que a soma de verificação não corresponder, o cliente precisará realizar uma atualização completa.

Como decodificar o conteúdo da lista

Decodificação de hashes e prefixos de hash

Todas as listas são enviadas usando uma codificação especial para reduzir o tamanho. Essa codificação funciona reconhecendo que as listas do Google Safe Browsing contêm, conceitualmente, um conjunto de hashes ou prefixos de hash, que são estatisticamente indistinguíveis de números inteiros aleatórios. Se você classificar esses números inteiros e calcular a diferença adjacente, essa diferença adjacente será "pequena". A codificação Golomb-Rice aproveita essa pequena quantidade.

Suponha que três expressões de prefixo de caminho de sufixo de host, a.example.com/, b.example.com/ e y.example.com/, sejam transmitidas usando prefixos de hash de 4 bytes. Suponha ainda que o parâmetro de Rice, denotado por k, seja escolhido como

  1. O servidor começaria calculando o hash completo dessas strings, que são, respectivamente:
291bc5421f1cd54d99afcc55d166e2b9fe42447025895bf09dd41b2110a687dc  a.example.com/
1d32c5084a360e58f1b87109637a6810acad97a861a7769e8f1841410d2a960c  b.example.com/
f7a502e56e8b01c6dc242b35122683c9d25d07fb1f532d9853eb0ef3ff334f03  y.example.com/

O servidor forma prefixos de hash de 4 bytes para cada um dos itens acima, que são os primeiros 4 bytes do hash completo de 32 bytes, interpretados como números inteiros de 32 bits big-endian. A big-endianness se refere ao fato de que o primeiro byte do hash completo se torna o byte mais significativo do número inteiro de 32 bits. Essa etapa resulta nos números inteiros 0x291bc542, 0x1d32c508 e 0xf7a502e5.

É necessário que o servidor classifique esses três prefixos de hash lexicograficamente (o que equivale à classificação numérica em big endian), e o resultado da classificação é 0x1d32c508, 0x291bc542, 0xf7a502e5. O primeiro prefixo de hash é armazenado sem alterações no campo first_value.

O servidor calcula as duas diferenças adjacentes, que são 0xbe9003a e 0xce893da3, respectivamente. Considerando que k é escolhido como 30, o servidor divide esses dois números em partes de quociente e de resto com 2 e 30 bits, respectivamente. Para o primeiro número, a parte do quociente é zero e o resto é 0xbe9003a. Para o segundo número, a parte do quociente é 3 porque os dois bits mais significativos são 11 em binário e o resto é 0xe893da3. Para um determinado quociente q, ele é codificado em (1 << q) - 1 usando exatamente 1 + q bits. O restante é codificado diretamente usando k bits. A parte do quociente do primeiro número é codificada como 0, e a parte do resto é 001011111010010000000000111010 em binário. A parte do quociente do segundo número é codificada como 0111, e a parte do resto é 001110100010010011110110100011.

Quando esses números são formados em uma string de bytes, o little endian é usado. Conceitualmente, pode ser mais fácil imaginar uma string de bits longa sendo formada a partir dos bits menos significativos: pegamos a parte do quociente do primeiro número e inserimos a parte restante do primeiro número; em seguida, inserimos a parte do quociente do segundo número e a parte restante. Isso deve resultar no seguinte número grande (quebras de linha e comentários adicionados para maior clareza):

001110100010010011110110100011 # Second number, remainder part
0111 # Second number, quotient part
001011111010010000000000111010 # First number, remainder part
0 # First number, quotient part

Escrito em uma única linha, seria

00111010001001001111011010001101110010111110100100000000001110100

Obviamente, esse número excede em muito os 8 bits disponíveis em um único byte. A codificação little endian pega os 8 bits menos significativos desse número e os gera como o primeiro byte, que é 01110100. Para maior clareza, podemos agrupar a bitstring acima em grupos de oito, começando pelos bits menos significativos:

0 01110100 01001001 11101101 00011011 10010111 11010010 00000000 01110100

A codificação little-endian pega cada byte da direita e o coloca em uma bytestring:

01110100
00000000
11010010
10010111
00011011
11101101
01001001
01110100
00000000

Como antepomos conceitualmente novas partes ao número grande à esquerda (ou seja, adicionamos mais bits significativos), mas codificamos da direita (ou seja, os bits menos significativos), a codificação e a decodificação podem ser realizadas de forma incremental.

Isso resulta em

additions_four_bytes {
  first_value: 489866504
  rice_parameter: 30
  entries_count: 2
  encoded_data: "t\000\322\227\033\355It\000"
}

O cliente simplesmente segue as etapas acima em ordem inversa para decodificar os prefixos de hash.

Como decodificar índices de remoção

Os índices de remoção são codificados usando a mesma técnica acima, com números inteiros de 32 bits.

Frequência de atualização

O cliente precisa inspecionar o valor retornado pelo servidor no campo minimum_wait_duration e usá-lo para programar a próxima atualização do banco de dados. Esse valor pode ser zero (o campo minimum_wait_duration está completamente ausente). Nesse caso, o cliente PRECISA realizar imediatamente outra atualização.