{"id":18561424,"url":"https://github.com/apostrophecms/security-headers","last_synced_at":"2025-04-10T03:30:51.678Z","repository":{"id":45330994,"uuid":"438731765","full_name":"apostrophecms/security-headers","owner":"apostrophecms","description":"This module sends the modern HTTP security headers that are expected by various security scanners.","archived":false,"fork":false,"pushed_at":"2024-03-13T17:19:46.000Z","size":23,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-14T08:28:37.536Z","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/apostrophecms.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","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":"2021-12-15T18:23:53.000Z","updated_at":"2024-07-09T18:46:51.072Z","dependencies_parsed_at":"2024-03-13T18:44:46.735Z","dependency_job_id":null,"html_url":"https://github.com/apostrophecms/security-headers","commit_stats":{"total_commits":11,"total_committers":4,"mean_commits":2.75,"dds":"0.36363636363636365","last_synced_commit":"52363522ce8d810dde0b092c3eadd6a32aeedb49"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fsecurity-headers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fsecurity-headers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fsecurity-headers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/apostrophecms%2Fsecurity-headers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/apostrophecms","download_url":"https://codeload.github.com/apostrophecms/security-headers/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223423356,"owners_count":17142743,"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-06T22:06:48.242Z","updated_at":"2024-11-06T22:06:48.928Z","avatar_url":"https://github.com/apostrophecms.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/apostrophecms/apostrophe/main/logo.svg\" alt=\"ApostropheCMS logo\" width=\"80\" height=\"80\"\u003e\n\n  \u003ch1\u003eSecurity Headers\u003c/h1\u003e\n  \u003cp\u003e\n    \u003ca aria-label=\"Apostrophe logo\" href=\"https://v3.docs.apostrophecms.org\"\u003e\n      \u003cimg src=\"https://img.shields.io/badge/MADE%20FOR%20ApostropheCMS-000000.svg?style=for-the-badge\u0026logo=Apostrophe\u0026labelColor=6516dd\"\u003e\n    \u003c/a\u003e\n    \u003ca aria-label=\"Test status\" href=\"https://github.com/apostrophecms/security-headers/actions\"\u003e\n      \u003cimg alt=\"GitHub Workflow Status (branch)\" src=\"https://img.shields.io/github/workflow/status/apostrophecms/security-headers/tests/main?label=Tests\u0026logo=github\u0026labelColor=000\u0026style=for-the-badge\"\u003e\n    \u003c/a\u003e\n    \u003ca aria-label=\"Join the community on Discord\" href=\"http://chat.apostrophecms.org\"\u003e\n      \u003cimg alt=\"\" src=\"https://img.shields.io/discord/517772094482677790?color=5865f2\u0026label=Join%20the%20Discord\u0026logo=discord\u0026logoColor=fff\u0026labelColor=000\u0026style=for-the-badge\u0026logoWidth=20\"\u003e\n    \u003c/a\u003e\n    \u003ca aria-label=\"License\" href=\"https://github.com/apostrophecms/security-headers/blob/main/LICENSE.md\"\u003e\n      \u003cimg alt=\"\" src=\"https://img.shields.io/static/v1?style=for-the-badge\u0026labelColor=000000\u0026label=License\u0026message=MIT\u0026color=3DA639\"\u003e\n    \u003c/a\u003e\n  \u003c/p\u003e\n\u003c/div\u003e\n\nThis module sends the modern HTTP security headers that are expected by various security scanners. The default settings are strict in most regards, so see below for adjustments you may wish to make.\n\n## Warning\n\nSome third-party services, including Google Analytics, Google Fonts, YouTube and Vimeo, are included as allowed sources for HTML, CSS and scripts in the standard configuration. However even with these permissive settings not all third-party services compatible with Apostrophe will be permitted out of the box. For instance, because they are used relatively rarely, no special testing has been done for Wufoo or Infogram. You should test your site and configure custom policies accordingly.\n\n## Installation\n\nTo install the module, use the command line to run this command in an Apostrophe project's root directory:\n\n```\nnpm install @apostrophecms/security-headers\n```\n\n## Usage\n\nActivate the `@apostrophecms/security-headers` module in the project's `app.js` file:\n\n```javascript\nrequire('apostrophe')({\n  shortName: 'my-project',\n  modules: {\n    '@apostrophecms/security-headers': {}\n  }\n});\n```\n\nThe headers to be sent can be overriden by setting them as options to the module in the project-level `modules/@apostrophecms/security-headers/index.js` file:\n\n```javascript\n// in modules/@apostrophecms/security-headers/index.js\nmodule.exports = {\n  options: {\n    'X-Frame-Options': 'DENY'\n  }\n};\n```\n\nYou can also disable a header entirely by setting the option for it to `false`.\n\n### Guide to configuring headers\n\nHere are the headers that are sent by default, with their default values:\n\n```javascript\nmodule.exports = {\n  // in modules/@apostrophecms/security-headers/index.js\n  options: {\n    // 1 year. Do not include subdomains as they could be unrelated sites\n    'Strict-Transport-Security': 'max-age=31536000',\n    // You may also set to DENY, however future Apostrophe modules may use\n    // iframes to present previews etc.\n    'X-Frame-Options': 'SAMEORIGIN',\n    // If you have issues with broken images etc., make sure content type\n    // configuration is correct for your production server\n    'X-Content-Type-Options': 'nosniff',\n    // Very new. Used to entirely disable browser features like geolocation per host.\n    // Since we don't know what your site may need, we don't try to set this\n    // header by default (false means \"don't send the header\")\n    'Permissions-Policy': false,\n    // Don't send a \"Referer\" (sp) header unless the new URL shares the same\n    // origin. You can set this to `false` if you prefer cross-origin \"Referer\"\n    // headers be sent. Apostrophe does not rely on them\n    'Referrer-Policy': 'same-origin',\n    // `true` means it should be computed according to the rules below.\n    // You may also pass your own string, or `false` to not send this header.\n    // The `policies` option and all of its sub-options are ignored unless\n    // `Content-Security-Policy` is `true`.\n    'Content-Security-Policy': true\n  }\n};\n```\n\nFor more information about these security headers see the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#security).\n\n### Configuring the `Content-Security-Policy` header\n\nThe `Content-Security-Policy` header is more complex than the others. As described [in MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy), this header is used to determine which hosts are permitted as a source for stylesheets, scripts, images and more. While you can certainly set a fixed value for it, the default response for it is the result of merging together options for individual use cases (\"policies\") as shown below. This makes it easier to think about what you need to allow for a particular purpose. The actual header sent contains all of the permissions required to satisfy all of the policies.\n\nThe default configuration is shown below. You do **not** have to copy and paste the entire default configuration, only the policies you wish to change. Any policy you specify explicitly at project level overrides all of the default settings shown below for that\npolicy. You may set one to `false` to completely disable it. You may also introduce entirely new policies.\n\nPolicies of the same type from different sub-options are merged, with the largest set of keywords and hosts enabled. This is done because browsers do not support more than one style-src policy, for example, but do support specifying several hosts.\n\nNote the `HOSTS` wildcard which matches all expected hosts that Apostrophe is aware of, including `baseUrl` settings, CDN hosts and locale-specific hostnames.\n\n```javascript\nmodule.exports = {\n  // in modules/@apostrophecms/security-headers/index.js\n  options: {\n    policies: {\n      general: {\n        'default-src': 'HOSTS',\n        // Because it is necessary for some of the output of our rich text editor\n        'style-src': \"HOSTS 'unsafe-inline'\",\n        'script-src': 'HOSTS',\n        'font-src': 'HOSTS',\n        'img-src': 'HOSTS blob:',\n        'frame-src': \"'self'\"\n      },\n\n      // Set this sub-option to false if you wish to forbid google fonts\n      googleFonts: {\n        'style-src': 'fonts.googleapis.com',\n        'font-src': 'fonts.gstatic.com'\n      },\n\n      // Set this sub-option to false if you do not use the video widget\n      oembed: {\n        'frame-src': '*.youtube.com *.vimeo.com',\n        'img-src': '*.ytimg.com'\n      },\n\n      // Set this sub-option to false if you do not wish to permit Google Analytics and\n      // Google Adsense\n      analytics: {\n        'default-src': '*.google-analytics.com *.doubleclick.net',\n        // Note that use of google tag manager by definition brings in scripts from\n        // more third party sites and you will need to add policies for them\n        'script-src': '*.google-analytics.com *.doubleclick.net *.googletagmanager.com',\n      }\n    }\n  }\n};\n```\n\n#### Inline style attributes are still allowed\n\nNote that `style-src` is set by default to permit inline style attributes. This is currently necessary because the output of the tiptap rich text editor used in Apostrophe involves inline\nstyles in some cases.\n\n#### Inline script tags are **not** allowed\n\nInline script tags (those without a `src`) are **not** allowed by the default policies shown above, as this is one of the primary benefits of using the `Content-Security-Policy` header. If you do choose to output an inline script tag, you may do so if you use the \"nonce\" template argument provided by this module, like this:\n\n```\n\u003cscript nonce=\"{{ nonce }}\"\u003e\n  // inline script code here\n\u003c/script\u003e\n```\n\nThe `nonce` template variable is always available in Nunjucks templates when using this module. It is generated uniquely for each new page request.\n\nThe nonce mechanism ensures that the script tag was the intention of the developer and is not an XSS attack. However please note that you will lose the security benefits of this if you output other user-entered data inside the script tag without properly escaping it, for instance using the `| json` nunjucks filter.\n\nSetting the nonce attribute on a DOM element has no ill effects when this module is not in use, so it is OK to set it in inline script tags output by npm modules intended for use with or without this module.\n\n### Custom policies\n\nYou may add any number of custom policies. Any sub-option nested in your\n`policies` option is treated just like the standard cases above and merged into\nthe final `Content-Security-Policy` header.\n\n### Disabling standard policies\n\nYou may set any of the standard policy sub-options above to `false` to disable them.\n\n### Hosts wildcard\n\nNote that the `HOSTS` wildcard is automatically replaced with a list of hosts including any `baseUrl` host, localized hostnames for specific locales, CDN hosts from your uploadfs configuration, and `self`. Use of this wildcard is recommended as Apostrophe pushes assets to Amazon S3, CDNs, etc. when configured to do so, including scripts and stylesheets.\n\nYou may override the normal list of hosts for `HOSTS` by setting the `legitimateHosts` option to an array of strings. You can also extend or override the `legitimateHosts` method of this module at project level.\n\nFor example:\n\n```javascript\nmodule.exports = {\n  // in modules/@apostrophecms/security-headers/index.js\n  options: {\n    legitimateHosts: [ 'mysite.com', 'www.mysite.com', 'surprise.mysite.com' ]\n  }\n};\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapostrophecms%2Fsecurity-headers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fapostrophecms%2Fsecurity-headers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fapostrophecms%2Fsecurity-headers/lists"}