Search code examples
securitynpmpackage-managers

What are the differences between and purposes of the NPM shasum, integrity, and signatures fields?


An example NPM package metadata JSON looks like this (with dist moved toward the top):

{
  "name": "lodash",
  "version": "4.17.21",
  "description": "Lodash modular utilities.",
  "dist": {
    "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
    "shasum": "679591c564c3bffaae8454cf0b3df370c3d6911c",
    "tarball": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
    "fileCount": 1054,
    "unpackedSize": 1412415,
    "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgMS3ZCRA9TVsSAnZWagAA8+4P/jx+SJ6Ue5oAJjz0L7gw\nLDD5YvP8aoliFq4GYkwUXfVQvOwomIPfa+U5Kao/hDfuwFQ/Bq5D5nSsl2bj\nrjJgvlKXna0SId8AgDgY2fB7zSfninuJvalY4iTWMN8DFSpG0XE2QFfoKpd3\njDmuzcNtgr79QV6DgjOVkHiP1IGNDlLTc1QEKiwo/5CdGQi1q/iCj6dViQMJ\nByuuuV2Qzi3f/FI25cG797WZar1MHhhlcnB50HiVBGp54IZOyuqdqWPduZQo\nvhONtonxPGBm3/J+uAkeUSSyL3Ud+FzLvdg8WEI9gDL0yvU4k0FcsnOONEYn\nngLaKEsw2xAnPBYW3Lf73Jnpwx6FAT3k49kgzxiNYSxEo7x4wiuNtBoDMyNw\nEKj6SZ0bUNmaJgiMfDnnDjCKjI3JrO1hho8z6CkwuvxuWLlW9wSsVayggzAI\nEhfeTeISugVHh332oDY2MI/Ysu8MnVN8fGmqeYQBBFj3aWatuA2NvVjACnX/\n54G7FtCU8TxZpm9shFRSopBx8PeI3r+icx1CT8YVFypY416PLnidHyqtME1G\neuRd1nWEz18hvVUAEHmuvHo+EPP3tITmTTUPQcZGMdBcZC+4UBmPMWX466HE\nbHw4aOnUWMa0sWfsERC5xzRZAb4lgMPEoTOnZyN4usMy7x9TzGZKZvU24HUE\nmpae\r\n=NOmG\r\n-----END PGP SIGNATURE-----\r\n",
    "signatures": [
      {
        "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA",
        "sig": "MEUCIF3Yithbtmy1aEBNlfNWbLswAfPIyQUuNUGARD3Ex2t4AiEA6TlN2ZKJCUpS/Sf2Z6MduF1BNSvayHIpu5wAcICcKXw="
      }
    ]
  },
  "keywords": [
    "modules",
    "stdlib",
    "util"
  ],
  "homepage": "https://lodash.com/",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/lodash/lodash.git"
  },
  "icon": "https://lodash.com/icon.svg",
  "license": "MIT",
  "main": "lodash.js",
  "author": {
    "name": "John-David Dalton",
    "email": "[email protected]"
  },
  "contributors": [
    {
      "name": "John-David Dalton",
      "email": "[email protected]"
    },
    {
      "name": "Mathias Bynens",
      "email": "[email protected]"
    }
  ],
  "scripts": {
    "test": "echo \"See https://travis-ci.org/lodash-archive/lodash-cli for testing details.\""
  },
  "gitHead": "c6e281b878b315c7a10d90f9c2af4cdb112d9625",
  "bugs": {
    "url": "https://github.com/lodash/lodash/issues"
  },
  "_id": "[email protected]",
  "_nodeVersion": "14.15.5",
  "_npmVersion": "6.14.11",
  "_npmUser": {
    "name": "bnjmnt4n",
    "email": "[email protected]"
  },
  "directories": {

  },
  "maintainers": [
    {
      "name": "mathias",
      "email": "[email protected]"
    },
    {
      "name": "jdalton",
      "email": "[email protected]"
    },
    {
      "name": "bnjmnt4n",
      "email": "[email protected]"
    }
  ],
  "_npmOperationalInternal": {
    "host": "s3://npm-registry-packages",
    "tmp": "tmp/lodash_4.17.21_1613835736675_0.01913912595366596"
  },
  "_hasShrinkwrap": false
}

Here are my questions related to the 3 fields:

  • dist.shasum: This is said to be the shasum <tarball> of the tarball (CLI command), using SHA-1.
  • dist.integrity: ChatGPT says (I think...) that this serves the same purpose as the shasum, but is "more secure" (because sometimes it uses SHA-256 or SHA-512 encoding. But what is the real difference or reason for having both dist.shasum and dist.integrity? Do they differ in any other way? Do you need to check both?
  • dist.signatures[*].sig: What does this signature do? How do you use it? ChatGPT seems to say this is tested using public keys, but who has which sides of the keys, and when does this get used? When/why would you have more than one?

Since we're here, too, how does dist.npm-signature relate to the 3 things above?


Solution

  • https://blog.npmjs.org/post/172999548390/new-pgp-machinery seems to have the answers you are looking for, in particular

    The shasum is a SHA-1 sum of the file. We’ve supplemented this to the side with an integrity field, based on the subresource integrity specification, to allow us to migrate to different hashing algorithms

    and

    The shasums above […] do not, however, protect against man-in-the-middle attacks. […] We offer you a way to detect this kind of tampering by signing package integrity fields along with some data that uniquely identifies the package-version. […] The registry signs the following data for each new package-version published: <package>@<version>:<integrity>. […] The detached signature is stored in the npm-signature field in dist.

    then https://docs.npmjs.com/about-registry-signatures says

    The public npm registry is migrating away from the existing PGP signatures [which were deprecated on April 25th 2023] to ECDSA signatures that are more compact and can be verified without extra dependencies in the npm CLI. […] Signatures are provided in the package's packument in each published version within the dist object.

    See also How does npm’s ECDSA signing system improve security?.