Canaux chiffrés
Un canal dont le nom commence par private-encrypted- est chiffré de bout en bout. Votre serveur scelle chaque charge utile avant de la publier, et les clients navigateur autorisés la déchiffrent avec une clé provenant de votre endpoint d'autorisation. Le edge Realtime et les intermédiaires réseau ne voient que du texte chiffré.
Générez et stockez une clé maître de 32 octets. La clé maître n'apparaît jamais dans une requête API Realtime, et le préfixe du nom de canal active la fonctionnalité. Bird ne peut pas récupérer une clé perdue, et les charges utiles scellées avec cette clé restent illisibles après son remplacement.
Les canaux chiffrés utilisent le même endpoint et la même signature que les canaux privés. La réponse d'autorisation inclut également la clé de déchiffrement dérivée du canal sous la forme shared_secret. Refuser un abonnement empêche ce client de recevoir la clé.
Générer une clé maître
Générez 32 octets aléatoires, encodez-les en base64 et stockez la valeur comme le secret de l'application :
Exemple de code
openssl rand -base64 32Transmettez-la à votre serveur SDK dans la configuration realtime, à côté de la clé et du secret de l'application.
Publier un événement chiffré
Le serveur SDK détecte le préfixe du canal, dérive sa clé à partir de la clé maître et scelle la charge utile JSON localement. La requête de publication contient l'enveloppe scellée.
import { BirdClient } from "@messagebird/sdk";
const bird = new BirdClient({
apiKey: process.env.BIRD_API_KEY,
realtime: {
key: process.env.BIRD_REALTIME_KEY,
secret: process.env.BIRD_REALTIME_SECRET,
encryptionMasterKey: process.env.BIRD_REALTIME_MASTER_KEY,
},
});
await bird.realtime.publish("rap_01krdgeqcxet5s7t44vh8rt9mg", {
event: "order.updated",
channels: ["private-encrypted-orders"],
data: { order_id: "ord_123", status: "shipped" },
});from bird import Bird
client = Bird(
realtime_key=os.environ["BIRD_REALTIME_KEY"],
realtime_secret=os.environ["BIRD_REALTIME_SECRET"],
realtime_encryption_master_key=os.environ["BIRD_REALTIME_MASTER_KEY"],
)
client.realtime.publish(
"rap_01krdgeqcxet5s7t44vh8rt9mg",
event="order.updated",
channels=["private-encrypted-orders"],
data={"order_id": "ord_123", "status": "shipped"},
)client, err := bird.NewClient(
option.WithAPIKey(os.Getenv("BIRD_API_KEY")),
option.WithRealtimeCredentials(os.Getenv("BIRD_REALTIME_KEY"), os.Getenv("BIRD_REALTIME_SECRET")),
option.WithRealtimeEncryptionMasterKey(os.Getenv("BIRD_REALTIME_MASTER_KEY")),
)
if err != nil {
log.Fatal(err)
}
_, err = client.Realtime.Publish(context.Background(), "rap_01krdgeqcxet5s7t44vh8rt9mg", bird.RealtimePublishParams{
Event: "order.updated",
Channels: []string{"private-encrypted-orders"},
Data: map[string]any{"order_id": "ord_123", "status": "shipped"},
})$bird = new Bird(getenv('BIRD_API_KEY'), realtime: new RealtimeOptions(
key: getenv('BIRD_REALTIME_KEY'),
secret: getenv('BIRD_REALTIME_SECRET'),
encryptionMasterKey: getenv('BIRD_REALTIME_MASTER_KEY'),
));
$bird->realtime->publish('rap_01krdgeqcxet5s7t44vh8rt9mg', (new RealtimePublish())
->setEvent('order.updated')
->setChannels(['private-encrypted-orders'])
->setData(['order_id' => 'ord_123', 'status' => 'shipped']));Un canal chiffré doit être le seul canal dans une publication unique. Chaque canal chiffré dérive une clé différente, les autres canaux ne pourraient donc pas déchiffrer la même charge utile scellée. Les SDK rejettent cette diffusion localement, et le API renvoie E23000 s'il en reçoit une. Pour publier vers plusieurs canaux chiffrés, utilisez un lot avec un canal par événement.
Renvoyer le secret partagé depuis votre endpoint d'authentification
Votre endpoint d'authentification approuve les abonnements chiffrés de la même manière que les abonnements privés. Utilisez le helper authorizeChannel du SDK et la réponse obtient automatiquement le shared_secret chaque fois que le nom du canal porte le préfixe chiffré :
app.post("/bird/auth", async (req, res) => {
const { connection_id, channel_name } = req.body;
const user = getUserFromSession(req);
if (!user || !mayJoin(user, channel_name)) return res.sendStatus(403);
res.json(
await bird.realtime.authorizeChannel({
connectionId: connection_id,
channelName: channel_name,
}),
);
});@app.post("/bird/auth")
def bird_auth():
body = request.get_json()
user = get_user_from_session()
if user is None or not may_join(user, body["channel_name"]):
abort(403)
return client.realtime.authorize_channel(
connection_id=body["connection_id"],
channel_name=body["channel_name"],
)func birdAuth(w http.ResponseWriter, r *http.Request) {
var body struct {
ConnectionID string `json:"connection_id"`
ChannelName string `json:"channel_name"`
}
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
user, ok := userFromSession(r)
if !ok || !mayJoin(user, body.ChannelName) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
auth, err := client.Realtime.AuthorizeChannel(bird.RealtimeChannelAuthorizationParams{
ConnectionID: body.ConnectionID,
ChannelName: body.ChannelName,
})
if err != nil {
http.Error(w, "authorization failed", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(auth)
}function birdAuth(string $connectionId, string $channelName, User $user): array
{
if (!mayJoin($user, $channelName)) {
http_response_code(403);
exit;
}
return $bird->realtime->authorizeChannel($connectionId, $channelName);
}Le SDK dérive un shared_secret distinct pour chaque canal. L'autorisation pour private-encrypted-orders ne permet donc pas de déchiffrer private-encrypted-invoices. Le secret transite dans votre réponse d'autorisation et n'est pas inclus dans la trame d'abonnement envoyée au edge.
S'abonner et déchiffrer dans le navigateur
Le chiffrement utilise le point d'entrée @messagebird/realtime/encrypted distinct. Importez-le et passez-le comme option encryption du client :
Exemple de code
import { BirdRealtime } from "@messagebird/realtime";
import { encryption } from "@messagebird/realtime/encrypted";
const bird = new BirdRealtime({
appKey: "your-app-key",
region: "us1",
authEndpoint: "/bird/auth",
encryption,
});
const orders = bird.subscribe("private-encrypted-orders");
orders.bind("order.updated", (data) => {
console.log(data); // decrypted: { order_id: "ord_123", status: "shipped" }
});Les bindings reçoivent du texte en clair. S'abonner sans l'option encryption lève immédiatement une erreur, et une réponse d'autorisation sans shared_secret fait échouer l'abonnement.
Seul le client navigateur reçoit actuellement les canaux chiffrés. Les clients Swift et Kotlin rejettent les abonnements private-encrypted- car ils n'implémentent pas le déchiffrement.
Rotation de la clé maître
Déployez la nouvelle clé sur chaque éditeur et endpoint d'autorisation en même temps. Pendant la rotation :
- Les nouvelles publications scellent sous la nouvelle clé.
- Un client navigateur abonné qui ne peut pas déchiffrer un événement se ré-autorise une fois et récupère le nouveau shared_secret.
- Des instances utilisant des clés maîtres différentes peuvent brièvement publier des événements que certains clients ne peuvent pas déchiffrer : coordonnez le déploiement entre les instances.
Effectuez la rotation d'une clé divulguée ou perdue. La rotation protège les charges utiles futures mais ne peut pas re-sceller les événements précédents ni révoquer les copies de l'ancienne clé.
Ce que les canaux chiffrés ne font pas
- Les clients officiels ne prennent pas en charge les événements client. Le navigateur trigger() lève une erreur sur les canaux chiffrés car le client ne scelle pas les charges utiles client-à-client. N'envoyez pas d'événements client en clair depuis un client personnalisé.
- Présence et chiffrement ne peuvent pas être combinés. Le préfixe presence-encrypted- n'est pas pris en charge. Cache et chiffrement fonctionnent ensemble : les canaux private-encrypted-cache- stockent l'événement mis en cache sous forme scellée, mais après une rotation de clé, la copie en cache reste scellée sous l'ancienne clé jusqu'à ce qu'une nouvelle publication la remplace.
- Les noms de canaux et les noms d'événements ne sont pas chiffrés. Seule la charge utile l'est. Choisissez des noms de canaux qui ne révèlent pas ce que vous protégez.
- Le edge Realtime ne peut pas inspecter les charges utiles. Les noms de canaux et d'événements restent visibles, tandis que la charge utile reste chiffrée.
Étapes suivantes
- Autoriser les canaux décrit le mécanisme de signature sur lequel ce guide s'appuie.
- Publier des événements couvre les API de publication et de lot.
- Canaux cache explique la relecture du dernier événement avec laquelle private-encrypted-cache- se combine.
Ressources associées
Poursuivez avec la documentation, les guides et les exemples sur ce sujet. Les ressources sont en anglais.
Explorer la fonctionnalitéRealtimeSuivre le parcours d'apprentissageBuild your first integrationGuide d'implémentationSend your first realtime event
Essayez la pratique et obtenez un guide d'implémentation