Pular para conteúdo

Autenticação de requisições na GloboCDN

O que é autenticação em uma CDN?

A autenticação em uma CDN é o processo pelo qual a CDN verifica a identidade de quem está tentando acessar seus recursos. Isso é crucial para garantir que apenas usuários autorizados possam acessar os dados da CDN.

Em termos simples, é como ter um cartão de identificação para entrar em um prédio. Na CDN, você precisa de uma "credencial" para acessar seus recursos, seja um nome de usuário e senha, uma chave única ou algum outro tipo de código.

Este processo é importante para manter a segurança dos dados distribuídos pela CDN, evitando que pessoas não autorizadas acessem informações confidenciais ou alterem os arquivos hospedados. É uma camada de proteção que ajuda a garantir que apenas quem deve ter acesso aos recursos realmente o tenha.

Adotamos dois modos como forma de autenticação na CDN da Globo:

  • Token JTW com assinatura RS512
  • Token HMAC com chave hexadecimal

Autenticação por chave de acesso

O uso de chaves de acesso é um método comum em serviços de CDN para a assinatura digital de uma determinada informação para controlar o acesso aos seus recursos distribuídos.

A autenticação por JWT funciona com o método de chave pública e privada. Já a por HMAC funciona com uma chave de 32 caracteres, podendo ser enviadas ou geradas até 2 chaves, onde uma delas será usada como primária e a outra como chave de transição. O Admin da GloboCDN gera as chaves automaticamente para ambos os métodos, mas permite o usuário do Admin também possa enviar sua chave.

Warning

Nunca compartilhe suas chaves. Mantenha as suas credenciais salvas em segurança.

Habilitando a autenticação via Admin

No Admin, tanto no fluxo de criação quanto no de atualização, o cliente terá a opção de fornecer a respectiva chave do método ou deixar o Admin gerar automaticamente as chaves RSA(pública/privada) ou de 32 caracteres, como mencionado no tópico anterior.

A chave será usada pela lógica de validação no servidor para conseguir verificar um token. As chaves geradas serão usadas pela pessoa usuária para gerar um token.

Video de demonstração

Como fazer requisições autenticadas com autenticação JWT

Para poder fazer uma requisição após ter configurado sua entrega com autenticação por token JWT, será necessário respeitar as seguintes regras na composição do path:

  • A validação pressupõe a presença de um prefixo, que é um identificador comum do conteúdo que a usuária tem acesso. Isso garante, por exemplo, que qualquer requisição para variante ou chunk seja validada para um mesmo prefixo, ou seja, qualquer variação de uma mídia pode usar um mesmo token. O prefixo deve ser utilizado para assinar o token no campo prefix do payload do JWT.
  • Após o prefixo, é esperado o token no formato JWT, que deve ser colocado após um identificador de acesso, que é o /j/.
  • Após o token, é esperado o conteúdo desejado. Aqui espera-se que o conteúdo seja um arquivo de manifest ou um chunk, por exemplo.

Dessa forma, após o token, teríamos a identificação de master ou chunks e antes do /j/ teríamos o prefixo que será usado na validação. Assim, permite-se acesso a qualquer URL, usando o domínio configurado, com o prefixo anterior ao componente /j/TOKEN/ que seja referente ao conteúdo descrito após esse componente.
Um exemplo do formato de path a ser seguido seria:
/prefixo-do-video/j/TOKEN/manifest_12382131.m3u8
Um exemplo da URL completa seria:
https://exemplo.glbcdn.net/prefixo-do-video/j/TOKEN/manifest_12382131.m3u8

Portanto, para a formação da requisição nesse modelo, é necessário que o cliente tenha em mãos o token. É possível gerar tokens de acesso para o conteúdo na GloboCDN, desde que sejam respeitadas as regras de validação descritas abaixo.

Gerando tokens JWT de acesso

A geração de tokens JWT é responsabilidade do cliente. Com os dados das chaves, pública e privada, o payload e o header do JWT, é possível gerar tokens para acesso ao conteúdo na GloboCDN.

O header descreve as operações criptográficas usadas para assinar o token, enquanto o payload contém as informações que serão verificadas pela GloboCDN. Para a Globo Technologies, o header possui uma informação a mais que é o kid, o identificador da chave que foi utilizada para assinar o token.
Dessa forma, o header possui as seguintes informações:

  • alg → Indica o algoritmo utilizado na assinatura, que será RS512. Será utilizado na validação para fazer o processo de decriptação.
  • kid → Identificador da chave pública. Isso permitirá identificar a chave pública no local que armazenaremos as chaves e utilizar na validação.
  • typ → Indica o tipo do token, que é JWT.

