├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── check-external-links.yml │ ├── headers-generate-json-files.yml │ ├── monitoring-oshp-site-references.yml │ ├── monitoring-technical-references-generate-dashboard.yml │ └── tab-stats-headers-generate-related-files.yml ├── .gitignore ├── .vscode └── extensions.json ├── Gemfile ├── LICENSE.txt ├── README.md ├── _config.yml ├── assets ├── css │ └── styles.css ├── images │ ├── README.md │ ├── miscellaneous_pna_request_header_schema.png │ ├── oshp_logo.png │ └── response_headers_header_lifecycle_flow.png ├── js │ └── direct-link-handler.js ├── misc │ ├── backup_discussions_29.pdf │ ├── demo_csp_bypass_due_to_no_base_uri_directive.mp4 │ ├── demo_csp_bypass_due_to_no_form_action_directive.mp4 │ ├── demo_csp_bypass_due_to_no_frame_ancestors_directive.mp4 │ ├── demo_information_disclosure_via_browser_file_caching.mp4 │ ├── nolimitsecu_449_notes.pdf │ └── nolimitsecu_449_podcast.mp3 └── tab_stats_generated_images │ ├── .keep │ ├── 0753b0c4fecc8c56d81e31f36bc8c397cea5032b.png │ ├── 15d82f7cac9021b254fdf8fed98bb870acc436fb.png │ ├── 2da94599d03c73073ac60b0d8864152f8609cc5b.png │ ├── 2ec5e9a684938a169c757a7a631595c53fccc769.png │ ├── 49f6a7d15e9a2e3fd4cad94360d37e83ef05fa00.png │ ├── 577d76c6092c4da6347e1d2c89523dd13a1925f7.png │ ├── 5808d16f90388bd6309eb12d74010d1c4a8518cf.png │ ├── 78fc7e8d03077546e27c016ee80b2143dc4ebb08.png │ ├── 7b2906800d5eb94d25d0f5cf18322155e8f2192d.png │ ├── 87eabe1fe075f9034dc4db8f76be07da0d08afe3.png │ ├── 8dd898e970a4cc540e0394ace9c9cedd425bc1c5.png │ ├── 9cf15b18b743939cbe01342ed5461bc7af6c4d36.png │ ├── be611e71c615c27471d766612bfb7e8b05d743c7.png │ ├── c0b5a705e7e94af3f71ef579bb01b45c2a80ca6b.png │ ├── c313c0ceef6eb3116547426b41bdf278df2cc0c6.png │ ├── c7ef83055cf836a48ed9dd26b3a8d55103645022.png │ ├── ccc438a754b6d9324c9c1ea62662969c6114bfdf.png │ ├── cfaf56ab8ec6588aa6ee9297b4f93638640d1048.png │ ├── e58d592c018472a09777c3fd5440f556bd176dd5.png │ ├── e7e550d9cbff786153f7f13f664361e41efee57c.png │ └── e90a8350bb77972b086599b65efc8fcd02036a11.png ├── ci ├── headers_add.json ├── headers_generate_json_files.py ├── headers_remove.json ├── monitoring_oshp_site_references.py ├── monitoring_technical_references_generate_dashboard.py ├── tab_stats_generate_md_file.py ├── tab_stats_generate_png_files.sh ├── tab_stats_manage_generation.sh ├── tab_stats_mermaid_config.json └── validate_md_links.py ├── index.md ├── info.md ├── leaders.md ├── logo ├── generated-via-ai │ ├── logo01-chatgpt.png │ ├── logo02-microsoft-copilot.png │ ├── logo03-microsoft-copilot.png │ ├── logo04-microsoft-copilot.png │ ├── logo05-microsoft-copilot.png │ ├── logo06-microsoft-copilot.png │ ├── logo07-chatgpt.png │ ├── logo08-microsoft-copilot.png │ └── logo09-microsoft-copilot.png ├── logo-all.pdf ├── logo-black-on-transparent.png ├── logo-black-on-white.jpg ├── logo-blue-on-transparent.png ├── logo-blue-on-white.jpg ├── logo-mockup1.jpg ├── logo-mockup2.jpg ├── logo-mockup3.jpg ├── logo-source.ai ├── logo-source.eps ├── logo-white-on-black.jpg └── logo-white-on-transparent.png ├── markdown-link-check_config.json ├── monitoring_technical_references_dashboard.md ├── project.code-workspace ├── requirements.txt ├── tab_bestpractices.md ├── tab_browsersupport.md ├── tab_casestudies.md ├── tab_codesnippets.md ├── tab_headers.md ├── tab_logo.md ├── tab_misc.md ├── tab_statistics.md └── tab_technical.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Define the mapping for PR opened 2 | # Doc: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners 3 | # Info: Order is important; the last matching pattern takes the most precedence. 4 | # For MD file is Ricardo 5 | # Dominique cannot be set because he does not have write access on the repo 6 | *.md @riramar 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: righettod 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[NEW-FEATURE]" 5 | labels: enhancement 6 | assignees: righettod 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | > [!WARNING] 2 | > @righettod : Take a first look at the PR. 3 | 4 | > [!IMPORTANT] 5 | > Describe the changes performed by the PR. 6 | 7 | Hi, 8 | 9 | ... 10 | 11 | Thanks in advance 😀. 12 | -------------------------------------------------------------------------------- /.github/workflows/check-external-links.yml: -------------------------------------------------------------------------------- 1 | name: check_validity_of_all_external_links 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | schedule: 9 | - cron: '0 0 * * 0' 10 | permissions: read-all 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Set up Python 3.12 17 | uses: actions/setup-python@v5 18 | with: 19 | python-version: "3.12" 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install -r requirements.txt 24 | - name: Run the validation 25 | run: | 26 | python ./ci/validate_md_links.py -c markdown-link-check_config.json 27 | -------------------------------------------------------------------------------- /.github/workflows/headers-generate-json-files.yml: -------------------------------------------------------------------------------- 1 | name: update_headers_reference_json_files 2 | on: 3 | workflow_dispatch: 4 | push: 5 | paths: 6 | - 'tab_bestpractices.md' 7 | permissions: read-all 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up Python 3.12 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: "3.12" 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 23 | - name: Run generation of both files 24 | run: | 25 | cd ci; python headers_generate_json_files.py 26 | - name: Set up Git user 27 | run: git config --global user.email "gha@github.com"; git config --global user.name "GHActionBot" 28 | - name: Commit update 29 | run: git commit -am "Sync reference headers json files"; git push 30 | - name: Attach generated artefacts 31 | uses: actions/upload-artifact@v4 32 | with: 33 | name: data 34 | path: | 35 | ci/headers_add.json 36 | ci/headers_remove.json 37 | -------------------------------------------------------------------------------- /.github/workflows/monitoring-oshp-site-references.yml: -------------------------------------------------------------------------------- 1 | name: perform_monitoring_oshp_site_references 2 | on: 3 | workflow_dispatch: 4 | push: 5 | paths: 6 | - 'tab_casestudies.md' 7 | schedule: 8 | - cron: '0 0 * * 0' 9 | permissions: read-all 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up Python 3.12 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: "3.12" 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install -r requirements.txt 23 | - name: Run the validation 24 | run: | 25 | cd ci; python monitoring_oshp_site_references.py 26 | 27 | 28 | -------------------------------------------------------------------------------- /.github/workflows/monitoring-technical-references-generate-dashboard.yml: -------------------------------------------------------------------------------- 1 | name: update_monitoring_technical_references_dashboard 2 | on: 3 | workflow_dispatch: 4 | push: 5 | paths: 6 | - 'tab_technical.md' 7 | schedule: 8 | - cron: '0 0 * * 0' 9 | permissions: read-all 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Set up Python 3.12 18 | uses: actions/setup-python@v5 19 | with: 20 | python-version: "3.12" 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install -r requirements.txt 25 | - name: Run generation of the dashboard 26 | run: | 27 | cd ci; python monitoring_technical_references_generate_dashboard.py "${{ secrets.GITHUB_TOKEN }}" 28 | - name: Set up Git user 29 | run: git config --global user.email "gha@github.com"; git config --global user.name "GHActionBot" 30 | - name: Commit update 31 | run: git commit -am "Sync monitoring technical references dashboard file"; git push 32 | 33 | -------------------------------------------------------------------------------- /.github/workflows/tab-stats-headers-generate-related-files.yml: -------------------------------------------------------------------------------- 1 | name: update_tab_stats_related_files 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: '0 0 5 * *' 6 | push: 7 | paths: 8 | - 'ci/tab_stats_generate_md_file.py' 9 | - 'ci/tab_stats_generate_png_files.sh' 10 | - 'ci/tab_stats_manage_generation.sh' 11 | permissions: read-all 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Set up Python 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: "3.10" 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 27 | sudo apt install -y wget dos2unix sqlite3 28 | - name: Run update of the tab related files 29 | run: | 30 | cd ci; bash tab_stats_manage_generation.sh 31 | - name: Set up Git user 32 | run: git config --global user.email "gha@github.com"; git config --global user.name "GHActionBot" 33 | - name: Commit update 34 | run: git add --all; git commit -am "Sync tab stats related files"; git push 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | env 4 | _site/ 5 | # File used as temporary workspace 6 | scratchpad.md -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["davidanson.vscode-markdownlint", "ms-python.python"] 3 | } -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | group :jekyll_plugins do 3 | gem "github-pages" 4 | end -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OWASP Secure Headers Project 2 | 3 | ![OSHP Logo](assets/images/oshp_logo.png) 4 | 5 | [![OWASP Production](https://img.shields.io/badge/owasp-production%20project-800080.svg)](https://www.owasp.org/projects) 6 | 7 | [![External Links Validity Check](https://github.com/OWASP/www-project-secure-headers/actions/workflows/check-external-links.yml/badge.svg?branch=master)](https://github.com/OWASP/www-project-secure-headers/actions/workflows/check-external-links.yml) 8 | 9 | [![Update headers reference JSON files](https://github.com/OWASP/www-project-secure-headers/actions/workflows/headers-generate-json-files.yml/badge.svg?branch=master)](https://github.com/OWASP/www-project-secure-headers/actions/workflows/headers-generate-json-files.yml) 10 | 11 | [![Update monitoring technical references dashboard](https://github.com/OWASP/www-project-secure-headers/actions/workflows/monitoring-technical-references-generate-dashboard.yml/badge.svg?branch=master)](https://github.com/OWASP/www-project-secure-headers/actions/workflows/monitoring-technical-references-generate-dashboard.yml) 12 | 13 | [![Perform_monitoring_oshp_site_references](https://github.com/OWASP/www-project-secure-headers/actions/workflows/monitoring-oshp-site-references.yml/badge.svg?branch=master)](https://github.com/OWASP/www-project-secure-headers/actions/workflows/monitoring-oshp-site-references.yml) 14 | 15 | [![update_tab_stats_related_files](https://github.com/OWASP/www-project-secure-headers/actions/workflows/tab-stats-headers-generate-related-files.yml/badge.svg?branch=master)](https://github.com/OWASP/www-project-secure-headers/actions/workflows/tab-stats-headers-generate-related-files.yml) 16 | 17 | 🎯 The **OWASP Secure Headers Project** (also named **OSHP**) describes HTTP response headers that your application can use to increase the security of your application. Once set, these HTTP response headers can restrict modern browsers from running into easily preventable vulnerabilities. The OWASP Secure Headers Project intends to raise awareness and use of these headers. 18 | 19 | ## Introduction 20 | 21 | 📚 HTTP headers are well known and also despised. Seeking a balance between usability and security, developers implement functionality through the headers that can make applications more versatile or secure. But in practice how are the headers being implemented? What sites follow the best implementation practices? Big companies, small, all or none? 22 | 23 | ## Description 24 | 25 | 📚 We aim to publish reports on header usage stats, developments and changes, code libraries that make these headers easily accessible to developers on a range of platforms, and data sets concerning the general usage of these headers. 26 | 27 | 🌍 The OWASP Secure Headers Project was migrated to a [new OWASP website](https://owasp.org/www-project-secure-headers/). 28 | 29 | 📁 You can still access the old website [here](https://wiki.owasp.org/index.php/OWASP_Secure_Headers_Project). 30 | 31 | ## Logo 32 | 33 | 🎨 The project official logo is stored into the folder [logo](logo) as well as into the [OWASP Swag](https://github.com/OWASP/owasp-swag) GitHub repository. 34 | 35 | ## Issue and discussions 36 | 37 | 💬 Both are handled using the following GitHub features: 38 | 39 | * [Issues](https://github.com/OWASP/www-project-secure-headers/issues). 40 | * [Discussions](https://github.com/OWASP/www-project-secure-headers/discussions). 41 | 42 | ## Content editor 43 | 44 | 👩‍💻 Content editing is done with [Visual Studio Code](https://code.visualstudio.com/). 45 | 46 | 📦 A [workspace file](project.code-workspace) is provided with [recommended extensions](.vscode/extensions.json). 47 | 48 | ## Automatically generated content 49 | 50 | > [!TIP] 51 | > 🔬[Technical References Dashboard](monitoring_technical_references_dashboard.md). 52 | 53 | 🏭 The folder [ci](ci) (**CI** for **C**ontinuous **I**ntegration) contains materials to generate the following content. 54 | 55 | 📝 Generation of the both JSON files containing the header recommended to add and remove: 56 | 57 | * Processing is performed by this GitHub action [workflow](.github/workflows/headers-generate-json-files.yml) every time the file [tab_bestpractices.md](tab_bestpractices.md) is modified. 58 | 59 | 📝 Generation of the [markdown file](monitoring_technical_references_dashboard.md) with the update health state of all GitHub repositories mentioned in the tab named **[Technical](tab_technical.md)**: 60 | 61 | * Processing is performed by this GitHub action [workflow](.github/workflows/monitoring-technical-references-generate-dashboard.yml) every week with a cron expression indicating `At 00:00 on Sunday` or every time the file [tab_technical.md](tab_technical.md) is modified. 62 | 63 | 📝 Generation of the file [tab_statistics.md](tab_statistics.md) as well as [all related PNG files](assets/tab_stats_generated_images): 64 | 65 | * Processing is performed by this GitHub action [workflow](.github/workflows/tab-stats-headers-generate-related-files.yml) every month with a cron expression indicating `At 00:00 on day-of-month 5` or every time any of the following files is modified: 66 | * [ci/tab_stats_manage_generation.sh](ci/tab_stats_manage_generation.sh). 67 | * [ci/tab_stats_generate_md_file.py](ci/tab_stats_generate_md_file.py). 68 | * [ci/tab_stats_generate_png_files.sh](ci/tab_stats_generate_png_files.sh). 69 | * The specified cron expression was selected because the database containing the data used by the script [tab_stats_generate_md_file.py](ci/tab_stats_generate_md_file.py) is updated on the first day of each month by the project [oshp-stats](https://github.com/oshp/oshp-stats/): 70 | * See [here](https://github.com/oshp/oshp-stats/blob/main/.github/workflows/update-datasource.yml) for technical details. 71 | 72 | ## Social media communication 73 | 74 | 📩 This template is used to announce news on social media about OSHP update: 75 | 76 | ```text 77 | 📡 OWASP Secure Headers Project: [MESSAGE]. 78 | 79 | #appsec #appsecurity #owasp_shp 80 | 81 | [PRINT_SCREEN_IN_PNG_FORMAT_WHEN_APPLICABLE] 82 | 83 | 📖 [LINK_TO_OSHP_SECTION] 84 | 85 | 💡 Source used: 86 | 87 | [LINK_TO_SOURCE_USED] 88 | ``` 89 | 90 | ## Contributors 91 | 92 | 💌 Contributors to OSHP, before the migration of the project to [GitHub](https://github.com/OWASP/www-project-secure-headers): 93 | 94 | * [Alexandre Menezes](mailto:alexandre.fmenezes@owasp.org) 95 | * [Adam Averay](https://github.com/adamaveray) 96 | * [Jim Manico](https://github.com/jmanico) 97 | 98 | 💌 Visit this [page](https://github.com/OWASP/www-project-secure-headers/graphs/contributors) for updated information about the contributors since the migration of the project to GitHub. 99 | 100 | ## Licensing 101 | 102 | 📑 This project content is free to use. It is licensed under the [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0). 103 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: "owasp/www--site-theme@main" 2 | 3 | # core files/folders to exclude 4 | exclude: 5 | - README.md 6 | - monitoring_technical_references_dashboard.md 7 | 8 | plugins: 9 | - jekyll-include-cache-0.2.0 10 | -------------------------------------------------------------------------------- /assets/css/styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | Set table first column width 3 | See https://github.com/oshp/oshp-tracking/issues/16#issuecomment-1463299928 4 | */ 5 | th:first-child { 6 | width: 30%; 7 | } -------------------------------------------------------------------------------- /assets/images/README.md: -------------------------------------------------------------------------------- 1 | # Placeholder 2 | 3 | Put images you wish to link to in this folder 4 | 5 | link would be in form `/assets/images/` 6 | 7 | # Mermaid code for images 8 | 9 | > 💻 This [live editor](https://mermaid.live/) was used. 10 | -------------------------------------------------------------------------------- /assets/images/miscellaneous_pna_request_header_schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/images/miscellaneous_pna_request_header_schema.png -------------------------------------------------------------------------------- /assets/images/oshp_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/images/oshp_logo.png -------------------------------------------------------------------------------- /assets/images/response_headers_header_lifecycle_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/images/response_headers_header_lifecycle_flow.png -------------------------------------------------------------------------------- /assets/js/direct-link-handler.js: -------------------------------------------------------------------------------- 1 | /* Script to allow to direct link a section (header) into a tab (div). */ 2 | function handleDirectLink() { 3 | if (window.location.hash) { 4 | //Format expected is [TAB-ID]_[HEADER-ID-INSIDE-TAB] 5 | let directLocationRef = window.location.hash; 6 | const validationRegex = new RegExp("^#[a-z0-9\-]{1,100}_[a-z0-9\-]{1,100}$"); 7 | //Ensure that the directLink respect the syntax 8 | if (validationRegex.test(directLocationRef)) { 9 | directLocationRef = directLocationRef.substring(1);//Remove the starting dash character 10 | let parts = directLocationRef.split("_"); 11 | let tabLocation = parts[0].substring(4) + "-link";//remove the "-div" prefix and add the "-link" suffix 12 | let headerLocation = parts[1]; 13 | console.debug(`[DirectLinkHandler] Move to tab '${tabLocation}' on header '${headerLocation}'.`); 14 | //Retrieve the target TAB that is a link to a DIV and trigger a click 15 | let tabLink = document.getElementById(tabLocation) 16 | if (typeof tabLink !== "undefined") { 17 | tabLink.click(); 18 | //Now move to the target header that have a link with a href to it 19 | let links = document.querySelectorAll("a"); 20 | let headerLink = null; 21 | links.forEach(function (lnk) { 22 | if (lnk.href.endsWith(`#${headerLocation}`) && headerLink === null) { 23 | headerLink = lnk; 24 | } 25 | }); 26 | if (headerLink !== null) { 27 | setTimeout(() => { headerLink.click(); console.debug(`[DirectLinkHandler] Move performed.`); }, "2000"); 28 | console.debug(`[DirectLinkHandler] Move scheduled.`) 29 | } else { 30 | console.warn(`[DirectLinkHandler] Header tag not found.`); 31 | } 32 | } else { 33 | console.warn(`[DirectLinkHandler] Tab link not found.`); 34 | } 35 | } else { 36 | console.warn("[DirectLinkHandler] Direct link does not match the validation regex.") 37 | } 38 | } 39 | } 40 | 41 | window.addEventListener("load", function () { 42 | handleDirectLink(); 43 | }) 44 | -------------------------------------------------------------------------------- /assets/misc/backup_discussions_29.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/misc/backup_discussions_29.pdf -------------------------------------------------------------------------------- /assets/misc/demo_csp_bypass_due_to_no_base_uri_directive.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/misc/demo_csp_bypass_due_to_no_base_uri_directive.mp4 -------------------------------------------------------------------------------- /assets/misc/demo_csp_bypass_due_to_no_form_action_directive.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/misc/demo_csp_bypass_due_to_no_form_action_directive.mp4 -------------------------------------------------------------------------------- /assets/misc/demo_csp_bypass_due_to_no_frame_ancestors_directive.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/misc/demo_csp_bypass_due_to_no_frame_ancestors_directive.mp4 -------------------------------------------------------------------------------- /assets/misc/demo_information_disclosure_via_browser_file_caching.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/misc/demo_information_disclosure_via_browser_file_caching.mp4 -------------------------------------------------------------------------------- /assets/misc/nolimitsecu_449_notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/misc/nolimitsecu_449_notes.pdf -------------------------------------------------------------------------------- /assets/misc/nolimitsecu_449_podcast.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/misc/nolimitsecu_449_podcast.mp3 -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/.keep: -------------------------------------------------------------------------------- 1 | NEVER DELETE ME!!!! 2 | -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/0753b0c4fecc8c56d81e31f36bc8c397cea5032b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/0753b0c4fecc8c56d81e31f36bc8c397cea5032b.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/15d82f7cac9021b254fdf8fed98bb870acc436fb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/15d82f7cac9021b254fdf8fed98bb870acc436fb.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/2da94599d03c73073ac60b0d8864152f8609cc5b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/2da94599d03c73073ac60b0d8864152f8609cc5b.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/2ec5e9a684938a169c757a7a631595c53fccc769.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/2ec5e9a684938a169c757a7a631595c53fccc769.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/49f6a7d15e9a2e3fd4cad94360d37e83ef05fa00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/49f6a7d15e9a2e3fd4cad94360d37e83ef05fa00.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/577d76c6092c4da6347e1d2c89523dd13a1925f7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/577d76c6092c4da6347e1d2c89523dd13a1925f7.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/5808d16f90388bd6309eb12d74010d1c4a8518cf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/5808d16f90388bd6309eb12d74010d1c4a8518cf.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/78fc7e8d03077546e27c016ee80b2143dc4ebb08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/78fc7e8d03077546e27c016ee80b2143dc4ebb08.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/7b2906800d5eb94d25d0f5cf18322155e8f2192d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/7b2906800d5eb94d25d0f5cf18322155e8f2192d.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/87eabe1fe075f9034dc4db8f76be07da0d08afe3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/87eabe1fe075f9034dc4db8f76be07da0d08afe3.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/8dd898e970a4cc540e0394ace9c9cedd425bc1c5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/8dd898e970a4cc540e0394ace9c9cedd425bc1c5.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/9cf15b18b743939cbe01342ed5461bc7af6c4d36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/9cf15b18b743939cbe01342ed5461bc7af6c4d36.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/be611e71c615c27471d766612bfb7e8b05d743c7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/be611e71c615c27471d766612bfb7e8b05d743c7.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/c0b5a705e7e94af3f71ef579bb01b45c2a80ca6b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/c0b5a705e7e94af3f71ef579bb01b45c2a80ca6b.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/c313c0ceef6eb3116547426b41bdf278df2cc0c6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/c313c0ceef6eb3116547426b41bdf278df2cc0c6.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/c7ef83055cf836a48ed9dd26b3a8d55103645022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/c7ef83055cf836a48ed9dd26b3a8d55103645022.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/ccc438a754b6d9324c9c1ea62662969c6114bfdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/ccc438a754b6d9324c9c1ea62662969c6114bfdf.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/cfaf56ab8ec6588aa6ee9297b4f93638640d1048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/cfaf56ab8ec6588aa6ee9297b4f93638640d1048.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/e58d592c018472a09777c3fd5440f556bd176dd5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/e58d592c018472a09777c3fd5440f556bd176dd5.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/e7e550d9cbff786153f7f13f664361e41efee57c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/e7e550d9cbff786153f7f13f664361e41efee57c.png -------------------------------------------------------------------------------- /assets/tab_stats_generated_images/e90a8350bb77972b086599b65efc8fcd02036a11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/assets/tab_stats_generated_images/e90a8350bb77972b086599b65efc8fcd02036a11.png -------------------------------------------------------------------------------- /ci/headers_add.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_update_utc": "2025-04-20 21:27:15", 3 | "headers": [ 4 | { 5 | "name": "Cache-Control", 6 | "value": "no-store, max-age=0" 7 | }, 8 | { 9 | "name": "Clear-Site-Data", 10 | "value": "\"cache\",\"cookies\",\"storage\"" 11 | }, 12 | { 13 | "name": "Content-Security-Policy", 14 | "value": "default-src 'self'; form-action 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests" 15 | }, 16 | { 17 | "name": "Cross-Origin-Embedder-Policy", 18 | "value": "require-corp" 19 | }, 20 | { 21 | "name": "Cross-Origin-Opener-Policy", 22 | "value": "same-origin" 23 | }, 24 | { 25 | "name": "Cross-Origin-Resource-Policy", 26 | "value": "same-origin" 27 | }, 28 | { 29 | "name": "Permissions-Policy", 30 | "value": "accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), unload=()" 31 | }, 32 | { 33 | "name": "Referrer-Policy", 34 | "value": "no-referrer" 35 | }, 36 | { 37 | "name": "Strict-Transport-Security", 38 | "value": "max-age=31536000; includeSubDomains" 39 | }, 40 | { 41 | "name": "X-Content-Type-Options", 42 | "value": "nosniff" 43 | }, 44 | { 45 | "name": "X-Frame-Options", 46 | "value": "deny" 47 | }, 48 | { 49 | "name": "X-Permitted-Cross-Domain-Policies", 50 | "value": "none" 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /ci/headers_generate_json_files.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility script to generate the reference both JSON files providing the collection of HTTP response security headers to add and remove. 3 | 4 | Use as a source, the information defined in the file "tab_bestpractices.md" in the following tables: 5 | - Section "Configuration proposal" for the headers to add: 6 | Identification via the markers "" and "" 7 | - Section "Prevent information disclosure via HTTP headers" for the headers to remove: 8 | Identification via the markers "" and "" 9 | 10 | Try to not use any external dependency to stay the more portable possible. 11 | 12 | A last update date attribute was added in both JSON files to allow user to quickly identify when files were changed 13 | and trigger update on their side. In addition, it helps the CI workflow to be easier by causing the JSON files to be different 14 | at each run of the script (made commit easier to manage). 15 | """ 16 | 17 | import json 18 | import re 19 | import operator 20 | from datetime import datetime 21 | from datetime import timezone 22 | 23 | # Constants 24 | SOURCE_MD_FILE = "../tab_bestpractices.md" 25 | HEADERS_TO_ADD_JSON_FILE = "headers_add.json" 26 | HEADERS_TO_REMOVE_JSON_FILE = "headers_remove.json" 27 | HEADERS_TO_ADD_TABLE_EXTRACTION_MARKERS = ("", "") 28 | HEADERS_TO_REMOVE_TABLE_EXTRACTION_MARKERS = ("", "") 29 | EXECUTION_DATETIME_UTC = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") 30 | LAST_UPDATE_ATTRIBUTE_NAME = "last_update_utc" 31 | JSON_IDENT = 2 32 | 33 | 34 | def extract_table_md(headers_category="add"): 35 | markers = HEADERS_TO_ADD_TABLE_EXTRACTION_MARKERS 36 | if headers_category != "add": 37 | markers = HEADERS_TO_REMOVE_TABLE_EXTRACTION_MARKERS 38 | with open(SOURCE_MD_FILE, mode="r", encoding="utf-8") as md_file: 39 | md_content = md_file.read() 40 | start = md_content.find(markers[0]) 41 | end = md_content.find(markers[1]) 42 | if start == -1 or end == -1: 43 | raise Exception(f"No table was identified with markers {markers} for headers category '{headers_category}'!") 44 | md_table = md_content[start + len(markers[0]):end] 45 | md_table = md_table.strip("\t\n\r ") 46 | return md_table 47 | 48 | 49 | def generate_headers_to_add_json(md_table): 50 | lines = md_table.split("\n") 51 | headers = [] 52 | for i in range(2, len(lines)): 53 | header_info = lines[i] 54 | header_parts = header_info.split("|") 55 | header_name = header_parts[1].strip(" `*\t\n\r") 56 | header_value = header_parts[2].strip(" `*\t\n\r") 57 | headers.append({"name": header_name, "value": header_value}) 58 | headers.sort(key=operator.itemgetter("name")) 59 | data = {LAST_UPDATE_ATTRIBUTE_NAME: EXECUTION_DATETIME_UTC, "headers": headers} 60 | return json.dumps(data, indent=JSON_IDENT) 61 | 62 | 63 | def generate_headers_to_remove_json(md_table): 64 | lines = md_table.split("\n") 65 | headers = [] 66 | for i in range(2, len(lines)): 67 | header_info = lines[i] 68 | header_parts = header_info.split("|") 69 | header_name = header_parts[1].strip(" `*\t\n\r") 70 | header_name = re.findall(r'\[(.*)\]', header_name)[0] 71 | headers.append(header_name) 72 | headers.sort() 73 | data = {LAST_UPDATE_ATTRIBUTE_NAME: EXECUTION_DATETIME_UTC, "headers": headers} 74 | return json.dumps(data, indent=JSON_IDENT) 75 | 76 | 77 | if __name__ == "__main__": 78 | for headers_category in ["add", "remove"]: 79 | print(f"[+] Generate content for headers category: {headers_category}") 80 | md_table = extract_table_md(headers_category) 81 | if headers_category == "add": 82 | json_content = generate_headers_to_add_json(md_table) 83 | json_ref_file = HEADERS_TO_ADD_JSON_FILE 84 | else: 85 | json_content = generate_headers_to_remove_json(md_table) 86 | json_ref_file = HEADERS_TO_REMOVE_JSON_FILE 87 | with open(json_ref_file, mode="w", encoding="utf-8") as json_file: 88 | json_file.write(json_content) 89 | -------------------------------------------------------------------------------- /ci/headers_remove.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_update_utc": "2025-04-20 21:27:15", 3 | "headers": [ 4 | "$wsep", 5 | "Host-Header", 6 | "K-Proxy-Request", 7 | "Liferay-Portal", 8 | "OracleCommerceCloud-Version", 9 | "Pega-Host", 10 | "Powered-By", 11 | "Product", 12 | "Server", 13 | "SourceMap", 14 | "X-AspNet-Version", 15 | "X-AspNetMvc-Version", 16 | "X-Atmosphere-error", 17 | "X-Atmosphere-first-request", 18 | "X-Atmosphere-tracking-id", 19 | "X-B3-ParentSpanId", 20 | "X-B3-Sampled", 21 | "X-B3-SpanId", 22 | "X-B3-TraceId", 23 | "X-BEServer", 24 | "X-Backside-Transport", 25 | "X-CF-Powered-By", 26 | "X-CMS", 27 | "X-CalculatedBETarget", 28 | "X-Cocoon-Version", 29 | "X-Content-Encoded-By", 30 | "X-DiagInfo", 31 | "X-Envoy-Attempt-Count", 32 | "X-Envoy-External-Address", 33 | "X-Envoy-Internal", 34 | "X-Envoy-Original-Dst-Host", 35 | "X-Envoy-Upstream-Service-Time", 36 | "X-FEServer", 37 | "X-Framework", 38 | "X-Generated-By", 39 | "X-Generator", 40 | "X-Jitsi-Release", 41 | "X-Joomla-Version", 42 | "X-Kubernetes-PF-FlowSchema-UI", 43 | "X-Kubernetes-PF-PriorityLevel-UID", 44 | "X-LiteSpeed-Cache", 45 | "X-LiteSpeed-Purge", 46 | "X-LiteSpeed-Tag", 47 | "X-LiteSpeed-Vary", 48 | "X-Litespeed-Cache-Control", 49 | "X-Mod-Pagespeed", 50 | "X-Nextjs-Cache", 51 | "X-Nextjs-Matched-Path", 52 | "X-Nextjs-Page", 53 | "X-Nextjs-Redirect", 54 | "X-OWA-Version", 55 | "X-Old-Content-Length", 56 | "X-OneAgent-JS-Injection", 57 | "X-Page-Speed", 58 | "X-Php-Version", 59 | "X-Powered-By", 60 | "X-Powered-By-Plesk", 61 | "X-Powered-CMS", 62 | "X-Redirect-By", 63 | "X-Server-Powered-By", 64 | "X-SourceFiles", 65 | "X-SourceMap", 66 | "X-Turbo-Charged-By", 67 | "X-Umbraco-Version", 68 | "X-Varnish-Backend", 69 | "X-Varnish-Server", 70 | "X-dtAgentId", 71 | "X-dtHealthCheck", 72 | "X-dtInjectedServlet", 73 | "X-ruxit-JS-Agent" 74 | ] 75 | } -------------------------------------------------------------------------------- /ci/monitoring_oshp_site_references.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility script to verify that every site mentioned in the tab named "Case Studies" have a mention to OSHP. 3 | 4 | The goal is to allow detection of site not mentioning the OSHP anymore and then update the "Case Studies" content. 5 | 6 | Dependencies: 7 | pip install requests 8 | """ 9 | import re 10 | import requests 11 | import time 12 | import os 13 | import sys 14 | 15 | OSHP_MARKER_STRINGS = ["owasp secure headers project", "https://owasp.org/www-project-secure-headers", "https://www.owasp.org/index.php/security_headers", "https://owasp.org/index.php/owasp_secure_headers_project"] 16 | DEFAULT_ENCODING = "utf-8" 17 | WAIT_DELAY_SECONDS = 4 18 | MAX_RETRY = 4 19 | TIMEOUT_SECONDS = 20 20 | IGNORED_HTTP_RESPONSE_CODES = [401] 21 | SOURCE_MD_FILE = "../tab_casestudies.md" 22 | USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36" 23 | 24 | 25 | def verify_mention(site_url): 26 | oshp_is_mentioned = "NO" 27 | # Assume by default that site refer to static non SPA page 28 | response = requests.get(url=site_url, headers={"User-Agent": USER_AGENT}, timeout=TIMEOUT_SECONDS, allow_redirects=True) 29 | if response.status_code in IGNORED_HTTP_RESPONSE_CODES: 30 | oshp_is_mentioned = "YES" 31 | else: 32 | content = response.text 33 | content_lower = content.lower() 34 | for marker in OSHP_MARKER_STRINGS: 35 | if marker.lower() in content_lower: 36 | oshp_is_mentioned = "YES" 37 | break 38 | # If mention is not detected then try to check if it's an SPA 39 | if oshp_is_mentioned == "NO": 40 | expr = r'(app|index|main)(\.|-)[a-zA-Z0-9]+\.js' 41 | bundles = re.findall(expr, content) 42 | if len(bundles) > 0 or "React" in content: 43 | oshp_is_mentioned = "SPA" 44 | # If mention is not detected then try to check if the site is protected by CLOUDFLARE 45 | if oshp_is_mentioned == "NO" and ("CF-RAY" in response.headers or "cf-mitigated" in response.headers): 46 | oshp_is_mentioned = "CLOUDFLARE" 47 | return oshp_is_mentioned 48 | 49 | 50 | def extract_site_urls(): 51 | expr = r'\*\s+\[[a-zA-Z0-9\s_\-\.]+\]\((.*?)\)' 52 | with open(SOURCE_MD_FILE, mode="r", encoding=DEFAULT_ENCODING) as f: 53 | content = f.read() 54 | return re.findall(expr, content) 55 | 56 | 57 | def print_github_error(site_url, oshp_is_mentioned): 58 | print(f"::error file={os.path.basename(__file__)},title=MissingOSHPReference::For site '{site_url}' reference is '{oshp_is_mentioned}'.") 59 | 60 | 61 | if __name__ == "__main__": 62 | print("[+] Extract site urls...") 63 | site_urls = extract_site_urls() 64 | print(f"{len(site_urls)} urls founds.") 65 | print("[+] Verify mention to OSHP on each site...") 66 | valid_mentions = ["YES", "SPA", "CLOUDFLARE"] 67 | error_count = 0 68 | for site_url in site_urls: 69 | if site_url.strip().startswith("http"): 70 | oshp_is_mentioned = "NO" 71 | for _ in range(0, MAX_RETRY): 72 | try: 73 | oshp_is_mentioned = verify_mention(site_url) 74 | if oshp_is_mentioned in valid_mentions: 75 | break 76 | else: 77 | time.sleep(WAIT_DELAY_SECONDS) 78 | except requests.exceptions.Timeout: 79 | oshp_is_mentioned = "IO_ERROR" 80 | time.sleep(WAIT_DELAY_SECONDS) 81 | pass 82 | if oshp_is_mentioned not in valid_mentions: 83 | print_github_error(site_url, oshp_is_mentioned) 84 | error_count += 1 85 | if error_count == 0: 86 | print("All references are OK.") 87 | sys.exit(error_count) 88 | -------------------------------------------------------------------------------- /ci/monitoring_technical_references_generate_dashboard.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility script to generate a markdown file with the update health state of all GitHub repositories mentioned in the tab named "Technical". 3 | 4 | The goal is to allow detection of old and dead projects. 5 | 6 | Sources: 7 | https://thispointer.com/python-get-difference-between-two-dates-in-months/ 8 | https://docs.github.com/en/rest/repos/repos#get-a-repository 9 | 10 | Dependencies: 11 | pip install requests 12 | """ 13 | import re 14 | import sys 15 | import requests 16 | from datetime import datetime 17 | from datetime import timezone 18 | 19 | # Constants 20 | REQ_SESSION = requests.session() 21 | TIMEOUT_SECONDS = 240 22 | EXECUTION_DATETIME_UTC = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") 23 | DEFAULT_ENCODING = "utf-8" 24 | SOURCE_MD_FILE = "../tab_technical.md" 25 | DASHBOARD_MD_FILE = "../monitoring_technical_references_dashboard.md" 26 | DASHBOARD_MD_FILE_TEMPLATE = f""" 27 | # Technical References Dashboard 28 | 29 | > 📅 Last verification (UTC): {EXECUTION_DATETIME_UTC} 30 | 31 | ## GitHub repositories health status 32 | 33 | Provides a quick visual status on the health status (whether they are updated or not) of the referenced GitHub projects in the tab named **Technical**. 34 | 35 | Project reaching the :red_circle: status **are removed**. 36 | 37 | :speech_balloon: **Status icon legends:** 38 | 39 | * :green_circle: Updated within the last **12 months** from the date of verification. 40 | * :orange_circle: Updated within the last **24 months** from the date of verification. 41 | * :red_circle: Not updated for **more than 24 months** from the date of verification. 42 | 43 | %s 44 | 45 | """ 46 | 47 | 48 | def determine_health_state(repo_updated_datetime): 49 | # Format: 2022-09-14T13:40:24Z 50 | # Keep only the date part 51 | start_date = datetime.strptime(repo_updated_datetime.split("T")[0], "%Y-%m-%d") 52 | end_date = datetime.now(timezone.utc) 53 | diff_months = ((end_date.year - start_date.year) * 12) + (end_date.month - start_date.month) 54 | if diff_months <= 12: 55 | health_state_icon = ":green_circle:" 56 | elif diff_months <= 24: 57 | health_state_icon = ":orange_circle:" 58 | else: 59 | health_state_icon = ":red_circle:" 60 | return (diff_months, health_state_icon) 61 | 62 | 63 | def extract_updated_datetime(github_repo_url): 64 | # Extract the repo owner and project name from the repo url 65 | parts = github_repo_url.strip(' \r\n\t').split("/") 66 | repo_owner = parts[3] 67 | project_name = parts[4] 68 | # Get the info of the repo using the GitHub API 69 | api_url = f"https://api.github.com/repos/{repo_owner}/{project_name}" 70 | response = REQ_SESSION.get(api_url, timeout=TIMEOUT_SECONDS) 71 | repo_info = response.json() 72 | # Return the updated date attribute 73 | # Format: 2022-09-14T13:40:24Z 74 | return repo_info["pushed_at"] 75 | 76 | 77 | def extract_github_repositories_url(): 78 | github_repositories_url_collection = [] 79 | expr = r'https://github\.com/[a-zA-Z0-9.\-_/]+' 80 | with open(SOURCE_MD_FILE, mode="r", encoding=DEFAULT_ENCODING) as f: 81 | content = f.read() 82 | repos = re.findall(expr, content) 83 | for repo in repos: 84 | github_repositories_url_collection.append(repo.strip(' <>')) 85 | github_repositories_url_collection.sort() 86 | return github_repositories_url_collection 87 | 88 | 89 | def generate_md_table(github_repositories_url_collection): 90 | table_md = "| Last update | Status | Repository |\n| --- | --- | --- |\n" 91 | lines = [] 92 | for repo in github_repositories_url_collection: 93 | updated_datetime = extract_updated_datetime(repo) 94 | health_state = determine_health_state(updated_datetime) 95 | repo_name = repo.replace("https://github.com/", "") 96 | lines.append(f"| `{updated_datetime}` ({health_state[0]} months ago) | {health_state[1]} | [{repo_name}]({repo}) |") 97 | lines.sort() 98 | table_md += "\n".join(lines) 99 | return table_md 100 | 101 | 102 | if __name__ == "__main__": 103 | if len(sys.argv) == 2: 104 | print("[i] GitHub access token provided.") 105 | github_access_token = sys.argv[1] 106 | REQ_SESSION.headers.update({"Authorization": f"Bearer {github_access_token}"}) 107 | else: 108 | print("[i] No GitHub access token provided.") 109 | print("[+] Extract GitHub repositories url...") 110 | repos = extract_github_repositories_url() 111 | print(f"{len(repos)} repos.") 112 | print("[+] Generate MD table...") 113 | table_md = generate_md_table(repos) 114 | print("[+] Update dashboard MD file...") 115 | dashboard_content = DASHBOARD_MD_FILE_TEMPLATE % table_md 116 | with open(DASHBOARD_MD_FILE, mode="w", encoding=DEFAULT_ENCODING) as f: 117 | f.write(dashboard_content) 118 | print(f"[V] File '{DASHBOARD_MD_FILE}' updated.") 119 | -------------------------------------------------------------------------------- /ci/tab_stats_generate_md_file.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | """ 3 | Script using the gathered data from the OSHP project "oshp-stats" to generate/update the 4 | markdown file "tab_statistics.md" with mermaid pie charts with differents statistics about HTTP security headers usage. 5 | 6 | Source: 7 | https://mermaid-js.github.io/mermaid/#/pie 8 | https://github.com/oshp/oshp-stats/ 9 | """ 10 | import sqlite3 11 | import re 12 | import json 13 | import hashlib 14 | from collections import Counter 15 | from datetime import datetime 16 | from pathlib import Path 17 | 18 | # Constants 19 | DEBUG = True 20 | DATA_DB_FILE = "/tmp/data.db" 21 | OSHP_SECURITY_HEADERS_FILE_lOCATION = "headers_add.json" 22 | OSHP_SECURITY_HEADERS_EXTRA_FILE_LOCATION = "/tmp/oshp_headers_extra_to_include.txt" 23 | MD_FILE = "../tab_statistics.md" 24 | IMAGE_FOLDER_LOCATION = "../assets/tab_stats_generated_images" 25 | TAB_MD_TEMPLATE = """--- 26 | title: statistics 27 | displaytext: Statistics 28 | layout: null 29 | tab: true 30 | order: 7 31 | tags: headers 32 | --- 33 | 34 | 35 | 36 | 37 | 38 | # Statistic about HTTP security response headers usage 39 | 40 | """ 41 | SECTION_TEMPLATE = f""" 42 | ## %s 43 | 44 | %s 45 | 46 | ![%s]({IMAGE_FOLDER_LOCATION.replace('../', '')}/%s) 47 | """ 48 | SECTION_TEMPLATE_NO_MERMAID_CODE = """ 49 | ## %s 50 | 51 | %s 52 | """ 53 | 54 | # Utility functions 55 | 56 | 57 | def trace(msg): 58 | if DEBUG: 59 | print(f"[DEBUG] {msg}") 60 | 61 | 62 | def prepare_generation_of_image_from_mermaid(mermaid_code, filename): 63 | trace(f"Call prepare_generation_of_image_from_mermaid() => {filename}") 64 | with open(f"{IMAGE_FOLDER_LOCATION}/{filename}.mmd", "w", encoding="utf-8") as f: 65 | f.write(mermaid_code + "\n") 66 | trace("Call end.") 67 | 68 | 69 | def load_oshp_headers(): 70 | trace("Call load_oshp_headers()") 71 | header_names = [] 72 | trace(f"Call load_oshp_headers() :: Load and parse file {OSHP_SECURITY_HEADERS_FILE_lOCATION}") 73 | with open(OSHP_SECURITY_HEADERS_FILE_lOCATION, mode="r", encoding="utf-8") as f: 74 | data = json.load(f) 75 | http_headers = data["headers"] 76 | for http_header in http_headers: 77 | header_names.append(http_header["name"].lower()) 78 | trace(f"Call load_oshp_headers() :: Load file {OSHP_SECURITY_HEADERS_EXTRA_FILE_LOCATION}") 79 | with open(OSHP_SECURITY_HEADERS_EXTRA_FILE_LOCATION, mode="r", encoding="utf-8") as f: 80 | http_headers = f.read() 81 | trace(f"Call load_oshp_headers() :: Parse file {OSHP_SECURITY_HEADERS_EXTRA_FILE_LOCATION}") 82 | for http_header in http_headers .split("\n"): 83 | header_names.append(http_header.lower().strip(" \n\r\t")) 84 | header_names = list(dict.fromkeys(header_names)) 85 | header_names.sort() 86 | trace("Call end.") 87 | return header_names 88 | 89 | 90 | def execute_query_against_data_db(sql_query): 91 | trace(f"Call execute_query_against_data_db() => {sql_query}") 92 | with sqlite3.connect(DATA_DB_FILE) as connection: 93 | curs = connection.cursor() 94 | curs.execute(sql_query) 95 | records = curs.fetchall() 96 | trace("Call end.") 97 | return records 98 | 99 | 100 | def add_stats_section(title, description, chart_mermaid_code): 101 | trace(f"Call add_stats_section() => '{title}'") 102 | with open(MD_FILE, mode="a", encoding="utf-8") as f: 103 | if chart_mermaid_code is not None and len(chart_mermaid_code.strip()) > 0: 104 | base_image_filename = hashlib.sha1(title.encode("utf8")).hexdigest() 105 | prepare_generation_of_image_from_mermaid(chart_mermaid_code, base_image_filename) 106 | md_code = SECTION_TEMPLATE % (title, description, base_image_filename, f"{base_image_filename}.png") 107 | else: 108 | md_code = SECTION_TEMPLATE_NO_MERMAID_CODE % (title, description) 109 | f.write(f"{md_code}\n") 110 | trace("Call end.") 111 | 112 | 113 | def init_stats_file(): 114 | trace("Call init_stats_file()") 115 | with open(MD_FILE, mode="w", encoding="utf-8") as f: 116 | cdate = datetime.now().strftime("%m/%d/%Y at %H:%M:%S") 117 | f.write(TAB_MD_TEMPLATE) 118 | f.write("\n\n") 119 | f.write(f"📅 Last update: {cdate} - Domains analyzed count: {get_domains_count()}.\n") 120 | trace("Call end.") 121 | 122 | 123 | def get_domains_count(): 124 | return len(execute_query_against_data_db("select distinct domain from stats")) 125 | 126 | 127 | def get_pie_chart_code(title, dataset_tuples): 128 | # code = f"pie title {title}\n" 129 | code = f"pie\n" 130 | for dataset_tuple in dataset_tuples: 131 | # Note: Mermaid use integer value when rendering 132 | code += f"\t\"{dataset_tuple[0]}\" : {round(dataset_tuple[1], 2)}\n" 133 | return code 134 | 135 | 136 | def csp_contain_unsafe_expression(csp_policy): 137 | contain_unsafe_expression = False 138 | # Determine if a CSP policy contains (default-src|script-src|script-src-elem|script-src-attr|style-src) directives using (unsafe-inline|unsafe-hashes|unsafe-eval) expressions 139 | # Based on "https://report-uri.com/home/generate" generator allowed instructions for CSP directives 140 | exp_all_unsafe_expressions = r'(unsafe-inline|unsafe-hashes|unsafe-eval)' 141 | exp_style_unsafe_expressions = r'(unsafe-inline|unsafe-hashes)' 142 | exp_directive_name_allowing_all_unsafe_expressions = r'(default-src|script-src|script-src-elem|script-src-attr)' 143 | directives = csp_policy.split(";") 144 | for directive in directives: 145 | if len(re.findall(exp_directive_name_allowing_all_unsafe_expressions, directive)) > 0 and len(re.findall(exp_all_unsafe_expressions, directive)) > 0: 146 | contain_unsafe_expression = True 147 | break 148 | elif directive.strip().startswith("style-src") and len(re.findall(exp_style_unsafe_expressions, directive)) > 0: 149 | contain_unsafe_expression = True 150 | break 151 | return contain_unsafe_expression 152 | 153 | 154 | # Functions in charge of generate stats sections 155 | 156 | 157 | def compute_header_global_usage(header_name): 158 | title = f"Global usage of header '{header_name}'" 159 | description = f"Provide the distribution of usage of the header '{header_name}' across all domains analyzed." 160 | # Prevent the case in which a domain specify X times the same headers... 161 | query = f"select distinct domain from stats where lower(http_header_name) = '{header_name}'" 162 | count_of_domains_using_the_header = len( 163 | execute_query_against_data_db(query)) 164 | domains_count = get_domains_count() 165 | percentage_of_domains_using_the_header = ( 166 | count_of_domains_using_the_header * 100) / domains_count 167 | dataset_tuples = [("Using it", percentage_of_domains_using_the_header), 168 | ("Not using it", (100-percentage_of_domains_using_the_header))] 169 | pie_chart_code = get_pie_chart_code(title, dataset_tuples) 170 | add_stats_section(title, description, pie_chart_code) 171 | 172 | 173 | def compute_insecure_framing_configuration_global_usage(): 174 | header_name = "x-frame-options" 175 | title = f"Global usage of insecure framing configuration via the header '{header_name}'" 176 | description = f"Provide the distribution of usage of the header '{header_name}' across all domains analyzed with a insecure framing configuration: value different from `DENY` or `SAMEORIGIN` including unsupported values." 177 | query = f"select count(*) from stats where lower(http_header_name) = '{header_name}' and lower(http_header_value) not in ('deny','sameorigin')" 178 | count_of_domains = execute_query_against_data_db(query)[0][0] 179 | domains_count = get_domains_count() 180 | percentage_of_domains = (count_of_domains * 100) / domains_count 181 | dataset_tuples = [("Insecure conf", percentage_of_domains), 182 | ("Secure conf", (100-percentage_of_domains))] 183 | pie_chart_code = get_pie_chart_code(title, dataset_tuples) 184 | add_stats_section(title, description, pie_chart_code) 185 | 186 | 187 | def compute_hsts_preload_global_usage(): 188 | header_name = "strict-transport-security" 189 | title = "Global usage of the Strict Transport Security 'preload' feature" 190 | description = f"Provide the distribution of usage of the '[preload](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#preloading_strict_transport_security)' feature for the header '{header_name}' across all domains analyzed." 191 | query = f"select count(*) from stats where lower(http_header_name) = '{header_name}' and lower(http_header_value) not like '%preload%'" 192 | count_of_domains = execute_query_against_data_db(query)[0][0] 193 | domains_count = get_domains_count() 194 | percentage_of_domains = (count_of_domains * 100) / domains_count 195 | dataset_tuples = [("Using it", percentage_of_domains), 196 | ("Not using it", (100-percentage_of_domains))] 197 | pie_chart_code = get_pie_chart_code(title, dataset_tuples) 198 | add_stats_section(title, description, pie_chart_code) 199 | 200 | 201 | def compute_secure_headers_global_usage(): 202 | title = "Global usage of secure headers" 203 | description = f"Provide the distribution of usage of secure headers across all domains analyzed." 204 | query = "select count(domain) from stats where http_header_name is NULL" 205 | count_of_domains = execute_query_against_data_db(query)[0][0] 206 | domains_count = get_domains_count() 207 | percentage_of_domains = (count_of_domains * 100) / domains_count 208 | dataset_tuples = [("Not using them", percentage_of_domains), 209 | ("Using them", (100-percentage_of_domains))] 210 | pie_chart_code = get_pie_chart_code(title, dataset_tuples) 211 | add_stats_section(title, description, pie_chart_code) 212 | 213 | 214 | def compute_insecure_referrer_configuration_global_usage(): 215 | header_name = "referrer-policy" 216 | title = f"Global usage of insecure referrer configuration via the header '{header_name}'" 217 | description = f"Provide the distribution of usage of the header '{header_name}' across all domains analyzed with a insecure referrer configuration: value set to `unsafe-url` or `no-referrer-when-downgrade`.\n\n`no-referrer-when-downgrade` was included because it send origin, path, and querystring when the protocol security level stays the same (HTTPS is very often in place)." 218 | query = f"select count(*) from stats where lower(http_header_name) = '{header_name}' and lower(http_header_value) in ('unsafe-url','no-referrer-when-downgrade')" 219 | count_of_domains = execute_query_against_data_db(query)[0][0] 220 | domains_count = get_domains_count() 221 | percentage_of_domains = (count_of_domains * 100) / domains_count 222 | dataset_tuples = [("Insecure conf", percentage_of_domains), 223 | ("Secure conf", (100-percentage_of_domains))] 224 | pie_chart_code = get_pie_chart_code(title, dataset_tuples) 225 | add_stats_section(title, description, pie_chart_code) 226 | 227 | 228 | def compute_hsts_average_maxage_global_usage(): 229 | title = "Global common 'max-age' values of the Strict Transport Security header" 230 | query = "select lower(http_header_value) from stats where lower(http_header_name) = 'strict-transport-security' and lower(http_header_value) like '%max-age=%'" 231 | header_values = execute_query_against_data_db(query) 232 | expr = r'max-age\s*=\s*(\-?"?\d+"?)' 233 | # Gather values for max-age attribute 234 | values = [] 235 | for header_value in header_values: 236 | v = header_value[0].strip('\n\r\t').replace('"', '') 237 | matches = re.findall(expr, v) 238 | if len(matches) > 0: 239 | values.append(int(matches[0])) 240 | # Find the most popular one 241 | occurences = Counter(values) 242 | maxage_most_popular_value = 0 243 | current_max_occurence_count = 0 244 | for maxage_value, occurence_count in occurences.items(): 245 | if occurence_count > current_max_occurence_count: 246 | current_max_occurence_count = occurence_count 247 | maxage_most_popular_value = maxage_value 248 | description = f"* Most common value used is {maxage_most_popular_value} seconds ({round(maxage_most_popular_value/60)} minutes) across all domains analyzed." 249 | description += f"\n* Maximum value used is {max(values)} seconds ({round(max(values)/60)} minutes) across all domains analyzed." 250 | description += f"\n* Minimum value used is {min(values)} seconds ({round(min(values)/60)} minutes) across all domains analyzed." 251 | add_stats_section(title, description, None) 252 | 253 | 254 | def compute_csp_using_directives_with_unsafe_expressions_configuration_global_usage(): 255 | header_name = "content-security-policy" 256 | title = f"Global usage of content security policy with directives allowing unsafe expressions" 257 | description = f"Provide the distribution of content security policy allowing unsafe expressions across all domains analyzed.\n\nDetermine if a CSP policy contains `(default-src|script-src|script-src-elem|script-src-attr|style-src)` directives using `(unsafe-inline|unsafe-hashes|unsafe-eval)` expressions.\n\nBased on [Report-URI CSP](https://report-uri.com/home/generate) generator allowed instructions for CSP directives." 258 | query = f"select lower(http_header_value) from stats where lower(http_header_name) like '{header_name}%' and lower(http_header_value) like '%unsafe%'" 259 | header_values = execute_query_against_data_db(query) 260 | count_of_domains = 0 261 | for header_value in header_values: 262 | if csp_contain_unsafe_expression(header_value[0]): 263 | count_of_domains += 1 264 | domains_count = get_domains_count() 265 | percentage_of_domains = (count_of_domains * 100) / domains_count 266 | dataset_tuples = [("Using unsafe", percentage_of_domains), 267 | ("Not using unsafe", (100-percentage_of_domains))] 268 | pie_chart_code = get_pie_chart_code(title, dataset_tuples) 269 | add_stats_section(title, description, pie_chart_code) 270 | 271 | 272 | if __name__ == "__main__": 273 | trace("Clear PNG files") 274 | for path in Path(IMAGE_FOLDER_LOCATION).glob("*.png"): 275 | path.unlink() 276 | trace("Clear MMD files") 277 | for path in Path(IMAGE_FOLDER_LOCATION).glob("*.mmd"): 278 | path.unlink() 279 | oshp_headers = load_oshp_headers() 280 | init_stats_file() 281 | compute_secure_headers_global_usage() 282 | for header_name in oshp_headers: 283 | compute_header_global_usage(header_name) 284 | compute_insecure_framing_configuration_global_usage() 285 | compute_insecure_referrer_configuration_global_usage() 286 | compute_hsts_preload_global_usage() 287 | compute_hsts_average_maxage_global_usage() 288 | compute_csp_using_directives_with_unsafe_expressions_configuration_global_usage() 289 | -------------------------------------------------------------------------------- /ci/tab_stats_generate_png_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################################################################### 3 | # Generate the PNG image files from corresponding MMD (mermaid) files. 4 | # 5 | # Dependencies: 6 | # https://github.com/mermaid-js/mermaid-cli 7 | # 8 | # Reference: 9 | # https://github.com/mermaid-js/mermaid-cli/blob/master/.github/workflows/test.yml#L24 10 | # https://mermaid.js.org/config/theming.html 11 | ######################################################################### 12 | # Constants 13 | IMAGE_FOLDER_LOCATION="../assets/tab_stats_generated_images" 14 | # Generate images 15 | # We use aa-exec since Ubuntu 24.04's AppArmor profile blocks the use of puppeteer otherwise 16 | # See https://github.com/puppeteer/puppeteer/issues/12818 17 | cd $IMAGE_FOLDER_LOCATION 18 | for mmd_file in *.mmd 19 | do 20 | png_file="${mmd_file%%.*}.png" 21 | aa-exec --profile=chrome npx -p @mermaid-js/mermaid-cli mmdc --quiet --input $mmd_file --output $png_file --outputFormat png --configFile ../../ci/tab_stats_mermaid_config.json 22 | done 23 | # Only let PNG files 24 | rm *.mmd 25 | cd - 26 | -------------------------------------------------------------------------------- /ci/tab_stats_manage_generation.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ######################################################################### 3 | # This script manage the generation/update of the tab represented by the 4 | # file "tab_statistics.md". 5 | ######################################################################### 6 | OSHP_SECURITY_HEADERS_EXTRA_FILE_LOCATION="https://raw.githubusercontent.com/oshp/oshp-stats/refs/heads/main/scripts/oshp_headers_extra_to_include.txt" 7 | OSHP_SECURITY_HEADERS_EXTRA_FILE="/tmp/oshp_headers_extra_to_include.txt" 8 | DATA_DB_FILE_LOCATION="https://github.com/oshp/oshp-stats/raw/refs/heads/main/data/data.db" 9 | DATA_DB_FILE="/tmp/data.db" 10 | IMAGE_FOLDER_LOCATION="../assets/tab_stats_generated_images" 11 | echo "[+] Download the database of headers analysis anc validate the database file..." 12 | wget -q -O $DATA_DB_FILE $DATA_DB_FILE_LOCATION 13 | wget -q -O $OSHP_SECURITY_HEADERS_EXTRA_FILE $OSHP_SECURITY_HEADERS_EXTRA_FILE_LOCATION 14 | file $DATA_DB_FILE 15 | sqlite3 $DATA_DB_FILE ".tables" 16 | file $OSHP_SECURITY_HEADERS_EXTRA_FILE 17 | wc -l $OSHP_SECURITY_HEADERS_EXTRA_FILE 18 | echo "[+] Set correct access rights for the scripts as well as UNIX CRLF settings..." 19 | dos2unix *.sh 20 | chmod +x tab_stats_generate_* 21 | echo "[+] Generate the MD file of the TAB and all the MMD files for every pie chart image..." 22 | python tab_stats_generate_md_file.py 23 | echo "[+] Generate the PNG image corresponding to each MMD file..." 24 | bash tab_stats_generate_png_files.sh 25 | echo "[+] Cleanup" 26 | rm $DATA_DB_FILE 27 | rm $OSHP_SECURITY_HEADERS_EXTRA_FILE 28 | echo "[+] Check correct generation of the images..." 29 | img_count=$(find $IMAGE_FOLDER_LOCATION -name "*.png" | wc -l) 30 | if [ $img_count -eq 0 ] 31 | then 32 | echo "[!] No image file was generated!" 33 | exit 1 34 | else 35 | echo "[V] $img_count image files were generated!" 36 | exit 0 37 | fi 38 | -------------------------------------------------------------------------------- /ci/tab_stats_mermaid_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "theme": "forest", 3 | "themeVariables": { 4 | "primaryColor": "#233E81", 5 | "primaryTextColor": "#000000", 6 | "secondaryColor": "#810E81", 7 | "secondaryTextColor": "#000000", 8 | "tertiaryColor": "#98AFC7", 9 | "tertiaryTextColor": "#000000" 10 | } 11 | } -------------------------------------------------------------------------------- /ci/validate_md_links.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import json 3 | import re 4 | import os 5 | import os.path 6 | import argparse 7 | import pathlib 8 | import sys 9 | import requests 10 | from requests import Session 11 | from requests.adapters import HTTPAdapter 12 | from urllib3.util import Retry 13 | """ 14 | Script used to validate all HTTP/HTTPS links contained into a collection of 15 | markdown (called MD) files. 16 | 17 | The goal is to remplace, by a standalone portable script, the following github action 18 | that is now deprecated: 19 | 20 | https://github.com/gaurav-nelson/github-action-markdown-link-check 21 | 22 | This script load the configuration from the file used to configure the tool "markdown-link-check" 23 | that was used by the github actions: 24 | 25 | https://github.com/tcort/markdown-link-check 26 | 27 | Only the following property are supported: 28 | - timeout 29 | - retryCount 30 | - aliveStatusCodes 31 | - httpHeaders[0]["headers"] 32 | 33 | Support for skipping validation for a link is supported via the following link MD format: 34 | 35 | [Duck Duck Go](https://duckduckgo.com "SKIP_VALIDATION") 36 | 37 | "SKIP_VALIDATION" marker instruct to skip the validation. 38 | 39 | Dependencies: 40 | pip install requests 41 | """ 42 | VALIDATION_SKIP_MARKER = "SKIP_VALIDATION" 43 | DEFAULT_ENCODING = "utf-8" 44 | DEFAULT_TIMEOUT_IN_SECONDS = 5 45 | DEFAULT_RETRY_COUNT = 2 46 | DEFAULT_ALIVE_STATUS_CODE = [200, 429, 502, 503, 504] 47 | DEFAULT_MAX_REDIRECT = 5 48 | DEFAULT_HEADERS = { 49 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36" 50 | } 51 | DEBUG_MODE = False 52 | 53 | 54 | def load_config(markdown_link_check_config_file_path): 55 | # Load markdown-link-check configuration is present 56 | if os.path.isfile(markdown_link_check_config_file_path): 57 | with open(markdown_link_check_config_file_path, mode="r", encoding=DEFAULT_ENCODING) as f: 58 | cfg = json.load(f) 59 | else: 60 | cfg = {} 61 | # Tune settings 62 | cfg["defaultConfig"] = (not os.path.isfile(markdown_link_check_config_file_path)) 63 | if "httpHeaders" not in cfg: 64 | cfg["httpHeaders"] = [{"headers": DEFAULT_HEADERS}] 65 | if len(cfg["httpHeaders"]) == 0: 66 | cfg["httpHeaders"] = [{"headers": DEFAULT_HEADERS}] 67 | if "headers" not in cfg["httpHeaders"][0]: 68 | cfg["httpHeaders"] = [{"headers": DEFAULT_HEADERS}] 69 | if "timeout" not in cfg: 70 | cfg["timeout"] = DEFAULT_TIMEOUT_IN_SECONDS 71 | else: 72 | cfg["timeout"] = int(cfg["timeout"].strip("ms")) 73 | if "retryCount" not in cfg: 74 | cfg["retryCount"] = DEFAULT_RETRY_COUNT 75 | if "aliveStatusCodes" not in cfg: 76 | cfg["aliveStatusCodes"] = DEFAULT_ALIVE_STATUS_CODE 77 | if len(cfg["aliveStatusCodes"]) == 0: 78 | cfg["aliveStatusCodes"] = DEFAULT_ALIVE_STATUS_CODE 79 | # Configure the automatic retry support 80 | req_session = Session() 81 | retries = Retry( 82 | total=cfg["retryCount"], 83 | redirect=DEFAULT_MAX_REDIRECT, 84 | backoff_factor=0.1, 85 | status_forcelist=[408, 500, 502, 503, 504], 86 | allowed_methods={"GET"}, 87 | raise_on_redirect=True 88 | ) 89 | req_session.mount("https://", HTTPAdapter(max_retries=retries)) 90 | req_session.mount("http://", HTTPAdapter(max_retries=retries)) 91 | cfg["req_session"] = req_session 92 | return cfg 93 | 94 | 95 | def extract_links(markdown_file_path): 96 | links = [] 97 | patterns = [r'<(http.*?)>', r'\[.*?\]\((http.*?)\)'] 98 | with open(markdown_file_path, mode="r", encoding=DEFAULT_ENCODING) as f: 99 | content = f.read() 100 | buffer_links = [] 101 | for pattern in patterns: 102 | buffer_links.extend(re.findall(pattern, content, re.IGNORECASE)) 103 | for link in buffer_links: 104 | if VALIDATION_SKIP_MARKER not in link.upper() and link not in links: 105 | links.append(link.strip(" ")) 106 | links.sort() 107 | return links 108 | 109 | 110 | def validate_link(cfg, link): 111 | status = (False, "") 112 | try: 113 | if DEBUG_MODE: 114 | print(f"<{link}> Test pending...") 115 | response = cfg["req_session"].get(link, headers=cfg["httpHeaders"][0]["headers"], timeout=cfg["timeout"], allow_redirects=True, verify=False) 116 | is_valid = (response.status_code in cfg["aliveStatusCodes"]) 117 | status = (is_valid, response.status_code) 118 | except Exception as e: 119 | status = (False, str(e)) 120 | finally: 121 | if DEBUG_MODE: 122 | print(f"<{link}> Test done, link is valid? {status[0]}.") 123 | return status 124 | 125 | 126 | def print_error(markdown_file_path, link, validation_status): 127 | if os.environ.get("GITHUB_WORKSPACE") is not None: 128 | print(f"::error file={markdown_file_path},title=BrokenLinkIdentified::Link is {link} => {validation_status[1]}") 129 | else: 130 | print(f"Broken link '{link}' identified in file '{markdown_file_path}' => {validation_status[1]}") 131 | return None 132 | 133 | 134 | if __name__ == "__main__": 135 | parser = argparse.ArgumentParser() 136 | parser.add_argument("-b", action="store", dest="base_folder", help="Location of the root base folder from which markdown files will be searched.", required=False, default=os.getcwd()) 137 | parser.add_argument("-c", action="store", dest="markdown_link_check_config_file_path", help="Location of the file containing the configuration for markdown-link-check.", required=False, default="markdown-link-check-config.json") 138 | parser.add_argument("-d", action=argparse.BooleanOptionalAction, dest="debug_mode", help="Print more information for help debugging.") 139 | args = parser.parse_args() 140 | markdown_link_check_config_file_path = args.markdown_link_check_config_file_path 141 | base_folder = args.base_folder 142 | DEBUG_MODE = args.debug_mode 143 | print("[+] Load config") 144 | conf = load_config(markdown_link_check_config_file_path) 145 | print(f"Default config loaded? {conf['defaultConfig']}") 146 | print("[+] Search and process any MD file") 147 | requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) 148 | dead_links_global_count = 0 149 | for md_file in pathlib.Path(base_folder).rglob("*.md", case_sensitive=False): 150 | markdown_file_path = str(md_file) 151 | markdown_file_name = md_file.name 152 | print(f"({markdown_file_name}) Extracting links...") 153 | links = extract_links(markdown_file_path) 154 | if len(links) == 0: 155 | print(f"({markdown_file_name}) No link found.") 156 | continue 157 | print(f"({markdown_file_name}) Processing {len(links)} link(s)...") 158 | dead_links_local_file_count = 0 159 | for link in links: 160 | validation_status = validate_link(conf, link) 161 | if not validation_status[0]: 162 | print_error(markdown_file_path, link, validation_status) 163 | dead_links_local_file_count += 1 164 | print(f"({markdown_file_name}) {dead_links_local_file_count} dead link(s) found.") 165 | dead_links_global_count += dead_links_local_file_count 166 | print(f"[+] Exit with code {dead_links_global_count}") 167 | sys.exit(dead_links_global_count) 168 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: OWASP Secure Headers Project 3 | level: 3 4 | url: https://owasp.org/www-project-secure-headers 5 | type: documentation 6 | layout: col-sidebar 7 | tags: headers 8 | pitch: Provides technical information about HTTP security headers. 9 | --- 10 | 11 | 12 | 13 | 14 | ## Introduction 15 | 16 | ![OSHP Logo](assets/images/oshp_logo.png) 17 | 18 | [![OWASP Production](https://img.shields.io/badge/owasp-production%20project-800080.svg)](https://www.owasp.org/projects) 19 | 20 | [![External Links Validity Check](https://github.com/OWASP/www-project-secure-headers/actions/workflows/check-external-links.yml/badge.svg?branch=master)](https://github.com/OWASP/www-project-secure-headers/actions/workflows/check-external-links.yml) 21 | 22 | [![Update headers reference JSON files](https://github.com/OWASP/www-project-secure-headers/actions/workflows/headers-generate-json-files.yml/badge.svg?branch=master)](https://github.com/OWASP/www-project-secure-headers/actions/workflows/headers-generate-json-files.yml) 23 | 24 | [![Update monitoring technical references dashboard](https://github.com/OWASP/www-project-secure-headers/actions/workflows/monitoring-technical-references-generate-dashboard.yml/badge.svg?branch=master)](https://github.com/OWASP/www-project-secure-headers/actions/workflows/monitoring-technical-references-generate-dashboard.yml) 25 | 26 | [![Perform_monitoring_oshp_site_references](https://github.com/OWASP/www-project-secure-headers/actions/workflows/monitoring-oshp-site-references.yml/badge.svg?branch=master)](https://github.com/OWASP/www-project-secure-headers/actions/workflows/monitoring-oshp-site-references.yml) 27 | 28 | [![update_tab_stats_related_files](https://github.com/OWASP/www-project-secure-headers/actions/workflows/tab-stats-headers-generate-related-files.yml/badge.svg?branch=master)](https://github.com/OWASP/www-project-secure-headers/actions/workflows/tab-stats-headers-generate-related-files.yml) 29 | 30 | 🎯 The **OWASP Secure Headers Project** (also called **OSHP**) describes HTTP response headers that your application can use to increase the security of your application. Once set, these HTTP response headers can restrict modern browsers from running into easily preventable vulnerabilities. The OWASP Secure Headers Project intends to raise awareness and use of these headers. 31 | 32 | 🤔 HTTP headers are well known and also despised. Seeking a balance between usability and security, developers implement functionality through the headers that can make applications more versatile or secure. But in practice how are the headers being implemented? What sites follow the best implementation practices? Big companies, small, all or none? 33 | 34 | ## Description 35 | 36 | 📚 The OWASP Secure Headers Project aim to provide elements about the following aspects regarding HTTP security headers: 37 | 38 | * [Guidance](https://owasp.org/www-project-secure-headers/index.html#div-bestpractices_configuration-proposal) about the recommended HTTP security headers that can be leveraged. 39 | * [Guidance](https://owasp.org/www-project-secure-headers/index.html#div-bestpractices_prevent-information-disclosure-via-http-headers) about the HTTP headers that should be removed. 40 | * [Tools](https://github.com/oshp/oshp-validator) to validate an HTTP security header configuration. 41 | * [Code](https://owasp.org/www-project-secure-headers/index.html#div-technical) libraries that can be leveraged to configure recommended HTTP security headers. 42 | * [Statistics](https://github.com/oshp/oshp-stats) about usage of the recommended HTTP security headers. 43 | 44 | 🏭 All the tools provided by the OSHP are gathered under this [GitHub organization](https://github.com/oshp/). 45 | 46 | 📺 A presentation of the project is available on the following locations: 47 | 48 | * [OWASP Spotlight Youtube playlists](https://www.youtube.com/watch?v=N4F3VWQYU9E). 49 | * [Application Security Podcast Youtube playlists](https://www.youtube.com/watch?v=0SNU9clVhKU). 50 | * [NoLimitSecu Podcast](https://www.nolimitsecu.fr/owasp-secure-headers-project/) (*French*). 51 | 52 | ## Migration 53 | 54 | 🌎 The OWASP Secure Headers Project was migrated from the [old website](https://wiki.owasp.org/index.php/OWASP_Secure_Headers_Project) to the [GitHub OWASP organization](https://github.com/OWASP/www-project-secure-headers). 55 | 56 | 📦 The following projects are now **archived**, they are initiatives that are now replaced by new projects: 57 | 58 | * [headers](https://github.com/oshp/headers). 59 | * [headers-ui-container](https://github.com/oshp/headers-ui-container). 60 | 61 | ## Security headers usage statistics 62 | 63 | 📈 We provide statistics, updated every month, about HTTP response security headers usage mentioned by the OWASP Secure Headers Project: 64 | 65 | * They are available through [this GitHub project](https://github.com/oshp/oshp-stats) and the tab named [Statistics](https://owasp.org/www-project-secure-headers/index.html#div-statistics). 66 | 67 | ## Security headers usage validator 68 | 69 | ✅ We provide a [venom](https://github.com/ovh/venom) tests suite to validate an HTTP security response header configuration against OWASP Secure Headers Project recommendation: 70 | 71 | * It is available through [this GitHub project](https://github.com/oshp/oshp-validator). 72 | 73 | 🧪 We also provide a *online mock endpoint* returning an HTTP response, for which, all HTTP response headers recommended by the OSHP will be set: 74 | 75 | * It is automatically deployed on `https://oshp-validator-mock.onrender.com` 76 | * Technical details about this endpoint are [here](https://github.com/oshp/oshp-validator#tests-suite-mock-service). 77 | 78 | ## Security headers reference files 79 | 80 | 📖 As mentioned in previous sections, we provide the collection of HTTP response security headers to add as well as HTTP response headers to remove, both in table form. 81 | 82 | 💡 Additionally, we provide this information as two JSON files to enable automation in the context of a provisioning workflow: 83 | 84 | * Collection of [HTTP response security headers to add](ci/headers_add.json). 85 | * Collection of [HTTP response headers to remove](ci/headers_remove.json). 86 | 87 | 📡 These json files are automatically updated. 88 | 89 | ## Technical references health dashboard 90 | 91 | 📍 We automatically generate and monitor this **[dashboard](https://github.com/OWASP/www-project-secure-headers/blob/master/monitoring_technical_references_dashboard.md)** to identify any dead project referenced in the **[Technical Resources](https://owasp.org/www-project-secure-headers/#div-technical)** tab. 92 | 93 | ## Discussions, information and roadmap 94 | 95 | 💬 We use the GitHub [discussions feature](https://github.com/OWASP/www-project-secure-headers/discussions) for discussions about the project as well as spreading global information about it. 96 | 97 | 👩‍💻 The work on the OSHP projects and associated components is tracked using the GitHub [project feature](https://github.com/orgs/OWASP/projects/44). 98 | 99 | ## Create a link to the OSHP site 100 | 101 | 📖 This is documented into the **[Case Studies](https://owasp.org/www-project-secure-headers/index.html#div-casestudies)** tab. 102 | 103 | ## Contributors 104 | 105 | 💌 Contributors to OSHP, before the migration of the project to [GitHub](https://github.com/OWASP/www-project-secure-headers): 106 | 107 | * [Alexandre Menezes](mailto:alexandre.fmenezes@owasp.org) 108 | * [Adam Averay](https://github.com/adamaveray) 109 | * [Jim Manico](https://github.com/jmanico) 110 | 111 | 💌 Visit this [page](https://github.com/OWASP/www-project-secure-headers/graphs/contributors) for updated information about the contributors since the migration of the project to GitHub. 112 | 113 | ## Licensing 114 | 115 | 📑 This project content is free to use. It is licensed under the [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0). 116 | -------------------------------------------------------------------------------- /info.md: -------------------------------------------------------------------------------- 1 | ### Project Information 2 | 3 | * Production project 4 | 5 | #### Classification 6 | 7 | * Documentation 8 | 9 | #### Audience 10 | 11 | * Builder 12 | * Defender 13 | 14 | ### Project GitHub Organization 15 | 16 | * 💻 [OSHP](https://github.com/oshp/) 17 | 18 | ### Notification 19 | 20 | * 🌏 We use the hashtag `owasp_shp` in our posts on [Bluesky](https://bsky.app/hashtag/owasp_shp) and [LinkedIn](https://www.linkedin.com/feed/hashtag/?keywords=owasp_shp), to publish an update about the project. 21 | * 📡 This [atom web feed](https://github.com/OWASP/www-project-secure-headers/commits/master.atom) can be used to be notified when an update is pushed on the OSHP website's repository. 22 | -------------------------------------------------------------------------------- /leaders.md: -------------------------------------------------------------------------------- 1 | ### Leaders 2 | 3 | * [Ricardo Iramar](mailto:ricardo.iramar@owasp.org) 4 | * [Dominique Righetto](mailto:dominique.righetto@owasp.org) 5 | -------------------------------------------------------------------------------- /logo/generated-via-ai/logo01-chatgpt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/generated-via-ai/logo01-chatgpt.png -------------------------------------------------------------------------------- /logo/generated-via-ai/logo02-microsoft-copilot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/generated-via-ai/logo02-microsoft-copilot.png -------------------------------------------------------------------------------- /logo/generated-via-ai/logo03-microsoft-copilot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/generated-via-ai/logo03-microsoft-copilot.png -------------------------------------------------------------------------------- /logo/generated-via-ai/logo04-microsoft-copilot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/generated-via-ai/logo04-microsoft-copilot.png -------------------------------------------------------------------------------- /logo/generated-via-ai/logo05-microsoft-copilot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/generated-via-ai/logo05-microsoft-copilot.png -------------------------------------------------------------------------------- /logo/generated-via-ai/logo06-microsoft-copilot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/generated-via-ai/logo06-microsoft-copilot.png -------------------------------------------------------------------------------- /logo/generated-via-ai/logo07-chatgpt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/generated-via-ai/logo07-chatgpt.png -------------------------------------------------------------------------------- /logo/generated-via-ai/logo08-microsoft-copilot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/generated-via-ai/logo08-microsoft-copilot.png -------------------------------------------------------------------------------- /logo/generated-via-ai/logo09-microsoft-copilot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/generated-via-ai/logo09-microsoft-copilot.png -------------------------------------------------------------------------------- /logo/logo-all.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-all.pdf -------------------------------------------------------------------------------- /logo/logo-black-on-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-black-on-transparent.png -------------------------------------------------------------------------------- /logo/logo-black-on-white.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-black-on-white.jpg -------------------------------------------------------------------------------- /logo/logo-blue-on-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-blue-on-transparent.png -------------------------------------------------------------------------------- /logo/logo-blue-on-white.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-blue-on-white.jpg -------------------------------------------------------------------------------- /logo/logo-mockup1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-mockup1.jpg -------------------------------------------------------------------------------- /logo/logo-mockup2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-mockup2.jpg -------------------------------------------------------------------------------- /logo/logo-mockup3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-mockup3.jpg -------------------------------------------------------------------------------- /logo/logo-source.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-source.ai -------------------------------------------------------------------------------- /logo/logo-source.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-source.eps -------------------------------------------------------------------------------- /logo/logo-white-on-black.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-white-on-black.jpg -------------------------------------------------------------------------------- /logo/logo-white-on-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OWASP/www-project-secure-headers/ee73679fe8b0e7c6643a6261ebafdd8893096e35/logo/logo-white-on-transparent.png -------------------------------------------------------------------------------- /markdown-link-check_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "httpHeaders": [ 3 | { 4 | "urls": [ 5 | "https://", 6 | "http://" 7 | ], 8 | "headers": { 9 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36" 10 | } 11 | } 12 | ], 13 | "timeout": "60s", 14 | "retryCount": 2, 15 | "aliveStatusCodes": [ 16 | 200, 17 | 403, 18 | 429, 19 | 500, 20 | 502, 21 | 503, 22 | 524, 23 | 526 24 | ] 25 | } -------------------------------------------------------------------------------- /monitoring_technical_references_dashboard.md: -------------------------------------------------------------------------------- 1 | 2 | # Technical References Dashboard 3 | 4 | > 📅 Last verification (UTC): 2025-06-08 00:10:17 5 | 6 | ## GitHub repositories health status 7 | 8 | Provides a quick visual status on the health status (whether they are updated or not) of the referenced GitHub projects in the tab named **Technical**. 9 | 10 | Project reaching the :red_circle: status **are removed**. 11 | 12 | :speech_balloon: **Status icon legends:** 13 | 14 | * :green_circle: Updated within the last **12 months** from the date of verification. 15 | * :orange_circle: Updated within the last **24 months** from the date of verification. 16 | * :red_circle: Not updated for **more than 24 months** from the date of verification. 17 | 18 | | Last update | Status | Repository | 19 | | --- | --- | --- | 20 | | `2023-05-22T21:11:40Z` (25 months ago) | :red_circle: | [nlf/blankie](https://github.com/nlf/blankie) | 21 | | `2023-11-17T09:59:27Z` (19 months ago) | :orange_circle: | [aidantwoods/SecureHeaders](https://github.com/aidantwoods/SecureHeaders) | 22 | | `2024-10-18T09:29:34Z` (8 months ago) | :green_circle: | [TypeError/secure](https://github.com/TypeError/secure) | 23 | | `2024-10-22T21:52:10Z` (8 months ago) | :green_circle: | [unrolled/secure](https://github.com/unrolled/secure) | 24 | | `2024-11-03T20:39:16Z` (7 months ago) | :green_circle: | [brokenhandsio/VaporSecurityHeaders](https://github.com/brokenhandsio/VaporSecurityHeaders) | 25 | | `2025-01-01T09:04:23Z` (5 months ago) | :green_circle: | [rwjblue/ember-cli-content-security-policy/](https://github.com/rwjblue/ember-cli-content-security-policy/) | 26 | | `2025-01-08T20:57:05Z` (5 months ago) | :green_circle: | [Santandersecurityresearch/DrHeader](https://github.com/Santandersecurityresearch/DrHeader) | 27 | | `2025-01-19T15:56:43Z` (5 months ago) | :green_circle: | [riramar/hsecscan](https://github.com/riramar/hsecscan) | 28 | | `2025-02-20T00:41:24Z` (4 months ago) | :green_circle: | [google/csp-evaluator](https://github.com/google/csp-evaluator) | 29 | | `2025-03-05T14:55:26Z` (3 months ago) | :green_circle: | [hapijs/hapi](https://github.com/hapijs/hapi) | 30 | | `2025-04-11T00:12:36Z` (2 months ago) | :green_circle: | [bepsvpt/secure-headers](https://github.com/bepsvpt/secure-headers) | 31 | | `2025-04-14T15:12:31Z` (2 months ago) | :green_circle: | [github/secure_headers](https://github.com/github/secure_headers) | 32 | | `2025-04-21T05:29:12Z` (2 months ago) | :green_circle: | [tmotagam/Secweb](https://github.com/tmotagam/Secweb) | 33 | | `2025-04-22T04:46:42Z` (2 months ago) | :green_circle: | [GaProgMan/OwaspHeaders.Core](https://github.com/GaProgMan/OwaspHeaders.Core) | 34 | | `2025-05-02T17:23:45Z` (1 months ago) | :green_circle: | [sdelements/django-security](https://github.com/sdelements/django-security) | 35 | | `2025-05-07T14:58:20Z` (1 months ago) | :green_circle: | [mozilla/django-csp](https://github.com/mozilla/django-csp) | 36 | | `2025-05-07T20:57:41Z` (1 months ago) | :green_circle: | [andrewlock/NetEscapades.AspNetCore.SecurityHeaders](https://github.com/andrewlock/NetEscapades.AspNetCore.SecurityHeaders) | 37 | | `2025-05-28T18:16:33Z` (1 months ago) | :green_circle: | [drwetter/testssl.sh](https://github.com/drwetter/testssl.sh) | 38 | | `2025-06-02T22:34:50Z` (0 months ago) | :green_circle: | [mdn/mdn-http-observatory](https://github.com/mdn/mdn-http-observatory) | 39 | | `2025-06-03T15:13:11Z` (0 months ago) | :green_circle: | [helmetjs/helmet](https://github.com/helmetjs/helmet) | 40 | | `2025-06-07T19:37:39Z` (0 months ago) | :green_circle: | [rfc-st/humble](https://github.com/rfc-st/humble) | 41 | 42 | -------------------------------------------------------------------------------- /project.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": {} 8 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests -------------------------------------------------------------------------------- /tab_bestpractices.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: bestpractices 3 | displaytext: Best Practices 4 | layout: null 5 | tab: true 6 | order: 3 7 | tags: headers 8 | --- 9 | 10 | # Best Practices 11 | 12 | * [Configuration proposal](#configuration-proposal) 13 | * [Prevent information disclosure via HTTP headers](#prevent-information-disclosure-via-http-headers) 14 | * [Prevent exposure to cross-site scripting when hosting uploaded files](#prevent-exposure-to-cross-site-scripting-when-hosting-uploaded-files) 15 | * [Prevent CORS misconfiguration issues](#prevent-cors-misconfiguration-issues) 16 | * [Prevent information disclosure via the browser local cached files](#prevent-information-disclosure-via-the-browser-local-cached-files) 17 | * [Prevent CSP bypasses](#prevent-csp-bypasses) 18 | * [Support for a large CSP policy](#support-for-a-large-csp-policy) 19 | 20 | ## Configuration proposal 21 | 22 | 23 | 24 | Please note the best practices below suggest methods to change web server configuration to add headers. Security headers can also be successfully added to your application at the software level as well in almost every web language. Many web frameworks add some of these headers automatically. 25 | 26 | The following section proposes a configuration for the [actively supported and working draft security headers](https://owasp.org/www-project-secure-headers/#div-headers). 27 | 28 | 💡 Additional information about HTTP security headers on [OpenCRE](https://opencre.org/cre/636-347?name=OWASP+Secure+Headers+Project§ion=configuration&link=https%3A%2F%2Fowasp.org%2Fwww-project-secure-headers%2F%23div-bestpractices). 29 | 30 | 📖 The headers proposed below can be applied both in the context of a *classic web application* and in that of a *web API*. 31 | 32 | 🚩 Regarding the header `Content-Security-Policy`, keep in mind that the policy applicability depends on the execution context. Technical details are available [here](https://www.w3.org/TR/CSP2/#which-policy-applies). Therefore, CSP usage in a web API application implies to **define the CSP in the document consuming the content of the web API**. 33 | 34 | 🚩 The header `Clear-Site-Data` will cause the browser to take additional processing time for the HTTP response, so, set it to the logout function when possible. 35 | 36 | 🔬 For the header `Permissions-Policy`, as it is currently only supported by [Chromium based browsers](https://caniuse.com/permissions-policy), the proposed value was generated with this [site](https://www.permissionspolicy.com/) and tested against the version `137.0.7124.0` of [Chromium](https://chromium.woolyss.com/download/en/) to only specify supported features. 37 | 38 | 💡 Content of the table below is also provided, as JSON, via this [file](ci/headers_add.json) (automatically updated). 39 | 40 | 41 | 42 | | Header name | Proposed value | 43 | | ---------------------------------------------|------------| 44 | | Strict-Transport-Security | `max-age=31536000; includeSubDomains` | 45 | | X-Frame-Options | `deny` | 46 | | X-Content-Type-Options | `nosniff` | 47 | | Content-Security-Policy | `default-src 'self'; form-action 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests` | 48 | | X-Permitted-Cross-Domain-Policies | `none` | 49 | | Referrer-Policy | `no-referrer` | 50 | | Clear-Site-Data | `"cache","cookies","storage"` | 51 | | Cross-Origin-Embedder-Policy | `require-corp` | 52 | | Cross-Origin-Opener-Policy | `same-origin` | 53 | | Cross-Origin-Resource-Policy | `same-origin` | 54 | | Permissions-Policy | `accelerometer=(), autoplay=(), camera=(), cross-origin-isolated=(), display-capture=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(self), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), gamepad=(), hid=(), idle-detection=(), interest-cohort=(), serial=(), unload=()` | 55 | | Cache-Control | `no-store, max-age=0` | 56 | 57 | 58 | 59 | ## Prevent information disclosure via HTTP headers 60 | 61 | 62 | 63 | This section provides a collection of HTTP response headers to remove, when possible, from any HTTP response to prevent any [disclosure of technical information](https://cwe.mitre.org/data/definitions/200.html) about environment. The following list of headers can be used to configure a [reverse proxy](https://www.nginx.com/resources/glossary/reverse-proxy-server/) or a [web application firewall](https://en.wikipedia.org/wiki/Web_application_firewall) to handle removal operation of the mentioned headers. 64 | 65 | 💡 Additional information about technical information disclosure in HTTP header on [OpenCRE](https://www.opencre.org/cre/403-005?name=OWASP+Secure+Headers+Project§ion=Prevent+information+disclosure+via+HTTP+headers&link=https%3A%2F%2Fowasp.org%2Fwww-project-secure-headers%2F%23div-bestpractices_prevent-information-disclosure-via-http-headers). 66 | 67 | 💡 When an HTTP response header is known by the analytics site [WebTechSurvey](https://webtechsurvey.com/), then, a reference link is added to its usage statistics page. Otherwise, a reference link regarding the documentation of the header is provided. 68 | 69 | 🚩 The response header `Content-Type` can sometimes discloses the web framework used. It is the case for the following ones: 70 | 71 | * [Spring Boot Actuator REST API](https://docs.spring.io/spring-boot/api/rest/actuator/auditevents.html): `Content-Type: application/vnd.spring-boot.actuator.v3+json`. 72 | 73 | 💡 Content of the table below is also provided, as JSON, via this [file](ci/headers_remove.json) (automatically updated). 74 | 75 | 76 | 77 | | Header name | Header value example | Description | 78 | | --------------------|----------------------|-------------| 79 | | [Server](https://webtechsurvey.com/response-header/server) | `Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips` | Contain information about the server handling the request. | 80 | | [Liferay-Portal](https://webtechsurvey.com/response-header/liferay-portal) | `Liferay Digital Experience Platform 7.2.10 GA1` | Contain the version of the [Liferay](https://www.liferay.com) software in use. | 81 | | [X-Turbo-Charged-By](https://webtechsurvey.com/response-header/x-turbo-charged-by) | `LiteSpeed/5.4.12 Enterprise` | Contain information about the server handling the request. | 82 | | [X-Powered-By](https://webtechsurvey.com/response-header/x-powered-by) | `PHP/5.3.3` | Contain information about hosting environments or other frameworks in use. | 83 | | [X-Server-Powered-By](https://webtechsurvey.com/response-header/x-server-powered-by) | `Engintron` | Contain information about hosting environments or other frameworks in use. | 84 | | [X-Powered-CMS](https://webtechsurvey.com/response-header/x-powered-cms) | `Bitrix Site Manager (DEMO)` | Contain the information about the [CMS](https://en.wikipedia.org/wiki/Content_management_system) that generated the HTTP response. | 85 | | [SourceMap](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/SourceMap) | `https://mysite.com/js/mylib.js.map`| Links generated code to a [source map](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map) file, enabling the browser to reconstruct the original source and present the reconstructed original in the debugger. | 86 | | [X-SourceMap](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/SourceMap) | `https://mysite.com/js/mylib.js.map`| Links generated code to a [source map](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map) file, enabling the browser to reconstruct the original source and present the reconstructed original in the debugger. | 87 | | [X-AspNetMvc-Version](https://webtechsurvey.com/response-header/x-aspnetmvc-version) | `5.2` | Contain the version of the ASP .Net MVC framework in use. | 88 | | [X-AspNet-Version](https://webtechsurvey.com/response-header/x-aspnet-version) | `4.0.30319` | Contain the version of the ASP .Net framework Common Language Runtime (CLR) in use (see [here](https://github.com/OWASP/www-project-secure-headers/issues/215) for more details).| 89 | | [X-SourceFiles](https://webtechsurvey.com/response-header/x-sourcefiles) | `=?UTF-8?B?QzpcVXNlcnN?=` | Contain information needed by the .Net SDK debugger during debugging operation on a project. | 90 | | [X-Redirect-By](https://webtechsurvey.com/response-header/x-redirect-by) | `TYPO3 Shortcut/Mountpoint` | Specifies the component that is responsible for a particular redirect (source [Wikipedia](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields)). | 91 | | [X-Generator](https://webtechsurvey.com/response-header/x-generator) | `Drupal 8` | Contain the information about the [CMS](https://en.wikipedia.org/wiki/Content_management_system) that generated the HTTP response. | 92 | | [X-Generated-By](https://webtechsurvey.com/response-header/x-generated-by) | `Smartsite version 7.11.1.3` | Contain the information about the [CMS](https://en.wikipedia.org/wiki/Content_management_system) that generated the HTTP response. | 93 | | [X-CMS](https://webtechsurvey.com/response-header/x-cms) | `Thinq CMS 1.7.0.0` | Contain the information about the [CMS](https://en.wikipedia.org/wiki/Content_management_system) that generated the HTTP response. | 94 | | [X-Powered-By-Plesk](https://webtechsurvey.com/response-header/x-powered-by-plesk) | `PleskLin` or `PleskWin` | Indicate that the platform is based on the [Plesk](https://www.plesk.com) software in addition to the underlying operating system. | 95 | | [X-Php-Version](https://webtechsurvey.com/response-header/x-php-version) | `7.4` | Indicate the version of [PHP](https://www.php.net) technology used. | 96 | | [Powered-By](https://webtechsurvey.com/response-header/powered-by) | `PrestaShop` | Indicate the name of the framework or platform used. | 97 | | [X-Content-Encoded-By](https://webtechsurvey.com/response-header/x-content-encoded-by) | `Joomla! 2.5` | Indicate the name of the framework or platform used. | 98 | | [Product](https://webtechsurvey.com/response-header/product) | `Z-BlogPHP 1.7.2` | Indicate the name of the framework or platform used. | 99 | | [X-CF-Powered-By](https://webtechsurvey.com/response-header/x-cf-powered-by) | `CF-Joomla 0.1.5` | Indicate the name of the framework or platform used. | 100 | | [X-Framework](https://webtechsurvey.com/response-header/x-framework) | `JP/4.01` | Indicate the name of the framework or platform used. | 101 | | [Host-Header](https://webtechsurvey.com/response-header/host-header) | `owasp.org` | Indicate which virtual host of the web server the response is coming from. | 102 | | [Pega-Host](https://webtechsurvey.com/response-header/pega-host) | `srv-pega11` | Indicate the internal host name of the server that handled the request in the context of usage of a software from the [PEGA](https://www.pega.com/) company. | 103 | | [X-Atmosphere-first-request](https://github.com/Atmosphere/atmosphere) | `true` | Indicate that the java framework [Atmosphere](https://github.com/Atmosphere/atmosphere) is used. | 104 | | [X-Atmosphere-tracking-id](https://github.com/Atmosphere/atmosphere) | `7852fcbf-f8a9-4667-9dcc-a0b5b162499c` | Indicate that the java framework [Atmosphere](https://github.com/Atmosphere/atmosphere) is used. | 105 | | [X-Atmosphere-error](https://github.com/Atmosphere/atmosphere) | `Websocket protocol not supported` | Indicate that the java framework [Atmosphere](https://github.com/Atmosphere/atmosphere) is used. | 106 | | [X-Mod-Pagespeed](https://webtechsurvey.com/response-header/x-mod-pagespeed) | `1.13.35.2-0` | Indicate the presence of the Apache module [mod_pagespeed](https://github.com/apache/incubator-pagespeed-mod) in the call flow. | 107 | | [X-Page-Speed](https://webtechsurvey.com/response-header/x-page-speed) | `1.13.35.2-0` | Indicate the presence of the Nginx module [mod_pagespeed](https://github.com/apache/incubator-pagespeed-ngx) in the call flow. | 108 | | [X-Varnish-Backend](https://webtechsurvey.com/response-header/x-varnish-backend) | `pb01` | Indicate the name of the backend server from which the [Varnish](https://varnish-cache.org) instance will accelerate the content. | 109 | | [X-Varnish-Server](https://webtechsurvey.com/response-header/x-varnish-server) | `proxy01` | Indicate the name of the [Varnish](https://varnish-cache.org) server instance that provided the accelerated content. | 110 | | [X-Envoy-Upstream-Service-Time](https://webtechsurvey.com/response-header/x-envoy-upstream-service-time) | `42` | Indicate the presence of the proxy software [Envoy](https://www.envoyproxy.io) in the call flow. | 111 | | [X-Envoy-Attempt-Count](https://webtechsurvey.com/response-header/x-envoy-attempt-count) | `1` | Indicate the presence of the proxy software [Envoy](https://www.envoyproxy.io) in the call flow. | 112 | | [X-Envoy-External-Address](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers) | `124.128.159.165` | Indicate the presence of the proxy software [Envoy](https://www.envoyproxy.io) in the call flow. | 113 | | [X-Envoy-Internal](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers) | `true` | Indicate the presence of the proxy software [Envoy](https://www.envoyproxy.io) in the call flow. | 114 | | [X-Envoy-Original-Dst-Host](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers) | `10.195.16.237:8888` | Indicate the presence of the proxy software [Envoy](https://www.envoyproxy.io) in the call flow. | 115 | | [X-B3-ParentSpanId](https://webtechsurvey.com/response-header/x-b3-parentspanid) | `dea3f6d0324583db` | Indicate the presence of the software [Zipkin](https://zipkin.io/) that is a distributed tracing system. | 116 | | [X-B3-Sampled](https://webtechsurvey.com/response-header/x-b3-sampled) | `0` | Indicate the presence of the software [Zipkin](https://zipkin.io/) that is a distributed tracing system. | 117 | | [X-B3-SpanId](https://webtechsurvey.com/response-header/x-b3-spanid) | `244753d494e83353` | Indicate the presence of the software [Zipkin](https://zipkin.io/) that is a distributed tracing system. | 118 | | [X-B3-TraceId](https://webtechsurvey.com/response-header/x-b3-traceid) | `11bef07b0f5c0468` | Indicate the presence of the software [Zipkin](https://zipkin.io/) that is a distributed tracing system. | 119 | | [K-Proxy-Request](https://knative.dev/docs/serving/istio-authorization/) | `activator` | Indicate the presence of the software [Knative](https://knative.dev) that is an Open-Source Enterprise-level solution to build Serverless and Event Driven Applications in Kubernetes environments. | 120 | | [X-Old-Content-Length](https://webtechsurvey.com/response-header/x-old-content-length) | `135` | Indicate the presence of the software [WebSEAL](https://www.ibm.com/docs/en/samfm/8.0.1.2?topic=overview-webseal-introduction) that is a high performance, multithreaded web server by IBM. | 121 | | [$wsep](https://www.ibm.com/docs/en/was/8.5.5?topic=SSEQTP_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/rweb_custom_props.htm) | `empty value` | Indicate the presence of the software [WebSphere Application Server](https://www.ibm.com/products/websphere-application-server) that is a JavaEE application server by IBM. | 122 | | [X-Nextjs-Matched-Path](https://webtechsurvey.com/response-header/x-nextjs-matched-path) | `/blog` | Indicate that the web framework [Next.js](https://nextjs.org/) is used. | 123 | | [X-Nextjs-Page](https://webtechsurvey.com/response-header/x-nextjs-page) | `/articles` | Indicate that the web framework [Next.js](https://nextjs.org/) is used. | 124 | | [X-Nextjs-Cache](https://webtechsurvey.com/response-header/x-nextjs-cache) | `REVALIDATED` | Indicate that the web framework [Next.js](https://nextjs.org/) is used. | 125 | | [X-Nextjs-Redirect](https://github.com/search?q=repo%3Avercel%2Fnext.js%20X-Nextjs-Redirect&type=code) | `/home` | Indicate that the web framework [Next.js](https://nextjs.org/) is used. | 126 | | [X-OneAgent-JS-Injection](https://webtechsurvey.com/response-header/x-oneagent-js-injection) | `true` | Indicate that the [Dynatrace](https://www.dynatrace.com) analytics and automation platform is used. | 127 | | [X-ruxit-JS-Agent](https://webtechsurvey.com/response-header/X-ruxit-JS-Agent) | `true` | Indicate that the [Dynatrace](https://www.dynatrace.com) analytics and automation platform is used. | 128 | | [X-dtHealthCheck](https://www.dynatrace.com/support/help/platform-modules/digital-experience/web-applications/initial-setup/firewall-constraints-for-rum) | `Technical diagnostic data` | Indicate that the [Dynatrace](https://www.dynatrace.com) analytics and automation platform is used. | 129 | | [X-dtAgentId](https://www.dynatrace.com/support/help/platform-modules/digital-experience/web-applications/initial-setup/firewall-constraints-for-rum) | `95b3121c36` | Indicate that the [Dynatrace](https://www.dynatrace.com) analytics and automation platform is used. | 130 | | [X-dtInjectedServlet](https://www.dynatrace.com/support/help/platform-modules/digital-experience/web-applications/initial-setup/firewall-constraints-for-rum) | `com.company.ReportServlet` | Indicate that the [Dynatrace](https://www.dynatrace.com) analytics and automation platform is used. | 131 | | [X-Litespeed-Cache-Control](https://webtechsurvey.com/response-header/X-Litespeed-Cache-Control) | `no-cache` | Indicate the presence of the [LiteSpeed](https://litespeedtech.com/) web server. | 132 | | [X-LiteSpeed-Purge](https://webtechsurvey.com/response-header/X-LiteSpeed-Purge) | `/phpinfo.php` | Indicate the presence of the [LiteSpeed](https://litespeedtech.com/) web server. | 133 | | [X-LiteSpeed-Tag](https://webtechsurvey.com/response-header/X-LiteSpeed-Tag) | `pubtag1,pubtag2` | Indicate the presence of the [LiteSpeed](https://litespeedtech.com/) web server. | 134 | | [X-LiteSpeed-Vary](https://webtechsurvey.com/response-header/X-LiteSpeed-Vary) | `value=ismobile` | Indicate the presence of the [LiteSpeed](https://litespeedtech.com/) web server. | 135 | | [X-LiteSpeed-Cache](https://webtechsurvey.com/response-header/X-LiteSpeed-Cache) | `hit,litemage` | Indicate the presence of the [LiteSpeed](https://litespeedtech.com/) web server. | 136 | | [X-Umbraco-Version](https://webtechsurvey.com/response-header/X-Umbraco-Version) | `4.7` | Indicate the usage of the [Umbraco CMS](https://umbraco.com/products/umbraco-cms/) software as well as its version. | 137 | | [OracleCommerceCloud-Version](https://webtechsurvey.com/response-header/OracleCommerceCloud-Version) | `23.08.01` | Indicate the usage of the [Oracle Commerce](https://www.oracle.com/cx/ecommerce/) software as well as its version. | 138 | | [X-BEServer](https://webtechsurvey.com/response-header/x-beserver) | `EXSRV01` | Indicate the internal host name of the server that handled the request in the context of usage of the [Microsoft Exchange](https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-ashttp/08ad30b6-5b73-41bc-890b-1cab2cf49827) software. | 139 | | [X-DiagInfo](https://webtechsurvey.com/response-header/x-diaginfo) | `EXSRV01` | Indicate the internal host name of the server that handled the request in the context of usage of the [Microsoft Exchange](https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-ashttp/08ad30b6-5b73-41bc-890b-1cab2cf49827) software. | 140 | | [X-FEServer](https://webtechsurvey.com/response-header/x-feserver) | `EXSRV01` | Indicate the internal host name of the server that handled the request in the context of usage of the [Microsoft Exchange](https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-ashttp/08ad30b6-5b73-41bc-890b-1cab2cf49827) software. | 141 | | [X-CalculatedBETarget](https://webtechsurvey.com/response-header/x-calculatedbetarget) | `exsrv01.mydomain.com` | Indicate the internal host name of the server that handled the request in the context of usage of the [Microsoft Exchange](https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-ashttp/08ad30b6-5b73-41bc-890b-1cab2cf49827) software. | 142 | | [X-OWA-Version](https://webtechsurvey.com/response-header/x-owa-version) | `15.2.1258.27` | Indicate the version of the Microsoft Exchange software in use. | 143 | | [X-Cocoon-Version](https://webtechsurvey.com/response-header/x-cocoon-version) | `2.1.13` | Indicate that the web framework [Apache Cocoon](https://cocoon.apache.org/) is used as well as the version used. | 144 | | [X-Kubernetes-PF-FlowSchema-UI](https://kubernetes.io/docs/reference/debug-cluster/flow-control) | `cf931e2d-5a5e-4c12-892c-9bafa71f30dc` | Indicate that the web application issuing the HTTP response is deployed on a [Kubernetes](https://kubernetes.io/) cluster. | 145 | | [X-Kubernetes-PF-PriorityLevel-UID](https://kubernetes.io/docs/reference/debug-cluster/flow-control) | `78b3face-e1cf-4fc6-a27e-08eb7f0f5b23` | Indicate that the web application issuing the HTTP response is deployed on a [Kubernetes](https://kubernetes.io/) cluster. | 146 | | [X-Jitsi-Release](https://webtechsurvey.com/response-header/x-jitsi-release) | `5082` | Indicate the version of [Jitsi](https://github.com/jitsi/jitsi) software in use. | 147 | | [X-Joomla-Version](https://webtechsurvey.com/response-header/x-joomla-version) | `3.9.25` |Indicate that the CMS [Joomla](https://www.joomla.org/) is used as well as the version used. | 148 | | [X-Backside-Transport](https://webtechsurvey.com/response-header/x-backside-transport) | `OK OK` |Indicate the presence of the products [IBM WebSphere DataPower](https://www.ibm.com/products/datapower-gateway) in the call flow.| 149 | 150 | 151 | 152 | ## Prevent exposure to cross-site scripting when hosting uploaded files 153 | 154 | This section describes, how the HTTP response header named [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition), can be used to prevent exposure to cross-site scripting when hosting uploaded files and opening them in the same web browsing context than the application. 155 | 156 | It can happen a case in which an application allows a user to upload a file and then allow this file to be accessed by other users. If such feature allows uploading of HTML files (also apply for [SVG file](https://hackerone.com/reports/1276742)) then it can be used, as a vector, to store an HTML file containing JavaScript code. Therefore, the feature become prone to [stored cross-site scripting](https://portswigger.net/web-security/cross-site-scripting/stored) vulnerability. 157 | 158 | To prevent this exposure, the HTTP response header named [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition), can be used with the following value to instruct browsers to download the file instead of open it in the same web browsing context than the application: 159 | 160 | ```text 161 | Content-Disposition: attachment; filename="myfile.html" 162 | ``` 163 | 164 | ## Prevent CORS misconfiguration issues 165 | 166 | > 📖 An excellent tutorial about [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (called **CORS**) is provided on the [Mozilla MDN](https://developer.mozilla.org/en-US/). In addition, [Julien Cretel](https://jub0bs.com/about/) provided a great [blog post](https://jub0bs.com/posts/2023-02-08-fearless-cors/) about CORS pitfalls. 167 | 168 | This section proposes an approach to help preventing [CORS misconfiguration issues](https://portswigger.net/research/exploiting-cors-misconfigurations-for-bitcoins-and-bounties) using a simple idea: *Provide the collection of [CORS related HTTP response headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_response_headers) to use according to different contexts.* 169 | 170 | ### Key points to consider 171 | 172 | * 💡 If the web framework/web server you are using provides CORS features then always leverage them instead of implements it manually: 173 | * [List of web framework/web server](https://enable-cors.org/server.html) supporting CORS. 174 | * [CORS middleware for Go](https://pkg.go.dev/github.com/jub0bs/cors) by Julien Cretel. 175 | 176 | * 🚩 Whatever the context, when the request is a **HTTP OPTIONS** ([preflight request](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests)) then the value provided by the following headers must be validated against expected values. If the validation failed then return an HTTP 403 **without any [CORS related HTTP response headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_response_headers)**: 177 | 178 | * [Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#origin) 179 | * [Access-Control-Request-Method](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#access-control-request-method) 180 | * [Access-Control-Request-Headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#access-control-request-headers) 181 | 182 | * 🚩 Validation of the `Origin` / `Access-Control-Request-Method` / `Access-Control-Request-Headers` request header value, against a list of allowed ones, must be performed using [strict case sensitive string comparison](https://jub0bs.com/posts/2023-02-08-fearless-cors/#violation-of-the-case-sensitivity-of-method-names) to prevent, as much as possible, the [presence of bypasses into the validation logic](https://portswigger.net/web-security/cors#errors-parsing-origin-headers). If possible, does not use regular expression for the implementation of the validation, see [here](https://jub0bs.com/posts/2023-02-08-fearless-cors/#disallow-dangerous-origin-patterns) for an explanation of the source of this recommendation. 183 | 184 | * 🚩 CORS scope is the access control aspect, from a browser perspective (client side), regarding [cross origins](https://developer.mozilla.org/en-US/docs/Glossary/Origin) access to a resource. Thus, it **does NOT replace** the requirement to implements access control on the server side too. CORS and server-side access controls are complementary. 185 | 186 | * 🚩 Never take the value of the request header [Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#origin) to add it into the response header [Access-Control-Allow-Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#access-control-allow-origin) (mirroring), see [here](https://portswigger.net/web-security/cors#server-generated-acao-header-from-client-specified-origin-header) for an explanation of the source of this recommendation. Indeed, always use a list of allowed origins when only a restricted collection of origins is expected to call your endpoints. 187 | 188 | ### Contexts 189 | 190 | #### Public without authentication 191 | 192 | 💬 Context: 193 | 194 | Your endpoints are expected to be consumed, by a browser, from any origins without any authentication. 195 | 196 | 💻 Configuration proposal: 197 | 198 | ```text 199 | Access-Control-Allow-Origin: * 200 | Access-Control-Max-Age: 3600 201 | Access-Control-Allow-Credentials: false 202 | ``` 203 | 204 | #### Public with authentication 205 | 206 | 💬 Context: 207 | 208 | Your endpoints are expected to be consumed, by a browser, from any origins with authentication. 209 | 210 | 🚩 The value `*`, for the response header `Access-Control-Allow-Origin`, cannot be used when the response header `Access-Control-Allow-Credentials` is allowed (`true`). Indeed, the browser raises the following error (tested on Chrome 118.x): 211 | 212 | ```text 213 | The value of the 'Access-Control-Allow-Origin' header in the response must not be 214 | the wildcard '*' when the request's credentials mode is 'include'. 215 | ``` 216 | 217 | 📖 It is explicitly mentioned, into the [Mozilla MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests) documentation, as the following: 218 | 219 | ```text 220 | When responding to a "credentialed request" request, the server must specify an origin in the value 221 | of the Access-Control-Allow-Origin header, instead of specifying the "*" wildcard. 222 | ``` 223 | 224 | Therefore, refer to the [restricted with authentication](#restricted-with-authentication) case for the configuration to use. 225 | 226 | #### Restricted without authentication 227 | 228 | 💬 Context: 229 | 230 | Your endpoints are expected to be consumed, by a browser, from a specific collection of origins without any authentication. 231 | 232 | 💻 Configuration proposal: 233 | 234 | * If the value of the request header [Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#origin) is present into the **list of allowed Origins** then: 235 | 236 | ```text 237 | Access-Control-Allow-Origin: [Value_Taken_From_The_List_Of_Allowed_Origins] 238 | Access-Control-Max-Age: 10 239 | Access-Control-Allow-Credentials: false 240 | ``` 241 | 242 | * Otherwise return an HTTP 403 **without any [CORS related HTTP response headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_response_headers)**. 243 | 244 | #### Restricted with authentication 245 | 246 | 💬 Context: 247 | 248 | Your endpoints are expected to be consumed, by a browser, from a specific collection of origins with authentication. 249 | 250 | 💻 Configuration proposal: 251 | 252 | * If the value of the request header [Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#origin) is present into the **list of allowed Origins** then: 253 | 254 | ```text 255 | Access-Control-Allow-Origin: [Value_Taken_From_The_List_Of_Allowed_Origins] 256 | Access-Control-Max-Age: 10 257 | Access-Control-Allow-Credentials: true 258 | ``` 259 | 260 | * Otherwise return an HTTP 403 **without any [CORS related HTTP response headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_response_headers)**. 261 | 262 | ### Test CORS configuration 263 | 264 | The tools [nuclei](https://github.com/projectdiscovery/nuclei) can be used, via the template named [cors-misconfig](https://github.com/projectdiscovery/nuclei-templates/blob/main/http/vulnerabilities/generic/cors-misconfig.yaml), to test a CORS configuration. 265 | 266 | 💻 Command to use: 267 | 268 | ```bash 269 | $ nuclei -silent -template-id cors-misconfig -u https://domain.com 270 | [cors-misconfig:arbitrary-origin] [http] [info] https://domain.com [...] 271 | ``` 272 | 273 | ### References 274 | 275 | * 276 | * 277 | * 278 | * 279 | * 280 | * 281 | * 282 | * [OWASP WSTG - Testing Cross Origin Resource Sharing](https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/11-Client-side_Testing/07-Testing_Cross_Origin_Resource_Sharing) 283 | * 284 | 285 | ## Prevent information disclosure via the browser local cached files 286 | 287 | This section describes why it is important to specify a **caching policy**, via the [Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) HTTP response header, when sensitive information is managed by a web-based application. 288 | 289 | ### Context 290 | 291 | 💬 The application allows a user to access to documents containing information, considered sensitive, from a confidentiality perspective. 292 | 293 | Let's assume that the application returns the following HTTP response to a request, in which, no caching policy is defined: 294 | 295 | ```text 296 | HTTP/1.1 200 OK 297 | accept-ranges: bytes 298 | content-length: 433994 299 | content-type: application/pdf 300 | date: Sat, 30 Mar 2024 10:06:34 GMT 301 | server: LiteSpeed 302 | ``` 303 | 304 | So, the browser will store a copy of the file (here a PDF one) into its cache storage for a certain amount of time. 305 | 306 | 🚩 **Consequence:** Any application running on the user's computer, will be able to access to the file (at least if executed as the user identity or an administrator one) causing an exposure of the resource to non-expected entities. 307 | 308 | 📺 This [demonstration video](assets/misc/demo_information_disclosure_via_browser_file_caching.mp4) show an example, of such information disclosure, via a file cached by the browser. 309 | 310 | ### Configuration proposal 311 | 312 | 💻 To prevent such issue, the following **caching policy** can be specified: 313 | 314 | ```text 315 | Cache-Control: no-store, max-age=0 316 | ``` 317 | 318 | 👀 Where: 319 | 320 | * `no-store`: Is used to indicate that the response may not be stored in any cache. 321 | * `max-age=0`: Is used to force the expiration of any cached version of the resources associated to the response. 322 | 323 | 💡 The HTTP response header [Clear-Site-Data](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data) can also be leveraged, in addition, to instruct the browser to remove any cached data related to the application. 324 | 325 | ### Other vulnerabilities related to caching 326 | 327 | 📖 An excellent research and course content, about [web cache poisoning](https://portswigger.net/web-security/web-cache-poisoning), is provided by the [PortSwigger team](https://portswigger.net/). 328 | 329 | ### References 330 | 331 | * 332 | * 333 | * 334 | * 335 | * 336 | * 337 | * [OWASP WSTG - Testing for Browser Cache Weaknesses](https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/04-Authentication_Testing/06-Testing_for_Browser_Cache_Weaknesses) 338 | * 339 | * 340 | * 341 | * 342 | 343 | ## Prevent CSP bypasses 344 | 345 | This section describes some points, to keep in mind, during the creation of a [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (called **CSP**) policy to prevent introducing bypasses. 346 | 347 | 🚩 Not every **[directives](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy#directives)** fallback to the **[default-src](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/default-src)** directive when it is not specified in the CSP policy. 348 | 349 | ### Directive form-action 350 | 351 | 👀 It is the case for the **[form-action](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/form-action)** directive. Therefore, an html form can be used to bypass the CSP in place when the `form-action` is not defined. 352 | 353 | 📺 This [demonstration video](assets/misc/demo_csp_bypass_due_to_no_form_action_directive.mp4) show an example. 354 | 355 | 💡 Therefore, ensure to always specify the `form-action` directive in a CSP policy to at least, the `'self'` value, to allow forms only on the current domain. 356 | 357 | ### Directive frame-ancestors 358 | 359 | 👀 It is the case for the **[frame-ancestors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors)** directive. Therefore, IF it is not defined **AND** IF the header [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) is not/incorrectly specified then the current domain can be embedded into a frame. 360 | 361 | 📺 This [demonstration video](assets/misc/demo_csp_bypass_due_to_no_frame_ancestors_directive.mp4) show an example. 362 | 363 | 💡 Therefore, ensure to always specify the `frame-ancestors` directive in a CSP policy to at least, the `'none'` value, to deny the current domain to be "framed". 364 | 365 | ### Directive base-uri 366 | 367 | 👀 It is the case for the **[base-uri](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/base-uri)** directive. 368 | 369 | 📺 This [demonstration video](assets/misc/demo_csp_bypass_due_to_no_base_uri_directive.mp4) show an example. 370 | 371 | 💡 Therefore, ensure to always specify the `base-uri` directive in a CSP policy to at least, the `'self'` value, to only allow the current domain to be specified as the document's base URI via a `` html tag. 372 | 373 | ## Support for a large CSP policy 374 | 375 | Tests were performed to identify if any limitation was in place, regarding the definition and usage of a large CSP policy. Tests were performed against the following browsers: 376 | 377 | * Firefox `132.0.2`. 378 | * Chromium `131.0.6755.0`. 379 | * Edge `131.0.2903.51`. 380 | 381 | 💡 Based on tests performed, modern browsers supports a sufficient size to specify a large CSP policy in case of need. 382 | 383 | 📊 Technical details can be found [here](https://github.com/oshp/oshp-tracking/discussions/29) ([backup copy](assets/misc/backup_discussions_29.pdf)). 384 | -------------------------------------------------------------------------------- /tab_browsersupport.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: BrowserSupport 3 | displaytext: Browser Support 4 | layout: null 5 | tab: true 6 | order: 2 7 | tags: headers 8 | --- 9 | 10 | # Browser Support 11 | 12 | 📚 A reference is provided, for each header, to a site always providing up-to-date information. 13 | 14 | | Feature | Reference | 15 | | ---------------------------------------------|------------| 16 | | HTTP Strict Transport Security (HSTS) | | 17 | | X-Frame-Options | | 18 | | X-Content-Type-Options | | 19 | | Content-Security-Policy | | 20 | | X-Permitted-Cross-Domain-Policies | [https://www.adobe.com/devnet-docs/acrobatetk/tools/AppSec/xdomain.html](https://www.adobe.com/devnet-docs/acrobatetk/tools/AppSec/xdomain.html "SKIP_VALIDATION") | 21 | | Referrer-Policy | | 22 | | Feature-Policy | | 23 | | HTTP Public-Key-Pins (HPKP) | | 24 | | Expect-CT | | 25 | | X-XSS-Protection | | 26 | | Clear-Site-Data | | 27 | | Cross-Origin-Embedder-Policy (COEP) | | 28 | | Cross-Origin-Opener-Policy (COOP) | | 29 | | Cross-Origin-Resource-Policy (CORP) | | 30 | | Permissions Policy | | 31 | | Cache-Control | | 32 | | Pragma | | 33 | -------------------------------------------------------------------------------- /tab_casestudies.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: casestudies 3 | displaytext: Case Studies 4 | layout: null 5 | tab: true 6 | order: 8 7 | tags: headers 8 | --- 9 | 10 | # Case Studies 11 | 12 | 📋 This section list the entities referencing the [OWASP Secure Headers Project](https://owasp.org/www-project-secure-headers/). 13 | 14 | 📩 Feel free to contact project leaders if your company or software (open source or not) was using the OSHP project. 15 | 16 | 🔎 **Google dork** used to identity references was `allintext:"OWASP Secure Headers Project" -site:owasp.org -site:github.com -site:youtube.com -site:twitter.com -site:linkedin.com`. 17 | 18 | ## How to create a link to the OSHP site? 19 | 20 | 🌎 Use the following absolute URL syntax to create a reference link to a specific location of the OSHP site. The presence of the `index.html` file is mandatory otherwise any anchor specified is not effective: 21 | 22 | ```text 23 | https://owasp.org/www-project-secure-headers/index.html#[TAB-ID]_[HEADER-ID-INSIDE-TAB] 24 | ``` 25 | 26 | 💡 Where: 27 | 28 | * `[TAB-ID]`: ID of the tab that can be obtained with the link to it. 29 | * `[HEADER-ID-INSIDE-TAB]`: Href value of the link to the section inside the tab. 30 | 31 | 👀 Example of reference link to the section about the header **Clear-Site-Data** in the **Response Headers** tab: 32 | 33 | ```text 34 | https://owasp.org/www-project-secure-headers/index.html#div-headers_clear-site-data 35 | ``` 36 | 37 | 👀 Example of reference link to the section **PHP** in the **Technical Resources** tab: 38 | 39 | ```text 40 | https://owasp.org/www-project-secure-headers/index.html#div-technical_php 41 | ``` 42 | 43 | ## Companies and other legal entities 44 | 45 | * [Cloud.gov](https://cloud.gov/docs/management/headers/). 46 | * [Salesforce](https://help.salesforce.com/s/articleView?language=en_US&id=cc.b2c_declarative_security_via_http_headers.htm&type=5). 47 | * [Black Hills Information Security](https://www.blackhillsinfosec.com/fixing-content-security-policies-with-cloudflare-workers/). 48 | * [Progress](https://www.progress.com/documentation/sitefinity-cms/110/predefined-security-headers-in-http-response). 49 | * [Bloomreach](https://documentation.bloomreach.com/14/library/concepts/security/configure-security-response-headers.html). 50 | * [Tableau](https://help.tableau.com/current/server-linux/en-us/security_http_headers.htm). 51 | * [42Crunch](https://docs.42crunch.com/latest/content/extras/protection_security_headers.htm). 52 | * [SAP](https://help.sap.com/docs/SAP_UPSCALE_COMMERCE/4620dd88ff9047c89ffb7fa897207a46/30af09ca9e394505a85661fa530d1263.html). 53 | * [SecureAuth](https://docs.secureauth.com/2104/en/identity-platform-http-security-header-best-practices.html). 54 | * [Detectify](https://support.detectify.com/support/solutions/articles/48001048949-https-stripping). 55 | * [ImmuniWeb](https://www.immuniweb.com/websec/about). 56 | * [Zoom](https://developers.zoom.us/docs/zoom-apps/security/owasp/). 57 | * [NexusGuard](https://blog.nexusguard.com/hardening-web-applications-using-secure-http-headers). 58 | * [VeraCode](https://docs.veracode.com/r/enable-security-headers). 59 | * [Ivanti](https://forums.ivanti.com/s/article/HTTP-Security-Headers-X-Frame-Options-X-XSS-Protection-X-Content-Type-Options). 60 | * [Government Technology Agency of Singapore GovTech](https://info.standards.tech.gov.sg/control-catalog/as/). 61 | * [HCL Volt MX](https://opensource.hcltechsw.com/volt-mx-docs/docs/documentation/Foundry/voltmx_Foundry_deployment_guide/Content/Hardening_Guide.html). 62 | * [Missouri State Website](https://assets.mo.gov/samples/security/security-headers.html). 63 | * [SAS](https://documentation.sas.com/doc/en/sasadmincdc/v_054/viyaov/p0i3vcgjpciz45n1of1v4vkffwbn.htm). 64 | * [RedHat](https://www.redhat.com/en/blog/creating-web-application-firewall-red-hat-openshift). 65 | * [Sages](https://www.sages.io/blog/headlines-safety-review-actual-recommendations). 66 | 67 | ## Software 68 | 69 | * [Nmap](https://github.com/nmap/nmap/blob/master/scripts/http-security-headers.nse). 70 | * [Spring Security](https://docs.spring.io/spring-security/reference/features/exploits/headers.html). 71 | -------------------------------------------------------------------------------- /tab_codesnippets.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: codesnippets 3 | displaytext: Code Snippets 4 | layout: null 5 | tab: true 6 | order: 5 7 | tags: headers 8 | --- 9 | 10 | # Code Snippets 11 | 12 | 🧾 The following code collection provides various code snippets to make working with HTTP security headers easier. 13 | 14 | * [Convert a Permissions-Policy back to Feature-Policy](#convert-a-permissions-policy-back-to-feature-policy) 15 | * [Test locally a Content-Security-Policy for weaknesses](#test-locally-a-content-security-policy-for-weaknesses) 16 | * [Generate configuration code using the OSHP headers reference files](#generate-configuration-code-using-the-oshp-headers-reference-files) 17 | * [Quickly check security HTTP headers](#quickly-check-security-http-headers) 18 | * [Syntax for adding HTTP response headers on different web servers](#syntax-for-adding-http-response-headers-on-different-web-servers) 19 | 20 | ## Convert a Permissions-Policy back to Feature-Policy 21 | 22 | As the [Permissions-Policy](https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md) header is still in development and is [not yet well supported](https://caniuse.com/permissions-policy), it can be interesting to use the two formats to increase the coverage of browsers according to their support level for **Permissions-Policy** and **[Feature-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy)** policy headers. 23 | 24 | The following _python3_ code snippet can be useful to achieve such conversion. 25 | 26 | 📑 Source for the conversion rules was this [one](https://github.com/w3c/webappsec-permissions-policy/blob/main/permissions-policy-explainer.md#appendix-big-changes-since-this-was-called-feature-policy). 27 | 28 | 💻 Code snippet: 29 | 30 | ```python 31 | permissions_policy = 'fullscreen=(), geolocation=(self "https://game.com" "https://map.example.com"), gyroscope=(self), usb=*' 32 | feature_policy_directives = [] 33 | # Collect directives 34 | permissions_policy_directives = permissions_policy.split(",") 35 | # Process each directive 36 | for permissions_policy_directive in permissions_policy_directives: 37 | # Remove leading and trailing spaces 38 | directive = permissions_policy_directive.strip(" ") 39 | # Remove all double quotes 40 | directive = directive.replace("\"", "") 41 | # Replace disabling expression () by the corresponding one in Feature-Policy 42 | directive = directive.replace("()", "'none'") 43 | # Quote keywords: self 44 | directive = directive.replace("self", "'self'") 45 | # Replace the equals affectation character by a space 46 | directive = directive.replace("=", " ") 47 | # Remove parenthesis 48 | directive = directive.replace("(", "").replace(")", "") 49 | # Add the current directive to the collection 50 | feature_policy_directives.append(directive) 51 | # Convert the collection of directives to a string with ; as directives separator 52 | feature_policy = "; ".join(feature_policy_directives) 53 | print(feature_policy) 54 | ``` 55 | 56 | 💻 Execution example: 57 | 58 | ```shell 59 | $ python code.py 60 | fullscreen 'none'; geolocation 'self' https://game.com https://map.example.com; gyroscope 'self'; usb * 61 | ``` 62 | 63 | ## Test locally a Content-Security-Policy for weaknesses 64 | 65 | It can be interesting to validate locally a **Content-Security-Policy** for presence of weaknesses prior to apply it on deployed web applications. 66 | 67 | The following _JavaScript_ code snippet can be useful to achieve such validation by leveraging the [csp-evaluator](https://github.com/google/csp-evaluator) NPM module provided by Google. 68 | 69 | 💻 Code snippet: 70 | 71 | ```javascript 72 | import {CspEvaluator} from "csp_evaluator/dist/evaluator.js"; 73 | import {CspParser} from "csp_evaluator/dist/parser.js"; 74 | 75 | const args = process.argv.slice(2) 76 | if(args.length == 0){ 77 | console.error("[!] Missing CSP!"); 78 | }else{ 79 | const csp = args[0] 80 | console.info(`[+] CSP to evaluate:\n${csp}`); 81 | const parsed = new CspParser(csp).csp; 82 | console.info(`[+] Evaluation results:`); 83 | const results = new CspEvaluator(parsed).evaluate(); 84 | results.forEach(elt => { 85 | let info = `[Directive '${elt.directive}' - Severity ${elt.severity}]: ${elt.description}`; 86 | console.info(info); 87 | }); 88 | } 89 | ``` 90 | 91 | 💻 Execution example: 92 | 93 | ```shell 94 | $ node code.js "default-src 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests" 95 | [+] CSP to evaluate: 96 | default-src 'self'; object-src 'none'; frame-ancestors 'none'; upgrade-insecure-requests 97 | [+] Evaluation results: 98 | [Directive 'default-src' - Severity 50]: 'self' can be problematic if you host JSONP, Angular or user uploaded files. 99 | ``` 100 | 101 | ## Generate configuration code using the OSHP headers reference files 102 | 103 | The following _bash_ code snippet, leveraging [jq](https://stedolan.github.io/jq/), can be used to generate configuration code using the OSHP headers reference files. 104 | 105 | 💻 Code snippet and execution example: 106 | 107 | ```shell 108 | # Generate the Nginx collection of instructions to add the recommended HTTP response headers 109 | $ curl -sk https://owasp.org/www-project-secure-headers/ci/headers_add.json | jq -r '.headers[] | "add_header \(.name) \(.value);"' 110 | add_header Cache-Control no-store, max-age=0; 111 | add_header Clear-Site-Data "cache","cookies","storage"; 112 | add_header Cross-Origin-Embedder-Policy require-corp; 113 | ... 114 | ``` 115 | 116 | ## Quickly check security HTTP headers 117 | 118 | The portable cross-platform tool [Venom](https://github.com/ovh/venom) with the dedicated [OSHP Validator test suites aligned with the OWASP Secure Headers Project](https://github.com/oshp/oshp-validator) can be used. 119 | 120 | 💻 Use the following example set of commands: 121 | 122 | ```shell 123 | $ venom run --var="target_site=https://mozilla.org" --var="logout_url=/logout" tests_suite.yml 124 | • HTTP security response headers test suites 125 | • Strict-Transport-Security SUCCESS 126 | • X-Frame-Options SUCCESS 127 | • X-Content-Type-Options SUCCESS 128 | • Content-Security-Policy FAILURE 129 | • X-Permitted-Cross-Domain-Policies SUCCESS 130 | • Referrer-Policy SUCCESS 131 | • Clear-Site-Data SUCCESS 132 | • Cross-Origin-Embedder-Policy SUCCESS 133 | • Cross-Origin-Opener-Policy SUCCESS 134 | • Cross-Origin-Resource-Policy SUCCESS 135 | • Permissions-Policy SUCCESS 136 | • Cache-Control SUCCESS 137 | • Feature-Policy SUCCESS 138 | [info] This header was split into Permissions-Policy and Document-Policy and will be considered deprecated once all impacted features are moved off of feature policy. 139 | • Public-Key-Pins SUCCESS 140 | [info] This header has been deprecated by all major browsers and is no longer recommended. Avoid using it, and update existing code if possible! 141 | • Expect-CT SUCCESS 142 | [info] This header will likely become obsolete in June 2021. Since May 2018 new certificates are expected to support SCTs by default. Certificates before March 2018 were allowed to have a lifetime of 39 months, those will all be expired in June 2021. 143 | • X-Xss-Protection SUCCESS 144 | [info] The X-XSS-Protection header has been deprecated by modern browsers and its use can introduce additional security issues on the client side. 145 | ``` 146 | 147 | ## Syntax for adding HTTP response headers on different web servers 148 | 149 | ### Apache 150 | 151 | 💻 Directive: 152 | 153 | `Header always set [HEADER_NAME] [PROPOSED_VALUE]` 154 | 155 | 🌎 References: 156 | 157 | * 158 | 159 | ### Nginx 160 | 161 | 💻 Directive: 162 | 163 | `add_header [HEADER_NAME] [PROPOSED_VALUE] always;` 164 | 165 | 🌎 References: 166 | 167 | * 168 | 169 | ### Lighttpd 170 | 171 | 💻 Directive: 172 | 173 | `setenv.add-response-header = ("[HEADER_NAME]" => "[PROPOSED_VALUE]")` 174 | 175 | 🌎 References: 176 | 177 | * 178 | 179 | ### IIS 180 | 181 | 💻 Directive: 182 | 183 | `` 184 | 185 | 🌎 References: 186 | 187 | * 188 | -------------------------------------------------------------------------------- /tab_headers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: headers 3 | displaytext: Response Headers 4 | layout: null 5 | tab: true 6 | order: 1 7 | tags: headers 8 | --- 9 | 10 | # Response Headers 11 | 12 | 💡 The collection of HTTP response security headers mentioned in this section is applicable when the [user agent](https://developer.mozilla.org/en-US/docs/Glossary/User_agent) processing the HTTP response is a browser. The support for these headers by non-browser API clients (user agent), like for example an HTTP client in a programming language, **is not standardized**. So, it requires specific testing to identify if an HTTP response security header is supported or not by such HTTP client. 13 | 14 | 🚦 Header lifecycle flow: 15 | 16 | ![Header lifecycle flow](assets/images/response_headers_header_lifecycle_flow.png) 17 | 18 | 📐 **Working draft** 19 | 20 | * [Permissions-Policy](#permissions-policy) 21 | 22 | ✅ **Active** 23 | 24 | * [Strict-Transport-Security](#strict-transport-security) 25 | * [X-Frame-Options](#x-frame-options) 26 | * [X-Content-Type-Options](#x-content-type-options) 27 | * [Content-Security-Policy](#content-security-policy) 28 | * [X-Permitted-Cross-Domain-Policies](#x-permitted-cross-domain-policies) 29 | * [Referrer-Policy](#referrer-policy) 30 | * [Clear-Site-Data](#clear-site-data) 31 | * [Cross-Origin-Embedder-Policy](#cross-origin-embedder-policy) 32 | * [Cross-Origin-Opener-Policy](#cross-origin-opener-policy) 33 | * [Cross-Origin-Resource-Policy](#cross-origin-resource-policy) 34 | * [Cache-Control](#cache-control) 35 | 36 | ⏰ **Almost deprecated** 37 | 38 | None 39 | 40 | ❌ **Deprecated** 41 | 42 | * [Feature-Policy](#feature-policy) 43 | * [Expect-CT](#expect-ct) 44 | * [Public-Key-Pins](#public-key-pins) 45 | * [X-XSS-Protection](#x-xss-protection) 46 | * [Pragma](#pragma) 47 | 48 | ## Strict-Transport-Security 49 | 50 | HTTP Strict Transport Security (also named *HSTS*) is a browser security policy mechanism which helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections, and within a defined timespan (max-age) not via the clear text HTTP protocol. HSTS is an IETF standard track protocol and is specified in [RFC 6797](https://www.rfc-editor.org/rfc/rfc6797). A server implements an HSTS policy by supplying a header (`Strict-Transport-Security`) over an HTTPS connection (HSTS headers over HTTP are ignored). 51 | 52 | 📍 Important note about the behavior of the header over a **HTTP connection** (source [Mozilla MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#description)): 53 | 54 | * The `Strict-Transport-Security` header **is ignored by the browser when your site has only been accessed using HTTP**. 55 | * Once your site is accessed over HTTPS **with no certificate errors**, the browser knows your site is HTTPS capable and will honor the `Strict-Transport-Security` header. 56 | 57 | 💡 If you need to let the access open, via HTTP, to the web server but want to ensure that `Strict-Transport-Security` header is taken into account for your site then you can use the **[preload](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security#preloading_strict_transport_security)** directive. 58 | 59 | ### Values 60 | 61 | | Value | Description | 62 | |---------------------|-------------| 63 | | `max-age=SECONDS` | The time, in seconds, that the browser should remember that this site is only to be accessed using HTTPS. | 64 | | `includeSubDomains` | If this optional parameter is specified, this rule applies to all of the site's subdomains as well. | 65 | | `preload` | If this optional parameter is specified, its instruct the browser to always access the site using HTTPS because the site is included into `Strict-Transport-Security` [preload list](https://www.chromium.org/hsts/). | 66 | 67 | ### Example 68 | 69 | ``` 70 | Strict-Transport-Security: max-age=31536000 71 | ``` 72 | 73 | ``` 74 | Strict-Transport-Security: max-age=31536000 ; includeSubDomains 75 | ``` 76 | 77 | ``` 78 | Strict-Transport-Security: max-age=31536000 ; includeSubDomains ; preload 79 | ``` 80 | 81 | ### References 82 | 83 | * 84 | * 85 | * 86 | * 87 | * 88 | * 89 | * 90 | * 91 | * 92 | 93 | ## X-Frame-Options 94 | 95 | The `X-Frame-Options` response header (also named **XFO**) improves the protection of web applications against [clickjacking](https://portswigger.net/web-security/clickjacking). It instructs the browser whether the content can be displayed within frames. 96 | 97 | The Content-Security-Policy (CSP) [frame-ancestors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors) directive obsoletes the X-Frame-Options header. If a resource has both policies, the CSP frame-ancestors policy will be enforced and the X-Frame-Options policy will be ignored. 98 | 99 | ### Values 100 | 101 | | Value | Description | 102 | |----------------------|-------------| 103 | | `deny` | No rendering within a frame. | 104 | | `sameorigin` | No rendering if origin mismatch. | 105 | | `allow-from: DOMAIN` | Allows rendering if framed by frame loaded from *DOMAIN* (**not supported by modern browsers**). | 106 | 107 | ### Example 108 | 109 | ``` 110 | X-Frame-Options: deny 111 | ``` 112 | 113 | ### References 114 | 115 | * 116 | * 117 | * 118 | * 119 | * 120 | * 121 | * 122 | 123 | ## X-Content-Type-Options 124 | 125 | Setting this header will prevent the browser from interpreting files as a different MIME type to what is specified in the `Content-Type` HTTP header (e.g. treating `text/plain` as `text/css`). 126 | 127 | ### Values 128 | 129 | | Value | Description | 130 | |-----------|-------------| 131 | | `nosniff` | Will prevent the browser from MIME-sniffing a response away from the declared content-type. | 132 | 133 | ### Example 134 | 135 | ``` 136 | X-Content-Type-Options: nosniff 137 | ``` 138 | 139 | ### References 140 | 141 | * 142 | * 143 | * 144 | 145 | ## Content-Security-Policy 146 | 147 | A Content Security Policy (also named CSP) requires careful tuning and testing after definition of the policy. A content security policy can have significant impact on the way browsers render pages (e.g., inline JavaScript or CSS can disabled). A proper CSP can prevents a wide range of attacks, including cross-site scripting, other cross-site injections and click jacking. 148 | 149 | ### Values 150 | 151 | | Directive | Description | 152 | |-----------------------------|-------------| 153 | | `base-uri` | Define the base URI for relative URIs. | 154 | | `default-src` | Define loading policy for all resources type in case a resource type's dedicated directive is not defined (fallback). | 155 | | `script-src` | Define which scripts the protected resource can execute. | 156 | | `object-src` | Define from where the protected resource can load plugins. | 157 | | `style-src` | Define which styles (CSS) can be applied to the protected resource. | 158 | | `img-src` | Define from where the protected resource can load images. | 159 | | `media-src` | Define from where the protected resource can load video and audio. | 160 | | `frame-src` | *(Deprecated and replaced by `child-src`)* Define from where the protected resource can embed frames. | 161 | | `child-src` | Define from where the protected resource can embed frames. | 162 | | `frame-ancestors` | Define from where the protected resource can be embedded in frames. Useful against [click jacking](https://owasp.org/www-community/attacks/Clickjacking) | 163 | | `font-src` | Define from where the protected resource can load fonts. | 164 | | `connect-src` | Define which URIs the protected resource can load using script interfaces. | 165 | | `manifest-src` | Define from where the protected resource can load manifests. | 166 | | `form-action` | Define which URIs can be used as the action of HTML form elements. | 167 | | `sandbox` | Specifies an HTML sandbox policy that the user agent applies to the protected resource. | 168 | | `script-nonce` | Define script execution by requiring the presence of the specified nonce on script elements. | 169 | | `plugin-types` | Define the set of plugins that can be invoked by the protected resource by limiting the types of resources that can be embedded. | 170 | | `reflected-xss` | Instruct the user agent to activate or deactivate any heuristics used to filter or block reflected cross-site scripting attacks, equivalent to the effects of the non-standard `X-XSS-Protection` header. | 171 | | `block-all-mixed-content` | *(Deprecated)* Prevent the user agent from loading mixed content. | 172 | | `upgrade-insecure-requests` | Instruct the user agent to using HTTPS when trying to download insecure HTTP resources | 173 | | `referrer` | *(Deprecated)* Define information the user agent can send in the `Referer` header. | 174 | | `report-uri` | *(Deprecated and replaced by `report-to`)* Specifies a URI to which the user agent sends reports about policy violation. | 175 | | `report-to` | Specifies a group (defined in the `Report-To` header) to which the user agent sends reports about policy violation. | 176 | 177 | ### Example 178 | 179 | ``` 180 | Content-Security-Policy: script-src 'self' 181 | ``` 182 | 183 | ### References 184 | 185 | * 186 | * 187 | * 188 | * 189 | * 190 | * 191 | * 192 | * 193 | 194 | ## X-Permitted-Cross-Domain-Policies 195 | 196 | A cross-domain policy file is an XML document that grants a web client, such as Adobe Flash Player or Adobe Acrobat (though not necessarily limited to these), permission to handle data across domains. When clients request content hosted on a particular source domain and that content makes requests directed towards a domain other than its own, the remote domain needs to host a cross-domain policy file that grants access to the source domain, allowing the client to continue the transaction. Normally a meta-policy is declared in the master policy file, but for those who can’t write to the root directory, they can also declare a meta-policy using the `X-Permitted-Cross-Domain-Policies` HTTP response header. 197 | 198 | ### Values 199 | 200 | | Value | Description | 201 | |-------------------|-------------| 202 | | `none` | No policy files are allowed anywhere on the target server, including this master policy file. | 203 | | `master-only` | Only this master policy file is allowed. | 204 | | `by-content-type` | [HTTP/HTTPS only] Only policy files served with Content-Type: text/x-cross-domain-policy are allowed. | 205 | | `by-ftp-filename` | [FTP only] Only policy files whose file names are crossdomain.xml (i.e. URLs ending in /crossdomain.xml) are allowed. | 206 | | `all` | All policy files on this target domain are allowed. | 207 | 208 | ### Example 209 | 210 | ``` 211 | X-Permitted-Cross-Domain-Policies: none 212 | ``` 213 | 214 | ### References 215 | 216 | * [https://www.adobe.com/devnet-docs/acrobatetk/tools/AppSec/xdomain.html](https://www.adobe.com/devnet-docs/acrobatetk/tools/AppSec/xdomain.html "SKIP_VALIDATION") 217 | * 218 | * 219 | 220 | ## Referrer-Policy 221 | 222 | The `Referrer-Policy` HTTP header governs which referrer information, sent in the `Referer` header, should be included with requests made. 223 | 224 | ### Values 225 | 226 | | Value | Description | 227 | |-----------------------------------|-------------| 228 | | `no-referrer` | The `Referer` header will be omitted entirely. No referrer information is sent along with requests. | 229 | | `no-referrer-when-downgrade` | This is the user agent's default behavior if no policy is specified. The origin is sent as referrer to a-priori as-much-secure destination (HTTPS → HTTPS), but isn't sent to a less secure destination (HTTPS → HTTP). | 230 | | `origin` | Only send the origin of the document as the referrer in all cases. (e.g. the document `https://example.com/page.html` will send the referrer `https://example.com/`.) | 231 | | `origin-when-cross-origin` | Send a full URL when performing a same-origin request, but only send the origin of the document for other cases. | 232 | | `same-origin` | A referrer will be sent for same-site origins, but cross-origin requests will contain no referrer information. | 233 | | `strict-origin` | Only send the origin of the document as the referrer to a-priori as-much-secure destination (HTTPS → HTTPS), but don't send it to a less secure destination (HTTPS → HTTP). | 234 | | `strict-origin-when-cross-origin` | Send a full URL when performing a same-origin request, only send the origin of the document to a-priori as-much-secure destination (HTTPS → HTTPS), and send no header to a less secure destination (HTTPS → HTTP). | 235 | | `unsafe-url` | Send a full URL (stripped from parameters) when performing a same-origin or cross-origin request. | 236 | 237 | ### Example 238 | 239 | ``` 240 | Referrer-Policy: no-referrer 241 | ``` 242 | 243 | ### References 244 | 245 | * 246 | * 247 | 248 | ## Clear-Site-Data 249 | 250 | The Clear-Site-Data header clears browsing data (cookies, storage, cache) associated with the requesting website. It allows web developers to have more control over the data stored locally by a browser for their origins (source [Mozilla MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data)). This header is useful for example, during a logout process, in order to ensure that all stored content on the client side like cookies, storage and cache are removed. 251 | 252 | ### Values 253 | 254 | | Value | Description | 255 | |---------------------|-------------| 256 | | `"cache"` | Indicates that the server wishes to remove locally cached data for the origin of the response URL. | 257 | | `"cookies"` | Indicates that the server wishes to remove all cookies for the origin of the response URL. HTTP authentication credentials are also cleared out. This affects the entire registered domain, including subdomains. | 258 | | `"storage"` | Indicates that the server wishes to remove all DOM storage for the origin of the response URL. | 259 | | `"executionContexts"` | Indicates that the server wishes to reload all browsing contexts for the origin of the response. Currently, this value is only supported by a small subset of browsers. | 260 | | `"*"` | Indicates that the server wishes to clear all types of data for the origin of the response. If more data types are added in future versions of this header, they will also be covered by it. | 261 | 262 | ### Example 263 | 264 | ``` 265 | Clear-Site-Data: "cache","cookies","storage" 266 | ``` 267 | 268 | ### References 269 | 270 | * 271 | * 272 | * 273 | * 274 | * 275 | 276 | ## Cross-Origin-Resource-Policy 277 | 278 | This response header (also named CORP) allows to define a policy that lets web sites and applications opt in to protection against certain requests from other origins (such as those issued with elements like `