{"meta":{"title":"Programme de partenariat d’analyse des secrets","intro":"En tant que fournisseur de services, vous pouvez collaborer avec GitHub pour sécuriser vos formats de jeton secret via une analyse secrète qui recherche les commits accidentels de votre format de secret et peuvent être envoyés au point de terminaison de vérification d’un fournisseur de services.","product":"Sécurité et qualité du code","breadcrumbs":[{"href":"/fr/code-security","title":"Sécurité et qualité du code"},{"href":"/fr/code-security/tutorials","title":"Tutorials"},{"href":"/fr/code-security/tutorials/secret-scanning-partner-program","title":"Programme partenaire"}],"documentType":"article"},"body":"# Programme de partenariat d’analyse des secrets\n\nEn tant que fournisseur de services, vous pouvez collaborer avec GitHub pour sécuriser vos formats de jeton secret via une analyse secrète qui recherche les commits accidentels de votre format de secret et peuvent être envoyés au point de terminaison de vérification d’un fournisseur de services.\n\nGitHub analyse les dépôts pour rechercher les formats de secret connus afin d’empêcher l’utilisation frauduleuse d’informations d’identification commitées par erreur. L’Secret scanning se produit par défaut sur les référentiels publics et les packages npm publics. Les administrateurs de référentiel et les propriétaires de l’organisation peuvent également activer l’secret scanning sur des référentiels privés. En tant que fournisseur de services, vous pouvez vous associer à GitHub pour ajouter vos formats de secret à notre secret scanning.\n\nQuand une correspondance de votre format de secret est trouvée dans une source publique, une charge utile est envoyée au point de terminaison HTTP de votre choix.\n\nQuand une correspondance de votre format de secret est trouvée dans un dépôt privé configuré pour l’secret scanning, les administrateurs du dépôt et le commiteur sont avertis, et peuvent voir et gérer le résultat de l’secret scanning sur GitHub. Pour plus d’informations, consultez « [Gérer les alertes d’analyse des secrets](/fr/code-security/secret-scanning/managing-alerts-from-secret-scanning) ».\n\nCet article explique comment vous associer à GitHub en tant que fournisseur de services et rejoindre le programme de partenariat d’secret scanning.\n\n## Processus d’secret scanning\n\nLe diagramme suivant récapitule le processus d’secret scanning pour les dépôts publics, toutes les correspondances étant envoyées au point de terminaison de vérification d’un fournisseur de services. Un processus similaire envoie des jetons de fournisseurs de services exposés dans des packages publics sur le registre npm.\n\n![Diagramme montrant le processus d’analyse d’un secret et l’envoi des correspondances au point de terminaison de vérification d’un fournisseur de services](/assets/images/help/security/secret-scanning-flow.png)\n\n## Adhésion au programme d’secret scanning sur GitHub\n\n1. Contactez GitHub pour lancer le processus.\n2. Identifiez les secrets pertinents que vous voulez analyser et créez des expressions régulières pour les capturer. Pour des informations et des recommandations plus détaillées, consultez [Identifier vos secrets et créer des expressions régulières](#identify-your-secrets-and-create-regular-expressions) ci-dessous.\n3. Pour les correspondances de secret trouvées dans les référentiels publics, créez un service d’alerte de secret qui accepte les webhooks de GitHub contenant la charge utile du message secret scanning.\n4. Implémentez la vérification de signature dans votre service d’alerte de secret.\n5. Implémentez la révocation de secret et la notification utilisateur dans votre service d’alerte de secret.\n6. Fournissez des commentaires pour les faux positifs (facultatif).\n\n### Contacter GitHub pour lancer le processus\n\nPour lancer le processus d’inscription, envoyez un e-mail à <a href=\"mailto:secret-scanning@github.com\"><secret-scanning@github.com></a>.\n\nVous recevez ensuite les détails du programme d’secret scanning et devez accepter les conditions de participation de GitHub avant de continuer.\n\n### Identifier vos secrets et créer des expressions régulières\n\nPour rechercher vos secrets, GitHub a besoin des informations suivantes pour chaque secret à ajouter au programme d’secret scanning :\n\n* Un nom unique et lisible par un utilisateur pour le type de secret. Nous l’utilisons par la suite pour générer la valeur `Type` dans la charge utile du message.\n\n* Expression régulière qui recherche le type de secret. Nous vous recommandons d’être aussi précis que possible, ce qui permettra de réduire le nombre de faux positifs. Voici quelques meilleures pratiques pour des secrets identifiables et de haute qualité :\n\n  * Préfixe défini de manière unique\n  * Chaînes aléatoires d’entropie élevée\n  * Somme de contrôle 32 bits\n\n  ![Capture d’écran montrant la décomposition d’un secret en préfixe et d’une somme de contrôle 32 bits.](/assets/images/help/security/regular-expression-guidance.png)\n\n* Compte de test pour votre service. Cela nous permettra de générer et d’analyser des exemples de secrets, ce qui réduira davantage les faux positifs.\n\n* URL du point de terminaison qui reçoit des messages de GitHub. L'URL n’a pas besoin d’être unique pour chaque type de secret.\n\nEnvoyez ces informations à <a href=\"mailto:secret-scanning@github.com\"><secret-scanning@github.com></a>.\n\n### Créer un service d’alerte de secret\n\nCréez un point de terminaison HTTP public accessible sur Internet au niveau de l’URL que vous nous avez fournie. Quand une correspondance de votre expression régulière est trouvée publiquement, GitHub envoie un message HTTP `POST` à votre point de terminaison.\n\n#### Exemple de corps de demande\n\n```json\n[\n  {\n    \"token\":\"NMIfyYncKcRALEXAMPLE\",\n    \"type\":\"mycompany_api_token\",\n    \"url\":\"https://github.com/octocat/Hello-World/blob/12345600b9cbe38a219f39a9941c9319b600c002/foo/bar.txt\",\n    \"source\":\"content\"\n  }\n]\n```\n\nLe corps du message est un tableau JSON qui contient un ou plusieurs objets, chaque objet représentant une seule correspondance de secret. Votre point de terminaison doit pouvoir gérer les requêtes ayant un grand nombre de correspondances sans expiration de délai. Les clés de chaque correspondance de secret sont les suivantes :\n\n* **jeton :** valeur de la correspondance de secret.\n* **type :** nom unique que vous avez fourni pour identifier votre expression régulière.\n* **url :** URL publique où la correspondance a été trouvée (peut être vide)\n* **source :** emplacement où le jeton a été trouvé sur GitHub.\n\nLa liste des valeurs valides pour `source` est la suivante :\n\n* Content\n* Validations\n* Pull\\_request\\_title\n* Pull\\_request\\_description\n* Pull\\_request\\_comment\n* Issue\\_title\n* Issue\\_description\n* Issue\\_comment\n* Discussion\\_title\n* Discussion\\_body\n* Discussion\\_comment\n* Commit\\_comment\n* Gist\\_content\n* Gist\\_comment\n* Wiki\\_contenu\n* Wiki\\_commit\n* Npm\n* Soumission manuelle\n* Inconnu\n\n### Implémenter la vérification de signature dans votre service d’alerte de secret\n\nLa requête HTTP envoyée à votre service contient également des en-têtes que nous vous recommandons vivement d’utiliser pour vérifier les messages que vous recevez proviennent bien de GitHub et qu’ils ne sont pas malveillants.\n\nLes deux en-têtes HTTP à rechercher sont les suivants :\n\n* `Github-Public-Key-Identifier` : `key_identifier` à utiliser à partir de notre API\n* `Github-Public-Key-Signature` : signature de la charge utile\n\nVous pouvez récupérer la clé publique de l’analyse des secrets GitHub sur <https://api.github.com/meta/public_keys/secret_scanning> et valider le message avec l’algorithme `ECDSA-NIST-P256V1-SHA256`. Le point de terminaison fournit plusieurs `key_identifier` et clés publiques. Vous pouvez déterminer la clé publique à utiliser en fonction de la valeur de `Github-Public-Key-Identifier`.\n\n> \\[!NOTE]\n> Quand vous envoyez une demande au point de terminaison de clé publique ci-dessus, vous risquez d’atteindre les limites de débit. Pour éviter d’atteindre les limites de débit, vous pouvez utiliser un personal access token (classic) (aucune étendue nécessaire) ou un fine-grained personal access token (seuls les dépôts publics automatiques sont accessibles en lecture), comme indiqué dans les exemples ci-dessous, ou utiliser une requête conditionnelle. Pour plus d’informations, consultez « [Prise en main de l’API REST](/fr/rest/guides/getting-started-with-the-rest-api#conditional-requests) ».\n\n> \\[!NOTE]\n> La signature a été générée à partir du corps du message brut. Vous devez donc utiliser également le corps du message brut pour la validation de signature, au lieu d’analyser et de stringifier le JSON, pour éviter de réorganiser le message ou de changer l’espacement.\n\n```\n          **Exemple HTTP POST envoyée pour vérifier le point de terminaison**\n```\n\n```http\nPOST / HTTP/2\nHost: HOST\nAccept: */*\nContent-Length: 104\nContent-Type: application/json\nGithub-Public-Key-Identifier: bcb53661c06b4728e59d897fb6165d5c9cda0fd9cdf9d09ead458168deb7518c\nGithub-Public-Key-Signature: MEQCIQDaMKqrGnE27S0kgMrEK0eYBmyG0LeZismAEz/BgZyt7AIfXt9fErtRS4XaeSt/AO1RtBY66YcAdjxji410VQV4xg==\n\n[{\"source\":\"commit\",\"token\":\"some_token\",\"type\":\"some_type\",\"url\":\"https://example.com/base-repo-url/\"}]\n```\n\nLes extraits de code suivants montrent comment vous pouvez effectuer la validation de signature.\nLes exemples de code supposent que vous avez défini une variable d’environnement appelée `GITHUB_PRODUCTION_TOKEN` avec un [personal access token](https://github.com/settings/tokens) généré pour éviter d’atteindre les limites de débit. Le personal access token n’a besoin d’aucune étendue/autorisation.\n\n```\n          **Exemple de validation dans Go**\n```\n\n```golang\npackage main\n\nimport (\n  \"crypto/ecdsa\"\n  \"crypto/sha256\"\n  \"crypto/x509\"\n  \"encoding/asn1\"\n  \"encoding/base64\"\n  \"encoding/json\"\n  \"encoding/pem\"\n  \"errors\"\n  \"fmt\"\n  \"math/big\"\n  \"net/http\"\n  \"os\"\n)\n\nfunc main() {\n  payload := `[{\"source\":\"commit\",\"token\":\"some_token\",\"type\":\"some_type\",\"url\":\"https://example.com/base-repo-url/\"}]`\n\n  kID := \"bcb53661c06b4728e59d897fb6165d5c9cda0fd9cdf9d09ead458168deb7518c\"\n\n  kSig := \"MEQCIQDaMKqrGnE27S0kgMrEK0eYBmyG0LeZismAEz/BgZyt7AIfXt9fErtRS4XaeSt/AO1RtBY66YcAdjxji410VQV4xg==\"\n\n  // Fetch the list of GitHub Public Keys\n  req, err := http.NewRequest(\"GET\", \"https://api.github.com/meta/public_keys/secret_scanning\", nil)\n  if err != nil {\n    fmt.Printf(\"Error preparing request: %s\\n\", err)\n    os.Exit(1)\n  }\n\n  if len(os.Getenv(\"GITHUB_PRODUCTION_TOKEN\")) == 0 {\n    fmt.Println(\"Need to define environment variable GITHUB_PRODUCTION_TOKEN\")\n    os.Exit(1)\n  }\n\n  req.Header.Add(\"Authorization\", \"Bearer \"+os.Getenv(\"GITHUB_PRODUCTION_TOKEN\"))\n\n  resp, err := http.DefaultClient.Do(req)\n  if err != nil {\n    fmt.Printf(\"Error requesting GitHub signing keys: %s\\n\", err)\n    os.Exit(2)\n  }\n\n  decoder := json.NewDecoder(resp.Body)\n  var keys GitHubSigningKeys\n  if err := decoder.Decode(&keys); err != nil {\n    fmt.Printf(\"Error decoding GitHub signing key request: %s\\n\", err)\n    os.Exit(3)\n  }\n\n  // Find the Key used to sign our webhook\n  pubKey, err := func() (string, error) {\n    for _, v := range keys.PublicKeys {\n      if v.KeyIdentifier == kID {\n        return v.Key, nil\n\n      }\n    }\n    return \"\", errors.New(\"specified key was not found in GitHub key list\")\n  }()\n\n  if err != nil {\n    fmt.Printf(\"Error finding GitHub signing key: %s\\n\", err)\n    os.Exit(4)\n  }\n\n  // Decode the Public Key\n  block, _ := pem.Decode([]byte(pubKey))\n  if block == nil {\n    fmt.Println(\"Error parsing PEM block with GitHub public key\")\n    os.Exit(5)\n  }\n\n  // Create our ECDSA Public Key\n  key, err := x509.ParsePKIXPublicKey(block.Bytes)\n  if err != nil {\n    fmt.Printf(\"Error parsing DER encoded public key: %s\\n\", err)\n    os.Exit(6)\n  }\n\n  // Because of documentation, we know it's a *ecdsa.PublicKey\n  ecdsaKey, ok := key.(*ecdsa.PublicKey)\n  if !ok {\n    fmt.Println(\"GitHub key was not ECDSA, what are they doing?!\")\n    os.Exit(7)\n  }\n\n  // Parse the Webhook Signature\n  parsedSig := asn1Signature{}\n  asnSig, err := base64.StdEncoding.DecodeString(kSig)\n  if err != nil {\n    fmt.Printf(\"unable to base64 decode signature: %s\\n\", err)\n    os.Exit(8)\n  }\n  rest, err := asn1.Unmarshal(asnSig, &parsedSig)\n  if err != nil || len(rest) != 0 {\n    fmt.Printf(\"Error unmarshalling asn.1 signature: %s\\n\", err)\n    os.Exit(9)\n  }\n\n  // Verify the SHA256 encoded payload against the signature with GitHub's Key\n  digest := sha256.Sum256([]byte(payload))\n  keyOk := ecdsa.Verify(ecdsaKey, digest[:], parsedSig.R, parsedSig.S)\n\n  if keyOk {\n    fmt.Println(\"THE PAYLOAD IS GOOD!!\")\n  } else {\n    fmt.Println(\"the payload is invalid :(\")\n    os.Exit(10)\n  }\n}\n\ntype GitHubSigningKeys struct {\n  PublicKeys []struct {\n    KeyIdentifier string `json:\"key_identifier\"`\n    Key           string `json:\"key\"`\n    IsCurrent     bool   `json:\"is_current\"`\n  } `json:\"public_keys\"`\n}\n\n// asn1Signature is a struct for ASN.1 serializing/parsing signatures.\ntype asn1Signature struct {\n  R *big.Int\n  S *big.Int\n}\n```\n\n```\n          **Exemple de validation dans Ruby**\n```\n\n```ruby\nrequire 'openssl'\nrequire 'net/http'\nrequire 'uri'\nrequire 'json'\nrequire 'base64'\n\npayload = <<-EOL\n[{\"source\":\"commit\",\"token\":\"some_token\",\"type\":\"some_type\",\"url\":\"https://example.com/base-repo-url/\"}]\nEOL\n\npayload = payload\n\nsignature = \"MEQCIQDaMKqrGnE27S0kgMrEK0eYBmyG0LeZismAEz/BgZyt7AIfXt9fErtRS4XaeSt/AO1RtBY66YcAdjxji410VQV4xg==\"\n\nkey_id = \"bcb53661c06b4728e59d897fb6165d5c9cda0fd9cdf9d09ead458168deb7518c\"\n\nurl = URI.parse('https://api.github.com/meta/public_keys/secret_scanning')\n\nraise \"Need to define GITHUB_PRODUCTION_TOKEN environment variable\" unless ENV['GITHUB_PRODUCTION_TOKEN']\nrequest = Net::HTTP::Get.new(url.path)\nrequest['Authorization'] = \"Bearer #{ENV['GITHUB_PRODUCTION_TOKEN']}\"\n\nhttp = Net::HTTP.new(url.host, url.port)\nhttp.use_ssl = (url.scheme == \"https\")\n\nresponse = http.request(request)\n\nparsed_response = JSON.parse(response.body)\n\ncurrent_key_object = parsed_response[\"public_keys\"].find { |key| key[\"key_identifier\"] == key_id }\n\ncurrent_key = current_key_object[\"key\"]\n\nopenssl_key = OpenSSL::PKey::EC.new(current_key)\n\nputs openssl_key.verify(OpenSSL::Digest::SHA256.new, Base64.decode64(signature), payload.chomp)\n```\n\n```\n          **Exemple de validation dans JavaScript**\n```\n\n```javascript\nconst crypto = require(\"crypto\");\nconst axios = require(\"axios\");\n\nconst GITHUB_KEYS_URI = \"https://api.github.com/meta/public_keys/secret_scanning\";\n\n/**\n * Verify a payload and signature against a public key\n * @param {String} payload the value to verify\n * @param {String} signature the expected value\n * @param {String} keyID the id of the key used to generated the signature\n * @return {void} throws if the signature is invalid\n */\nconst verify_signature = async (payload, signature, keyID) => {\n  if (typeof payload !== \"string\" || payload.length === 0) {\n    throw new Error(\"Invalid payload\");\n  }\n  if (typeof signature !== \"string\" || signature.length === 0) {\n    throw new Error(\"Invalid signature\");\n  }\n  if (typeof keyID !== \"string\" || keyID.length === 0) {\n    throw new Error(\"Invalid keyID\");\n  }\n\n  const keys = (await axios.get(GITHUB_KEYS_URI)).data;\n  if (!(keys?.public_keys instanceof Array) || keys.length === 0) {\n    throw new Error(\"No public keys found\");\n  }\n\n  const publicKey = keys.public_keys.find((k) => k.key_identifier === keyID) ?? null;\n  if (publicKey === null) {\n    throw new Error(\"No public key found matching key identifier\");\n  }\n\n  const verify = crypto.createVerify(\"SHA256\").update(payload);\n  if (!verify.verify(publicKey.key, Buffer.from(signature, \"base64\"), \"base64\")) {\n    throw new Error(\"Signature does not match payload\");\n  }\n};\n```\n\n### Implémenter la révocation de secret et la notification utilisateur dans votre service d’alerte de secret\n\nPour l’secret scanning dans les référentiels publics, vous pouvez renforcer votre service d’alerte de secret en lui permettant de révoquer les secrets exposés et d’avertir les utilisateurs affectés. Vous pouvez choisir la façon dont vous implémentez ces fonctionnalités dans votre service d’alerte de secret, mais nous vous recommandons de considérer tous les secrets pour lesquels GitHub vous envoie des messages les indiquant comme étant publics et compromis.\n\n### Fournir des commentaires pour les faux positifs\n\nNous collectons des commentaires sur la validité des secrets individuels détectés dans les réponses des partenaires. Si vous voulez participer, envoyez-nous un e-mail à <a href=\"mailto:secret-scanning@github.com\"><secret-scanning@github.com></a>.\n\nQuand nous vous signalons des secrets, nous envoyons un tableau JSON dont chaque élément contient le jeton, l’identificateur du type et l’URL de commit. Quand vous nous envoyez des commentaires, vous nous indiquez si le jeton détecté correspondait à des informations d’identification réelles ou erronées. Nous acceptons les commentaires dans les formats suivants.\n\nVous pouvez nous envoyer le jeton brut :\n\n```json\n[\n  {\n    \"token_raw\": \"The raw token\",\n    \"token_type\": \"ACompany_API_token\",\n    \"label\": \"true_positive\"\n  }\n]\n```\n\nVous pouvez également fournir le jeton haché après avoir appliqué un hachage de chiffrement unidirectionnel du jeton brut avec SHA-256 :\n\n```json\n[\n  {\n    \"token_hash\": \"The SHA-256 hashed form of the raw token\",\n    \"token_type\": \"ACompany_API_token\",\n    \"label\": \"false_positive\"\n  }\n]\n```\n\nVoici quelques points importants :\n\n* Vous devez nous envoyer uniquement la forme brute du jeton (« token\\_raw ») ou la forme hachée (« token\\_hash »), mais pas les deux.\n* Pour la forme hachée du jeton brut, vous pouvez uniquement utiliser SHA-256 pour hacher le jeton et aucun autre algorithme de hachage.\n* L’étiquette indique si le jeton est un vrai positif (« true\\_positive ») ou un faux positif (« false\\_positive »). Seules ces deux chaînes littérales en minuscules sont autorisées.\n\n> \\[!NOTE]\n> Notre délai d’expiration de demande est défini sur une valeur supérieure (c’est-à-dire 30 secondes) pour les partenaires qui fournissent des données sur les faux positifs. Si vous avez besoin d’un délai d’expiration supérieur à 30 secondes, envoyez-nous un e-mail à <a href=\"mailto:secret-scanning@github.com\"><secret-scanning@github.com></a>."}