Já o payload possui as seguintes informações:

  • domain → Indica o domínio em que esse token poderá ser usado. É utilizado na validação para entender se o domínio que chegou a requisição é o que o token permite. Se não for, a resposta será de negação.
  • exp → Indica o tempo de expiração do token. É utilizado na validação para entender se o token ainda não expirou. Se tiver expirado, a resposta será de negação.
  • iat → Indica o momento de assinatura do token. É utilizado na validação para entender se o token foi gerado antes do momento da requisição. Se não tiver sido, é um indicativo de token corrompido e, por isso, a resposta será de negação.
  • iss → Indica a entidade assinante do token. É utilizado na validação para entender se a entidade que assinou é conhecida por quem valida. Se não for, a resposta será de negação.
  • prefix → Indica o prefixo do conteúdo em que esse token poderá ser usado. É utilizado na validação para entender se o prefixo utilizado para assinar é compatível com o prefixo da requisição. Isso permite que um token de um conteúdo aberto não seja usado para consumir um conteúdo pago, por exemplo. Se o prefixo não for compatível, a resposta será de negação.

Ao utilizar qualquer linguagem de programação que suporte JWT, os clientes da GloboCDN podem gerar tokens para os seus consumidores. Desde que todas as diretivas sejam respeitadas, os tokens serão validados pela GloboCDN e caso seja um token inválido ou expirado, um erro HTTP 403 será retornado.

Exemplo de informações em um token no formato esperado pela GloboCDN:

JWT Globo Technologies

{
  header: {
      "alg": "RS512",
      "kid": "Valor Mostrado via Admin",
      "typ": "JWT"
  }
  payload: {
      "domain": "exemplo.glbcdn.net",
      "exp": 1707333201,
      "iat": 1707246801,
      "iss": "tenant_id", -> Mostrado no admin, no momento de criação de domínio
      "prefix": "prefixo-do-video"
  }
}

Info

Ao ativar a Autenticação via Token no Admin os dados de iss e kid são exibidos na tela de edição da instância. É necessário guardar essas informações para que seja possível gerar tokens válidos.

Utilize os exemplos abaixo como base para gerar os seus tokens. Os exemplos foram testados e validados dentro do fluxo da GloboCDN. Caso queira utilizar outra tecnologia que atenda os seus requisitos, fique à vontade, mas não se esqueça de seguir as regras listadas anteriormente.

Info

Os códigos abaixo tem como saída um token JWT que deve ser utilizado nas requisições. Leia atentamente para saber o que deve ser modificado com base na sua configuração de instância para gerar um token válido.

Exemplo de como gerar um token utilizando a linguagem Python.

1 - Instale as dependências necessárias

pip install PyJWT
pip install cryptography
pip install pycryptodome

2 - As linhas comentadas geram uma chave para testes. Preencha as informações de kid, iss e private_key com os dados que foram fornecidos pelo Admin.

import time
import jwt

## -----------------------------------
# Generate key for testing
# from Crypto.PublicKey import RSA
# key = RSA.generate(2048)
# private_key = key.export_key().decode("utf-8")
# public_key = key.publickey().export_key().decode("utf-8")
## -----------------------------------

# Get Current time
current_time = int(time.time())

# Set Expiration of token that will be generated
expiration_time = current_time + 86400 # one day by example
issuer = "identificacao-do-emissor-do-token-mostrado-no-admin"

# Set your claims to generate token
claims = {
'exp': expiration_time,
'domain': "exemplo.glbcdn.net",
'prefix': "prefixo-do-video",
'iat': current_time,
'iss': issuer
}

kid = "identificador-da-chave-publica-mostrado-no-admin"
headers = {
'kid': kid,
'alg': "RS512",
'typ': "JWT"
}

jwt_token = jwt.encode(claims, private_key, algorithm='RS512', headers=headers)

# ## Since you generated the token, the token will available to use in GloboCDN.
print(jwt_token)

Exemplo de como gerar um token utilizando a linguagem Ruby.

1 - Instalar a dependência jwt

gem install jwt

2 - Script que gera um token assinado válido

require 'jwt'

# ---------------------------------
# Generate key for testing

private_key = OpenSSL::PKey::RSA.generate 2048
public_key = private_key.public_key
# ---------------------------------

# Set your public key if you want to use as Subject
# public_key = ''

# # Set your private key to sign your JWT token
# private_key = ''

# Token expiration
current_time = Time.now.to_i
expiration_time = current_time + 86400 # one days

claims = {
"domain": "exemplo.glbcdn.net",
"exp": expiration_time,
"iat": current_time,
"iss": "identificacao-do-emissor-do-token-mostrado-no-admin",
"prefix": "prefixo-do-video"
}

kid = "identificador-da-chave-publica-mostrado-no-admin"
token = JWT.encode claims, private_key, 'RS512', { 'kid' => kid, 'typ' => 'JWT' }
puts token

Exemplo de como gerar um token utilizando a linguagem Golang.

1 - Instale a biblioteca lestrrat-go

go get github.com/lestrrat-go/jwx/v2

2 - Geração do token válido assinado

package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "time"

    "github.com/lestrrat-go/jwx/v2/jwa"
    "github.com/lestrrat-go/jwx/v2/jwk"
    "github.com/lestrrat-go/jwx/v2/jwt"
)

