Dev.to Security 🔐 Cybersecurity 👁 0 📖 5 min read

Gmail refuse une sauvegarde de 329 Mo — et relever la limite n'est pas la réparation

La sauvegarde faisait 329 Mo. Le message d'erreur faisait une ligne : 552 5.3.4 Message size exceeds fixed maximum message size Rien sur la taille autorisée, rien sur la taille réellement envoyée, rien sur ce qu

La sauvegarde faisait 329 Mo. Le message d'erreur faisait une ligne :

552 5.3.4 Message size exceeds fixed maximum message size

Rien sur la taille autorisée, rien sur la taille réellement envoyée, rien sur ce qu'il faut
faire. J'ai passé une heure à vérifier mon mot de passe, mon serveur SMTP et mon port avant de
comprendre que le problème était arithmétique.

Les 25 Mo ne sont pas 25 Mo

Gmail refuse un message au-delà de 25 Mo. Ce chiffre est celui du message encodé, pas celui
du fichier sur le disque. Et c'est là que tout se joue.

Un fichier joint est transporté en base64. Cet encodage transforme 3 octets en 4 caractères :
le volume augmente d'environ un tiers. Ajoutez les en-têtes MIME, les séparateurs de parties,
l'encodage du nom de fichier, et un fichier de 20 Mo produit un message de 27 à 28 Mo.

Autrement dit, la vraie limite utile est autour de 18 Mo de fichier, pas 25.

def taille_message(taille_fichier: int) -> int:
    """Estimation du message encodé : base64 (4/3) + en-têtes MIME."""
    return int(taille_fichier * 4 / 3) + 4096

Une sauvegarde de 329 Mo, c'est donc l'équivalent de 440 Mo de message. Dix-sept fois la
limite. Il n'y avait pas de réglage à trouver : il y avait un découpage à écrire.

Découper, mais pas n'importe comment

Premier réflexe : split -b 10M. C'est ce que j'ai fait la première fois, et c'est une
mauvaise idée pour trois raisons.

split produit des fichiers sans aucune information sur leur ordre, leur nombre, ni leur
contenu. Si un bloc arrive corrompu — et sur trente-trois pièces jointes, ça finit par arriver —
vous ne le savez qu'au moment de reconstituer, sans savoir lequel. Et si un envoi échoue, vous
ne savez pas ce qui manque.

Le découpage doit produire, en plus des blocs, un manifeste. Trente lignes de Python :

import hashlib, json, pathlib

SRC = pathlib.Path("/home/moi/backups/sauvegarde-2026-10.enc")
OUT = pathlib.Path("/home/moi/backups/blocs")
BLOC = 10 * 1024 * 1024

OUT.mkdir(parents=True, exist_ok=True)
manifeste = {"source": SRC.name, "taille": SRC.stat().st_size, "blocs": []}

with SRC.open("rb") as f:
    i = 0
    while True:
        data = f.read(BLOC)
        if not data:
            break
        nom = f"{SRC.stem}.part{i:03d}"
        (OUT / nom).write_bytes(data)
        manifeste["blocs"].append({
            "fichier": nom,
            "octets": len(data),
            "sha256": hashlib.sha256(data).hexdigest(),
        })
        i += 1

(OUT / "manifeste.json").write_text(json.dumps(manifeste, indent=2))
print(f"{len(manifeste['blocs'])} blocs, {manifeste['taille']} octets")

Résultat : 33 blocs, dont 32 de 10 Mo et un dernier plus petit, plus un manifeste.json de
quelques kilo-octets. Chaque bloc encodé fait environ 13,3 Mo de message — largement sous les
25 Mo, avec de la marge pour les en-têtes et pour un client mail qui décide d'ajouter sa
signature.

Le sha256 par bloc n'est pas du zèle. C'est ce qui permet de répondre à la seule question qui
compte quand on restaure : « ce bloc est-il celui que j'ai envoyé ? »

Le manifeste part en premier

