{"id":20009831,"url":"https://github.com/plsyssec/lockdown-node-example","last_synced_at":"2025-07-16T15:33:06.433Z","repository":{"id":71979497,"uuid":"139641338","full_name":"PLSysSec/lockdown-node-example","owner":"PLSysSec","description":null,"archived":false,"fork":false,"pushed_at":"2018-10-23T10:32:33.000Z","size":41,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-02T01:44:05.032Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PLSysSec.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-03T22:04:23.000Z","updated_at":"2018-10-23T10:32:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"765ee0cd-55ca-4830-a829-7398eaff18a2","html_url":"https://github.com/PLSysSec/lockdown-node-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/PLSysSec/lockdown-node-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PLSysSec%2Flockdown-node-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PLSysSec%2Flockdown-node-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PLSysSec%2Flockdown-node-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PLSysSec%2Flockdown-node-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PLSysSec","download_url":"https://codeload.github.com/PLSysSec/lockdown-node-example/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PLSysSec%2Flockdown-node-example/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265521407,"owners_count":23781500,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-13T07:17:11.813Z","updated_at":"2025-07-16T15:33:06.383Z","avatar_url":"https://github.com/PLSysSec.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lockdown\n\nLockdown is an extension to server-side runtimes that maintains the integrity of web applications. With Lockdown,\nthe runtime checks the hash of each file before executing it against a list of hashes generated before the\napplication is deployed to a production server. This integrity check prevents Remote Code Execution from File Upload\nvulnerabilities that are common with server-side scripting languages.\n\n# Lockdown + Node.js\n\nWe have implemented Lockdown for Node.js by adding the hash checking at the module level before compiling the\ncode, and verifying that it matches one of the hashes that the user provides to node in a hash file. The hash file\ncan be specified by using either the flag `--lockdown-hashfile=` or the environment variable `LOCKDOWN_HASHFILE`.\nUsing either of the two methods enables Lockdown hash checking by default. The hash file must contain a list of SHA256\nhashes of any code that will be executed by node (taking into account the special function wrapper that node\nautomatically adds to every module).\n\nWe additionally provide a special hash generation mode that can be enabled with the flag `--lockdown-gen-hashes`\nwhich outputs the hashes without enforcing the hash checking. We use this flag to generate the hashes for the\ncore node modules which we explain in the hash generation section.\n\n# Lockdown Node.js example\n\nThis repository contains a simple example to demonstrate how to work with a Lockdown-enabled node. The code\nsimply takes command line arguments and outputs even, odd, or NaN for each input. Our example depends on 4\npopular npm packages in total which we need to generate hashes for. Some packages are dynamically required\n(also referred to as lazy loading) depending on the input. The code needs at least a single input to require\n`is-number`, and only if one of the inputs is indeed a number we require `is-even`.\n\nEven though our example only depends on 4 packages, our `node_modules` directory already has 14 different\nmodules and a total of 34 .js files. Some npm packages come with their test files (e.g. minimist) which\nwe do not want to include in our TCB. We discuss in the next section some hash generation approaches\nand explain what we used for this example.\n\n## Hash generation\n\nGenerating hashes for all the files that an application depends on while keeping the TCB as small as possible is\nchallenging and there are different approaches each with their own shortcomings.\n\nOne approach, for example, is to write a script that requires each and every .js file our example depends\non and run it in the hash generation mode but this is very challenging as the number of files grows rapidly\nwith more dependencies. Additionally, if we simply require the modules themselves we could miss dynamically\nrequired dependencies in these modules. This approach, however, is suitable for generating hashes for\nthe internal node modules such as `fs`.\n\nAnother approach which we use for this example is using Webpack with our own custom loader to only\ngenerate the hashes for the files our code depends on without any extra files. Webpack take an entry\nfile and uses static analysis to find all the dependencies recursively. We will combine this approach\nwith the previous approach to get both the internal hashes as well as our code hashes. There are\nsome challenges when dealing with dependencies in the form of expressions that cannot be resolved\nstatically which we discuss in a separate document.\n\n### Internal node modules\n\n- We run node in the hash generation mode using the flag `--lockdown-gen-hashes` and require all\n  the node internal modules:\n\n```console\n$ node --lockdown-gen-hashes modules.js\nLockdown :: events.js :: f6c4ec1f5d3899866462b65a6746b77eeba6d4736836e8c94d258e9bb1fb210a\nLockdown :: internal/async_hooks.js :: 1b5cb9e96f6941ee9f32f3df38d925f5a482e13965f82f9545fa7793af9c7024\nLockdown :: internal/errors.js :: d44471a1b02a00406a59ffbf9889e06ab62e00c1f0fade194885e8eea5970d36\nLockdown :: util.js :: 4ebae400554d23853f576e4173167c766dc1c88fa3a8cad62a9c982037d8d03a\nLockdown :: internal/encoding.js :: 00e06e0efdeac644d8278dfe67195766e6b19928cc7f266bf334ac731ad81227\n...\n  ```\n\n- These modules do not change often so we only need to generate their hashes once. In fact, these hashes\n  could be made available publicly online or bundled together with node\n\n### Application and node_modules (Webpack)\n\n- We rely on Webpack to resolve all the dependencies and use a custom loader that generates hashes for each\n  dependency. A simple Webpack configuration file for this example code is provided in this repository\n\n- Our example has a total of 35 .js files (index.js and 34 in `node_modules`) but Webpack found\n  only 19 .js files that our code needs and generated hashes for them\n\n- We combine the hashes generated by Webpack with the hashes for the internal node modules in one file and\n  run node with this file\n\n## Running the code with Lockdown\n\n- We can either use the flag `--lockdown-hashfile=` or the environment variable `LOCKDOWN_HASHFILE` to provide\n  the hash file as shown below:\n\n```console\n$ node --lockdown-hashfile=./hashlock index.js a 1\nThe input a is NaN\nThe input 1 is odd\n$ LOCKDOWN_HASHFILE=./hashlock node index.js a 1\nThe input a is NaN\nThe input 1 is odd\n```\n\n## Integrity violation\n\n- When one of the files that our code depends on changes (e.g. an attacker injects malicious code), node is able\n  to detect that and crash\n\n- Below is an example of a hash violation detected by node for one of the files in our `node_modules`:\n\n```console\n$ node --lockdown-hashfile=./hashlock index.js a 1\nThe input a is NaN\nLockdown :: hash not found for file: ./node_modules/is-odd/index.js\nnode[31105]: ../src/node_contextify.cc:649:static void node::contextify::ContextifyScript::New(const v8::FunctionCallbackInfo\u003cv8::Value\u003e\u0026): Assertion `false' failed.\n 1: 0x8b93f0 node::Abort() [node]\n 2: 0x8b94df  [node]\n 3: 0x8ec598 node::contextify::ContextifyScript::New(v8::FunctionCallbackInfo\u003cv8::Value\u003e const\u0026) [node]\n 4: 0xb3db6f  [node]\n 5: 0xb3fb72 v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) [node]\n 6: 0x30c0cfe041bd\nAborted (core dumped)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplsyssec%2Flockdown-node-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplsyssec%2Flockdown-node-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplsyssec%2Flockdown-node-example/lists"}