func main() {
    // Gerando chaves privadas
    // Lembre-se que é necessário ter também a chave pública para inserir no Admin.
    rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        panic(err)
    }

    // -------------Caso possua a chave privada--------------
    // Insira a chave privada no formato PEM na variável pemString
    //
    // pemString := `-----BEGIN PRIVATE KEY-----
    // ...
    // -----END PRIVATE KEY-----`
    //
    pemString := ``
    enableOwnPrivatekey := len(pemString) != 0

    if enableOwnPrivatekey {
        fmt.Println("use your own private key")

        block, _ := pem.Decode([]byte(pemString))

        parseResult, err := x509.ParsePKCS8PrivateKey(block.Bytes)
        if err != nil {
            panic(err)
        }
        rsaPrivateKey = parseResult.(*rsa.PrivateKey)
    }

    expiration := time.Now().Local().Add(time.Hour * time.Duration(24)) // 24 hours
    issuer := "identificacao-do-emissor-do-token-mostrado-no-admin"

    unsigned, err := jwt.NewBuilder().
        Issuer(issuer).
        Expiration(expiration).
        IssuedAt(time.Now()).
        Build()

    if err != nil {
        fmt.Printf("failed to build token: %s\n", err)
        return
    }

    claims := map[string]interface{}{
        "domain": "exemplo.glbcdn.net",
        "exp":    expiration,
        "iss":    issuer,
        "prefix": "prefixo-do-video",
    }

    for k, v := range claims {
        err := unsigned.Set(k, v)
        if err != nil {
            fmt.Printf("failed add claim to token: %s", err)
        }
    }

    rsaKey, err := jwk.FromRaw(rsaPrivateKey)
    if err != nil {
        fmt.Printf("failed add private key %s", err)
    }

    kid := "identificador-da-chave-publica-mostrado-no-admin"
    err = rsaKey.Set("kid", kid)
    if err != nil {
        fmt.Printf("failed add kid to token: %s", err)
    }

    signed, err := jwt.Sign(unsigned, jwt.WithKey(jwa.RS512, rsaKey))
    if err != nil {
        fmt.Printf("failed to sign token: %s", err)
    }
    fmt.Println(string(signed))
}

Gerando tokens HMAC de acesso

A geração de tokens HMAC é responsabilidade do cliente. Com as chaves e o path é possível gerar tokens para acesso ao conteúdo na GloboCDN.

Os parâmetros do script para gerar os tokens possui as seguintes informações:

  • seconds → Indica o tempo de expiração do token. É utilizado na validação para entender se o token ainda não expirou. Se tiver expirado, a resposta será de negação.
  • key → Indica a chave hexadecimal de 32 caracters que foi enviado ou gerado automaticamente no GloboCDN Admin. Exemplo: 7e991a6a61e02a6b4fdb87a6756272c0.
  • path → Indica o prefixo do conteúdo em que esse token poderá ser usado. É utilizado na validação para entender se o prefixo utilizado para assinar é compatível com o prefixo da requisição. Isso permite que um token de um conteúdo aberto não seja usado para consumir um conteúdo pago, por exemplo. Se o prefixo não for compatível, a resposta será de negação. Exemplo: /media_2.mp4/index-v1-a1.m3u8.
  • session_id → O identificador de sessão para tokens de uso único ou outros casos avançados.

Abaixo o script para gerar um token utilizando a linguagem Python.

# -*- coding: utf-8 -*-

import binascii
import hashlib
import hmac
import os
import time

# Force the local timezone to be GMT.

os.environ['TZ'] = 'GMT'
if __name__ == '__main__':
        seconds = 60
        key = 'YOUR_HEX_KEY'
        path = 'YOUR_PATH_HERE'
        session_id = '199832'
        field_delimiter = '~'
        end_time = int(time.mktime(time.gmtime())) + seconds
        hash_source = []
        new_token = []
        new_token.append('exp={0}'.format(end_time))
        new_token.append('id={0}'.format(session_id))

        hash_source = list(new_token)
        hash_source.append('url={0}'.format(path))

        token_hmac = hmac.new(
                binascii.a2b_hex(key.encode()),
                field_delimiter.join(hash_source).encode(),
                getattr(hashlib, "sha256")).hexdigest()

        new_token.append('hmac={0}'.format(token_hmac))

        print("?hdnts={0}".format(field_delimiter.join(new_token)))

FAQ

Posso gerar um token para cada usuário meu?

Sim. A GloboCDN irá validar individualmente cada request com token e as informações do token serão validadas.

O que acontece se eu trocar a chave no Admin?

A chave é um contrato entre o Cliente e a GloboCDN, ao trocar a chave o contrato é quebrado e todos os usuários que possuem tokens gerados com essa chave serão impactados. Os novos tokens gerados com a nova chave passarão a funcionar instantaneamente. Sendo assim é recomendado que a troca de chaves seja feita somente em situações específicas.

Meu token não está funcionando corretamente, todos os requests estão retornando HTTP 403

Os headers são obrigatórios na criação do token, e os dados das claims devem ser enviados corretamente. Uma última checagem pode ser feita na chave, caso tudo esteja correto, entre em contato com o suporte.