L'ordre d'envoi a son importance. Le manifeste est le plus petit fichier et le plus important :
il décrit tout le reste. Je l'envoie en premier, avec un objet explicite.

[backup 2026-10] 00/33 manifeste — 329 Mo, 33 blocs
[backup 2026-10] 01/33 part000
...
[backup 2026-10] 33/33 part032

Trois choses dans cette convention :

  • le numéro dans l'objet permet de retrouver un message égaré par une simple recherche ;
  • le total (/33) permet de savoir s'il manque quelque chose sans ouvrir le manifeste ;
  • le nom du fichier reste dans l'objet, donc un bloc renommé par un client mail reste identifiable.

Un détail qui n'en est pas un : je n'envoie jamais deux blocs dans le même message. Deux
pièces jointes de 10 Mo, c'est 26,6 Mo encodés, donc un refus. La limite se calcule par
message, pas par envoi.

Reconstituer et vérifier

La restauration ne doit pas dépendre de ma mémoire. Elle lit le manifeste et refuse de
continuer si un bloc manque.

import hashlib, json, pathlib

D = pathlib.Path("/home/moi/backups/blocs")
m = json.loads((D / "manifeste.json").read_text())

sortie = pathlib.Path("/home/moi/restaure.enc")
with sortie.open("wb") as out:
    for b in m["blocs"]:
        p = D / b["fichier"]
        if not p.exists():
            raise SystemExit(f"bloc manquant: {b['fichier']}")
        data = p.read_bytes()
        if hashlib.sha256(data).hexdigest() != b["sha256"]:
            raise SystemExit(f"bloc corrompu: {b['fichier']}")
        out.write(data)

print("taille:", sortie.stat().st_size, "attendu:", m["taille"])

Le contrôle final est une comparaison de taille, puis un déchiffrement qui échouera de
lui-même si un octet manque — un fichier chiffré est son propre checksum. Sur trente-trois
blocs envoyés en trois soirs, c'est ce contrôle qui m'a évité de découvrir le problème six mois
plus tard, au pire moment.

Pourquoi relever la limite est toujours la mauvaise réparation

J'ai cherché la solution évidente : un autre fournisseur, un compte payant, un envoi par lien
de partage. Chacune de ces réponses repousse le problème sans le traiter, et en crée un autre.

  • La limite n'est pas une brimade. Un serveur mail doit stocker, scanner et parfois répliquer chaque message. Un plafond par message est ce qui empêche un envoi unique de saturer une boîte partagée.
  • Le destinataire a sa propre limite. Relever la mienne ne change rien à la sienne. Un message de 300 Mo sera refusé à l'arrivée, ou silencieusement tronqué par un relais intermédiaire — et c'est bien pire qu'un refus.
  • Un gros message n'est pas reprenable. Un envoi de 329 Mo qui casse à 90 % doit recommencer à zéro. Trente-trois blocs de 10 Mo : on renvoie le bloc qui a échoué, en deux minutes.
  • La boîte du destinataire a un quota. Une sauvegarde de 329 Mo dans une boîte de 15 Go, c'est deux pour cent de l'espace, tous les mois, sans jamais rien supprimer. Au bout de deux ans, la boîte est pleine et personne ne sait pourquoi.

Le découpage, lui, est une contrainte qui s'applique partout : n'importe quel hébergeur, un
disque amovible, un dépôt d'objets, une clé USB. C'est la seule version du système qui
fonctionnera encore quand le fournisseur aura changé ses règles.

Ce que je garde

Une fonction de découpage de trente lignes, un manifeste avec un hash par bloc, une convention
de nommage dans l'objet du message, et un script de restauration qui refuse de mentir.

La taille de 10 Mo n'est pas magique : elle laisse de la marge sous les 25 Mo encodés, elle
tient dans une pièce jointe raisonnable, et elle permet de renvoyer un bloc sans y passer la
soirée. Le chiffre à retenir est ailleurs : base64 coûte un tiers. Tant qu'on l'oublie, on
cherche un réglage là où il n'y a qu'une division.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.