Vérifier l’intégrité d’un fichier avec Go et SHA256
Pour vérifier un fichier en Go, transmettez ses octets en flux à sha256.New() avec io.Copy, puis comparez
le condensé à une somme de contrôle obtenue auprès d’une source fiable. Le programme en ligne de
commande ci-dessous peut afficher le hachage d’un fichier ou vérifier une valeur attendue, et renvoie
un code de sortie non nul lorsque la vérification échoue.
Partir d’une somme de contrôle fiable
SHA256 produit un condensé de 256 bits : 32 octets, généralement écrits sous forme de
64 caractères hexadécimaux. La bibliothèque standard de Go le fournit via
crypto/sha256.
Un condensé décrit les octets du fichier ; ce n’est ni une signature numérique ni une garantie que
le fichier peut être exécuté sans risque.
Pour un téléchargement, obtenez la somme de contrôle attendue depuis la page HTTPS de confiance de l’éditeur ou depuis un manifeste signé dont vous avez vérifié la signature avec une clé de confiance. Si quelqu’un peut remplacer à la fois le fichier et la somme de contrôle attendue, il peut faire réussir la comparaison. Le guide de vérification des versions d’Apache explique la différence entre la vérification d’un hachage et l’authentification d’une version.
Créer un vérificateur de fichiers SHA256
Il vous faut une installation de Go et un shell. Ces commandes ont été testées avec Go 1.27.1 sous Linux avec Bash. Le programme n’utilise que la bibliothèque standard : il n’y a donc aucun paquet à télécharger et aucune configuration de module n’est nécessaire pour cette compilation d’un seul fichier.
Créez un nouveau répertoire de travail. Ne continuez que si cette commande réussit ; elle refuse de réutiliser un répertoire existant :
mkdir sha256-example && cd sha256-example
Enregistrez le code suivant sous main.go dans ce répertoire :
package main
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
)
func hashFile(path string) (sum []byte, err error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer func() {
if closeErr := file.Close(); closeErr != nil && err == nil {
sum, err = nil, closeErr
}
}()
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
return nil, err
}
return hash.Sum(nil), nil
}
func run(args []string) int {
if len(args) < 1 || len(args) > 2 {
fmt.Fprintln(os.Stderr, "Usage: sha256file FILE [EXPECTED_SHA256]")
return 2
}
var expected []byte
if len(args) == 2 {
var err error
expected, err = hex.DecodeString(args[1])
if err != nil || len(expected) != sha256.Size {
fmt.Fprintln(os.Stderr, "Expected SHA256 must be exactly 64 hexadecimal characters")
return 2
}
}
actual, err := hashFile(args[0])
if err != nil {
fmt.Fprintln(os.Stderr, "sha256file:", err)
return 2
}
if len(args) == 1 {
fmt.Printf("%x\n", actual)
return 0
}
if !bytes.Equal(actual, expected) {
fmt.Fprintf(os.Stderr, "FAIL: %q (SHA256 mismatch)\n", args[0])
return 1
}
fmt.Printf("OK: %q\n", args[0])
return 0
}
func main() {
os.Exit(run(os.Args[1:]))
}
os.Open ouvre l’entrée en lecture. io.Copy transmet les octets du
fichier au hachage jusqu’à EOF ou jusqu’à une erreur, et hash.Sum(nil) renvoie le
condensé de ces octets. La fermeture différée s’exécute avant que hashFile ne retourne, y compris en
cas d’erreur de lecture. Ce n’est qu’après le retour de run que main appelle
os.Exit, qui ignorerait sinon les appels différés en attente.
La valeur attendue est décodée et sa longueur est contrôlée avant la lecture du fichier. Le décodage
permet aussi de considérer comme égales les sommes de contrôle hexadécimales en majuscules et en
minuscules. Ce CLI compare des sommes de contrôle publiques avec bytes.Equal ; il n’a aucune valeur de
comparaison secrète à protéger. Pour l’authentification de messages avec clé, utilisez une
conception HMAC distincte et hmac.Equal.
Compilez l’exécutable dans le même répertoire. Une recompilation remplace un exécutable Go existant
nommé sha256file ; choisissez un autre nom de sortie si ce chemin est déjà utilisé par un fichier sans
rapport :
go build -o sha256file main.go
Vérifier un fichier et examiner le code de sortie
Créez une petite entrée contenant exactement les trois octets abc, sans saut de ligne final, puis
calculez son hachage. Cette commande crée ou écrase le fichier jetable example.txt dans votre
répertoire de travail :
printf 'abc' > example.txt && ./sha256file example.txt
La sortie est :
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Vérifiez-le maintenant par rapport à cette somme de contrôle connue :
./sha256file example.txt ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Le programme se termine avec le code 0 et affiche :
OK: "example.txt"
Cette entrée connue illustre le mécanisme. Pour un vrai téléchargement, transmettez son chemin et la
somme de contrôle de confiance de l’éditeur ; calculer une nouvelle somme de contrôle à partir du
fichier téléchargé puis la comparer avec elle-même ne permet pas d’établir son intégrité. Mettez
entre guillemets les chemins qui contiennent des espaces. Le second argument n’accepte que le
condensé de 64 caractères, sans nom de fichier, sans préfixe sha256: et sans espaces autour.
Remplacez le contenu de l’échantillon et vérifiez-le par rapport à la somme de contrôle d’origine.
Dans un shell Bash interactif sans set -e, exécutez ces lignes ensemble pour que $? capture
immédiatement le code de sortie du vérificateur :
printf 'changed\n' > example.txt &&
./sha256file example.txt ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
printf 'Exit status: %s\n' "$?"
Vous devriez voir :
FAIL: "example.txt" (SHA256 mismatch)
Exit status: 1
Le vérificateur envoie les échecs vers la sortie d’erreur standard et les résultats réussis vers la sortie standard. Ses codes de sortie permettent à un script de décider s’il doit continuer :
| Statut | Signification |
|---|---|
0 | Hachage affiché, ou le fichier correspond à la somme de contrôle fournie |
1 | Le fichier a été lu correctement, mais sa somme de contrôle diffère |
2 | Arguments invalides, somme de contrôle mal formée, ou erreur d’ouverture, de lecture ou de fermeture du fichier |
Lancez l’exécutable compilé pour vérifier ces codes ; go run signale l’échec d’un programme enfant
via l’outil Go et ne conserve pas son code de sortie exact. Le vérificateur ouvre les entrées
existantes sans les modifier et signale les fichiers manquants au lieu de créer des remplacements.
Traiter les gros fichiers en flux et garder l’entrée stable
Le même programme traite un fichier vide, des données binaires ou une archive volumineuse. Il traite
les octets de manière incrémentale au lieu d’allouer une slice aussi grande que le fichier. Chaque
octet doit tout de même être lu, donc la vitesse du stockage et le débit de hachage influent sur le
temps écoulé. Aucune taille de tampon universelle ne rend cette tâche la plus rapide possible.
Mesurez votre charge de travail réelle avant de remplacer io.Copy par une mise en tampon
personnalisée, et tenez compte du cache du système de fichiers lorsque vous répétez une mesure.
Terminez le téléchargement ou l’écriture du fichier avant de calculer son hachage, et gardez-le inchangé jusqu’à ce que le consommateur l’utilise. Ce programme ne verrouille pas le fichier et ne crée pas d’instantané. Une comparaison réussie vérifie les octets qui ont été lus ; elle ne peut pas empêcher un autre processus de les modifier ensuite.
Pièges courants
Un écart peut signifier une corruption, mais il peut aussi signifier que vous avez utilisé une somme de contrôle destinée à une autre version, une autre plateforme ou une autre archive. Calculez le hachage du fichier exact désigné par l’éditeur : une archive et son contenu extrait ont des condensés différents. Ajouter un saut de ligne ou convertir les fins de ligne modifie aussi les octets.
Un fichier vide possède un condensé SHA256 valide et ne constitue pas un échec de lecture. Sa vérification échouera tout de même par rapport à la somme de contrôle d’un téléchargement non vide. Si la lecture échoue en cours de route, le programme signale une erreur au lieu de comparer un condensé partiel. Pour une entrée manquante ou inaccessible, vérifiez le chemin et les permissions avant de réessayer ; une erreur d’E/S ne permet pas de déterminer si le fichier correspond.
