A claim dentro da claim: decodificando um perfil de dispositivo escondido no payload
O desafio
Um coletor de loja da Brightloom passou uma compra bem abaixo do preço de prateleira, e a auditoria mostra que o desconto foi aplicado pelo software do caixa, não digitado por alguém. A claim scope do aparelho diz que ele pode ler o catálogo e registrar uma venda, nada mais, e o colega que o segurava não tem permissão de desconto na própria conta. A permissão não vem da pessoa. Está no token do aparelho, um nível abaixo de onde todo mundo olhou. Informe o desconto, em porcentagem, a que esse dispositivo tem direito silenciosamente.
O que você vai aprender
- Reconhecer um documento codificado embutido dentro de uma claim do token
- Decodificar um valor base64 aninhado e ler sua estrutura
- Converter pontos-base em porcentagem
- Explicar por que duas fontes de permissão em um mesmo token acabam divergindo
- Descrever por que embutir um perfil em um token o torna público para quem o possui
Habilidades testadas
Pré-requisitos
- O que é um JWT e que seu payload é codificado, não criptografado
Como funciona
O payload de um JWT é JSON, e valores JSON são strings. Nada impede que um serviço coloque um segundo documento inteiro dentro de uma dessas strings, codificado em base64, e muitos fazem isso: um perfil de dispositivo, um conjunto de feature flags, uma sessão serializada de um sistema mais antigo. A decodificação externa mostra o invólucro, e se você parar por aí a metade interessante continua sendo uma única linha longa e opaca que parece algo que você não deveria ler.
Não é. Base64 é codificação de transporte. Quem tiver o token tem o perfil por completo, e em um aparelho compartilhado de chão de loja isso é uma audiência ampla. Qualquer coisa embutida dessa forma deve ser tratada como publicada.
O problema mais profundo é ter duas respostas para uma mesma pergunta. scope diz que o dispositivo pode ler e vender. ctx diz que ele é do tier colleague, com direito a trinta por cento de desconto e flags de sobrescrita de preço. As duas são assinadas, as duas são genuínas, e qual delas um determinado trecho de código lê é o que esse trecho aplica. Permissões deveriam estar em um único lugar que todo controle consulta, não em uma claim que os controles ignoram e a aplicação confia.
Erros comuns
- Responder 3000. Esse é o valor de
discount_bps. Pontos-base são centésimos de por cento, então o valor correto é 30. - Parar na claim scope.
pos.read pos.saleé verdadeiro, e é justamente por isso que ninguém olhou mais adiante. - Tratar ctx como a assinatura. A assinatura é o terceiro segmento, depois do segundo ponto.
ctxé um valor dentro do payload. - Responder com o nome de uma flag.
pos.price_overrideé como o desconto é aplicado, mas o rótulo pede a porcentagem. - Responder h.mbatha. Essa é a pessoa para quem o aparelho foi provisionado, não o que ele tem direito.
Como se proteger
Coloque as permissões em um único lugar e mantenha segredos totalmente fora dos tokens.
- Expresse as permissões somente em
scope, e faça o caixa derivar toda decisão a partir dele. Se um dispositivo precisa de um direito a desconto, ele deve estar ali, onde os controles conseguem ver. - Não embuta perfis em tokens bearer. Referencie-os por id e resolva-os no servidor, para que o aparelho carregue um ponteiro em vez de um documento.
- Mantenha a autoridade sobre descontos na pessoa, não no terminal. Um dispositivo que concede trinta por cento a quem quer que o pegue sobrevive a toda troca de funcionário e a todo turno.
- Reconcilie os descontos aplicados com a identidade autorizadora todas as noites. Um desconto sem colega aprovador é uma consulta de uma linha só e teria pego isso já na primeira compra.