├── .appveyor.yml ├── .gitattributes ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── .vsts-pipelines └── builds │ ├── ci-internal.yml │ └── ci-public.yml ├── Antiforgery.sln ├── CONTRIBUTING.md ├── Directory.Build.props ├── Directory.Build.targets ├── LICENSE.txt ├── NuGet.config ├── NuGetPackageVerifier.json ├── README.md ├── build.cmd ├── build.sh ├── build ├── Key.snk ├── dependencies.props ├── repo.props └── sources.props ├── korebuild-lock.txt ├── korebuild.json ├── run.cmd ├── run.ps1 ├── run.sh ├── src └── Microsoft.AspNetCore.Antiforgery │ ├── AntiforgeryOptions.cs │ ├── AntiforgeryServiceCollectionExtensions.cs │ ├── AntiforgeryTokenSet.cs │ ├── AntiforgeryValidationException.cs │ ├── IAntiforgery.cs │ ├── IAntiforgeryAdditionalDataProvider.cs │ ├── Internal │ ├── AntiforgeryFeature.cs │ ├── AntiforgeryLoggerExtensions.cs │ ├── AntiforgeryOptionsSetup.cs │ ├── AntiforgerySerializationContext.cs │ ├── AntiforgerySerializationContextPooledObjectPolicy.cs │ ├── AntiforgeryToken.cs │ ├── BinaryBlob.cs │ ├── CryptographyAlgorithms.cs │ ├── DefaultAntiforgery.cs │ ├── DefaultAntiforgeryAdditionalDataProvider.cs │ ├── DefaultAntiforgeryTokenGenerator.cs │ ├── DefaultAntiforgeryTokenSerializer.cs │ ├── DefaultAntiforgeryTokenStore.cs │ ├── DefaultClaimUidExtractor.cs │ ├── IAntiforgeryFeature.cs │ ├── IAntiforgeryTokenGenerator.cs │ ├── IAntiforgeryTokenSerializer.cs │ ├── IAntiforgeryTokenStore.cs │ └── IClaimUidExtractor.cs │ ├── Microsoft.AspNetCore.Antiforgery.csproj │ ├── Properties │ ├── AssemblyInfo.cs │ └── Resources.Designer.cs │ ├── Resources.resx │ └── baseline.netcore.json ├── test ├── Directory.Build.props └── Microsoft.AspNetCore.Antiforgery.Test │ ├── Internal │ ├── AntiforgeryOptionsSetupTest.cs │ ├── AntiforgeryTokenTest.cs │ ├── BinaryBlobTest.cs │ ├── DefaultAntiforgeryTest.cs │ ├── DefaultAntiforgeryTokenGeneratorTest.cs │ ├── DefaultAntiforgeryTokenSerializerTest.cs │ ├── DefaultAntiforgeryTokenStoreTest.cs │ └── DefaultClaimUidExtractorTest.cs │ ├── Microsoft.AspNetCore.Antiforgery.Test.csproj │ └── TestOptionsManager.cs └── version.props /.appveyor.yml: -------------------------------------------------------------------------------- 1 | init: 2 | - git config --global core.autocrlf true 3 | branches: 4 | only: 5 | - master 6 | - /^release\/.*$/ 7 | - /^(.*\/)?ci-.*$/ 8 | build_script: 9 | - ps: .\run.ps1 default-build 10 | clone_depth: 1 11 | environment: 12 | global: 13 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 14 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 15 | test: 'off' 16 | deploy: 'off' 17 | os: Visual Studio 2017 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.doc diff=astextplain 2 | *.DOC diff=astextplain 3 | *.docx diff=astextplain 4 | *.DOCX diff=astextplain 5 | *.dot diff=astextplain 6 | *.DOT diff=astextplain 7 | *.pdf diff=astextplain 8 | *.PDF diff=astextplain 9 | *.rtf diff=astextplain 10 | *.RTF diff=astextplain 11 | 12 | *.jpg binary 13 | *.png binary 14 | *.gif binary 15 | 16 | *.cs text=auto diff=csharp 17 | *.vb text=auto 18 | *.resx text=auto 19 | *.c text=auto 20 | *.cpp text=auto 21 | *.cxx text=auto 22 | *.h text=auto 23 | *.hxx text=auto 24 | *.py text=auto 25 | *.rb text=auto 26 | *.java text=auto 27 | *.html text=auto 28 | *.htm text=auto 29 | *.css text=auto 30 | *.scss text=auto 31 | *.sass text=auto 32 | *.less text=auto 33 | *.js text=auto 34 | *.lisp text=auto 35 | *.clj text=auto 36 | *.sql text=auto 37 | *.php text=auto 38 | *.lua text=auto 39 | *.m text=auto 40 | *.asm text=auto 41 | *.erl text=auto 42 | *.fs text=auto 43 | *.fsx text=auto 44 | *.hs text=auto 45 | 46 | *.csproj text=auto 47 | *.vbproj text=auto 48 | *.fsproj text=auto 49 | *.dbproj text=auto 50 | *.sln text=auto eol=crlf 51 | *.sh eol=lf 52 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | THIS ISSUE TRACKER IS CLOSED - please log new issues here: https://github.com/aspnet/Home/issues 2 | 3 | For information about this change, see https://github.com/aspnet/Announcements/issues/283 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | [Oo]bj/ 2 | [Bb]in/ 3 | TestResults/ 4 | .nuget/ 5 | *.sln.ide/ 6 | _ReSharper.*/ 7 | packages/ 8 | artifacts/ 9 | PublishProfiles/ 10 | .vs/ 11 | bower_components/ 12 | node_modules/ 13 | **/wwwroot/lib/ 14 | debugSettings.json 15 | project.lock.json 16 | *.user 17 | *.suo 18 | *.cache 19 | *.docstates 20 | _ReSharper.* 21 | nuget.exe 22 | *net45.csproj 23 | *net451.csproj 24 | *k10.csproj 25 | *.psess 26 | *.vsp 27 | *.pidb 28 | *.userprefs 29 | *DS_Store 30 | *.ncrunchsolution 31 | *.*sdf 32 | *.ipch 33 | .settings 34 | *.sln.ide 35 | node_modules 36 | **/[Cc]ompiler/[Rr]esources/**/*.js 37 | *launchSettings.json 38 | .build/ 39 | .testPublish/ 40 | global.json 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | sudo: false 3 | dist: trusty 4 | env: 5 | global: 6 | - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true 7 | - DOTNET_CLI_TELEMETRY_OPTOUT: 1 8 | mono: none 9 | os: 10 | - linux 11 | - osx 12 | osx_image: xcode8.2 13 | addons: 14 | apt: 15 | packages: 16 | - libunwind8 17 | branches: 18 | only: 19 | - master 20 | - /^release\/.*$/ 21 | - /^(.*\/)?ci-.*$/ 22 | before_install: 23 | - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s 24 | /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib 25 | /usr/local/lib/; fi 26 | script: 27 | - ./build.sh 28 | -------------------------------------------------------------------------------- /.vsts-pipelines/builds/ci-internal.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | - release/* 4 | 5 | resources: 6 | repositories: 7 | - repository: buildtools 8 | type: git 9 | name: aspnet-BuildTools 10 | ref: refs/heads/master 11 | 12 | phases: 13 | - template: .vsts-pipelines/templates/project-ci.yml@buildtools 14 | -------------------------------------------------------------------------------- /.vsts-pipelines/builds/ci-public.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | - release/* 4 | 5 | # See https://github.com/aspnet/BuildTools 6 | resources: 7 | repositories: 8 | - repository: buildtools 9 | type: github 10 | endpoint: DotNet-Bot GitHub Connection 11 | name: aspnet/BuildTools 12 | ref: refs/heads/master 13 | 14 | phases: 15 | - template: .vsts-pipelines/templates/project-ci.yml@buildtools 16 | -------------------------------------------------------------------------------- /Antiforgery.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.26208.0 4 | MinimumVisualStudioVersion = 15.0.26730.03 5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{71D070C4-B325-48F7-9F25-DD4E91C2BBCA}" 6 | EndProject 7 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6EDD8B57-4DE8-4246-A6A3-47ECD92740B4}" 8 | EndProject 9 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Antiforgery", "src\Microsoft.AspNetCore.Antiforgery\Microsoft.AspNetCore.Antiforgery.csproj", "{46FB03FB-7A44-4106-BDDE-D6F5417544AB}" 10 | EndProject 11 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Antiforgery.Test", "test\Microsoft.AspNetCore.Antiforgery.Test\Microsoft.AspNetCore.Antiforgery.Test.csproj", "{415E83F8-6002-47E4-AA8E-CD5169C06F28}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {46FB03FB-7A44-4106-BDDE-D6F5417544AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {46FB03FB-7A44-4106-BDDE-D6F5417544AB}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {46FB03FB-7A44-4106-BDDE-D6F5417544AB}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {46FB03FB-7A44-4106-BDDE-D6F5417544AB}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {415E83F8-6002-47E4-AA8E-CD5169C06F28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {415E83F8-6002-47E4-AA8E-CD5169C06F28}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {415E83F8-6002-47E4-AA8E-CD5169C06F28}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {415E83F8-6002-47E4-AA8E-CD5169C06F28}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(SolutionProperties) = preSolution 29 | HideSolutionNode = FALSE 30 | EndGlobalSection 31 | GlobalSection(NestedProjects) = preSolution 32 | {46FB03FB-7A44-4106-BDDE-D6F5417544AB} = {71D070C4-B325-48F7-9F25-DD4E91C2BBCA} 33 | {415E83F8-6002-47E4-AA8E-CD5169C06F28} = {6EDD8B57-4DE8-4246-A6A3-47ECD92740B4} 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ====== 3 | 4 | Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo. 5 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Microsoft ASP.NET Core 12 | https://github.com/aspnet/Antiforgery 13 | git 14 | $(MSBuildThisFileDirectory) 15 | $(MSBuildThisFileDirectory)build\Key.snk 16 | true 17 | true 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | $(MicrosoftNETCoreApp20PackageVersion) 4 | $(MicrosoftNETCoreApp21PackageVersion) 5 | $(MicrosoftNETCoreApp22PackageVersion) 6 | $(NETStandardLibrary20PackageVersion) 7 | 8 | 99.9 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (c) .NET Foundation and Contributors 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NuGetPackageVerifier.json: -------------------------------------------------------------------------------- 1 | { 2 | "Default": { 3 | "rules": [ 4 | "DefaultCompositeRule" 5 | ] 6 | } 7 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Antiforgery [Archived] 2 | ====================== 3 | 4 | **This GitHub project has been archived.** Ongoing development on this project can be found in . 5 | 6 | Antiforgery system for generating secure tokens to prevent Cross-Site Request Forgery attacks. 7 | 8 | This project is part of ASP.NET Core. You can find documentation and getting started instructions for ASP.NET Core at the [AspNetCore](https://github.com/aspnet/AspNetCore) repo. 9 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' default-build %*; exit $LASTEXITCODE" 3 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | # Call "sync" between "chmod" and execution to prevent "text file busy" error in Docker (aufs) 7 | chmod +x "$DIR/run.sh"; sync 8 | "$DIR/run.sh" default-build "$@" 9 | -------------------------------------------------------------------------------- /build/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aspnet/Antiforgery/8124442320b6de41a89bd779dd1b82b5bb8131e7/build/Key.snk -------------------------------------------------------------------------------- /build/dependencies.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 4 | 5 | 6 | 3.0.0-alpha1-20180919.1 7 | 3.0.0-alpha1-10549 8 | 3.0.0-alpha1-10549 9 | 3.0.0-alpha1-10549 10 | 3.0.0-alpha1-10549 11 | 3.0.0-alpha1-10549 12 | 3.0.0-alpha1-10549 13 | 3.0.0-alpha1-10549 14 | 3.0.0-alpha1-10549 15 | 3.0.0-alpha1-10549 16 | 3.0.0-alpha1-10549 17 | 3.0.0-alpha1-10549 18 | 2.0.9 19 | 2.1.3 20 | 2.2.0-preview2-26905-02 21 | 15.6.1 22 | 4.9.0 23 | 2.0.3 24 | 2.3.1 25 | 2.4.0 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /build/repo.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Internal.AspNetCore.Universe.Lineup 6 | https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /build/sources.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(DotNetRestoreSources) 6 | 7 | $(RestoreSources); 8 | https://dotnet.myget.org/F/dotnet-core/api/v3/index.json; 9 | https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; 10 | https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json; 11 | 12 | 13 | $(RestoreSources); 14 | https://api.nuget.org/v3/index.json; 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /korebuild-lock.txt: -------------------------------------------------------------------------------- 1 | version:3.0.0-alpha1-20180919.1 2 | commithash:3066ae0a230870ea07e3f132605b5e5493f8bbd4 3 | -------------------------------------------------------------------------------- /korebuild.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json", 3 | "channel": "master" 4 | } 5 | -------------------------------------------------------------------------------- /run.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' %*; exit $LASTEXITCODE" 3 | -------------------------------------------------------------------------------- /run.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env powershell 2 | #requires -version 4 3 | 4 | <# 5 | .SYNOPSIS 6 | Executes KoreBuild commands. 7 | 8 | .DESCRIPTION 9 | Downloads korebuild if required. Then executes the KoreBuild command. To see available commands, execute with `-Command help`. 10 | 11 | .PARAMETER Command 12 | The KoreBuild command to run. 13 | 14 | .PARAMETER Path 15 | The folder to build. Defaults to the folder containing this script. 16 | 17 | .PARAMETER Channel 18 | The channel of KoreBuild to download. Overrides the value from the config file. 19 | 20 | .PARAMETER DotNetHome 21 | The directory where .NET Core tools will be stored. 22 | 23 | .PARAMETER ToolsSource 24 | The base url where build tools can be downloaded. Overrides the value from the config file. 25 | 26 | .PARAMETER Update 27 | Updates KoreBuild to the latest version even if a lock file is present. 28 | 29 | .PARAMETER Reinstall 30 | Re-installs KoreBuild 31 | 32 | .PARAMETER ConfigFile 33 | The path to the configuration file that stores values. Defaults to korebuild.json. 34 | 35 | .PARAMETER ToolsSourceSuffix 36 | The Suffix to append to the end of the ToolsSource. Useful for query strings in blob stores. 37 | 38 | .PARAMETER CI 39 | Sets up CI specific settings and variables. 40 | 41 | .PARAMETER Arguments 42 | Arguments to be passed to the command 43 | 44 | .NOTES 45 | This function will create a file $PSScriptRoot/korebuild-lock.txt. This lock file can be committed to source, but does not have to be. 46 | When the lockfile is not present, KoreBuild will create one using latest available version from $Channel. 47 | 48 | The $ConfigFile is expected to be an JSON file. It is optional, and the configuration values in it are optional as well. Any options set 49 | in the file are overridden by command line parameters. 50 | 51 | .EXAMPLE 52 | Example config file: 53 | ```json 54 | { 55 | "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json", 56 | "channel": "master", 57 | "toolsSource": "https://aspnetcore.blob.core.windows.net/buildtools" 58 | } 59 | ``` 60 | #> 61 | [CmdletBinding(PositionalBinding = $false)] 62 | param( 63 | [Parameter(Mandatory = $true, Position = 0)] 64 | [string]$Command, 65 | [string]$Path = $PSScriptRoot, 66 | [Alias('c')] 67 | [string]$Channel, 68 | [Alias('d')] 69 | [string]$DotNetHome, 70 | [Alias('s')] 71 | [string]$ToolsSource, 72 | [Alias('u')] 73 | [switch]$Update, 74 | [switch]$Reinstall, 75 | [string]$ToolsSourceSuffix, 76 | [string]$ConfigFile = $null, 77 | [switch]$CI, 78 | [Parameter(ValueFromRemainingArguments = $true)] 79 | [string[]]$Arguments 80 | ) 81 | 82 | Set-StrictMode -Version 2 83 | $ErrorActionPreference = 'Stop' 84 | 85 | # 86 | # Functions 87 | # 88 | 89 | function Get-KoreBuild { 90 | 91 | $lockFile = Join-Path $Path 'korebuild-lock.txt' 92 | 93 | if (!(Test-Path $lockFile) -or $Update) { 94 | Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile $ToolsSourceSuffix 95 | } 96 | 97 | $version = Get-Content $lockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1 98 | if (!$version) { 99 | Write-Error "Failed to parse version from $lockFile. Expected a line that begins with 'version:'" 100 | } 101 | $version = $version.TrimStart('version:').Trim() 102 | $korebuildPath = Join-Paths $DotNetHome ('buildtools', 'korebuild', $version) 103 | 104 | if ($Reinstall -and (Test-Path $korebuildPath)) { 105 | Remove-Item -Force -Recurse $korebuildPath 106 | } 107 | 108 | if (!(Test-Path $korebuildPath)) { 109 | Write-Host -ForegroundColor Magenta "Downloading KoreBuild $version" 110 | New-Item -ItemType Directory -Path $korebuildPath | Out-Null 111 | $remotePath = "$ToolsSource/korebuild/artifacts/$version/korebuild.$version.zip" 112 | 113 | try { 114 | $tmpfile = Join-Path ([IO.Path]::GetTempPath()) "KoreBuild-$([guid]::NewGuid()).zip" 115 | Get-RemoteFile $remotePath $tmpfile $ToolsSourceSuffix 116 | if (Get-Command -Name 'Microsoft.PowerShell.Archive\Expand-Archive' -ErrorAction Ignore) { 117 | # Use built-in commands where possible as they are cross-plat compatible 118 | Microsoft.PowerShell.Archive\Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath 119 | } 120 | else { 121 | # Fallback to old approach for old installations of PowerShell 122 | Add-Type -AssemblyName System.IO.Compression.FileSystem 123 | [System.IO.Compression.ZipFile]::ExtractToDirectory($tmpfile, $korebuildPath) 124 | } 125 | } 126 | catch { 127 | Remove-Item -Recurse -Force $korebuildPath -ErrorAction Ignore 128 | throw 129 | } 130 | finally { 131 | Remove-Item $tmpfile -ErrorAction Ignore 132 | } 133 | } 134 | 135 | return $korebuildPath 136 | } 137 | 138 | function Join-Paths([string]$path, [string[]]$childPaths) { 139 | $childPaths | ForEach-Object { $path = Join-Path $path $_ } 140 | return $path 141 | } 142 | 143 | function Get-RemoteFile([string]$RemotePath, [string]$LocalPath, [string]$RemoteSuffix) { 144 | if ($RemotePath -notlike 'http*') { 145 | Copy-Item $RemotePath $LocalPath 146 | return 147 | } 148 | 149 | $retries = 10 150 | while ($retries -gt 0) { 151 | $retries -= 1 152 | try { 153 | Invoke-WebRequest -UseBasicParsing -Uri $($RemotePath + $RemoteSuffix) -OutFile $LocalPath 154 | return 155 | } 156 | catch { 157 | Write-Verbose "Request failed. $retries retries remaining" 158 | } 159 | } 160 | 161 | Write-Error "Download failed: '$RemotePath'." 162 | } 163 | 164 | # 165 | # Main 166 | # 167 | 168 | # Load configuration or set defaults 169 | 170 | $Path = Resolve-Path $Path 171 | if (!$ConfigFile) { $ConfigFile = Join-Path $Path 'korebuild.json' } 172 | 173 | if (Test-Path $ConfigFile) { 174 | try { 175 | $config = Get-Content -Raw -Encoding UTF8 -Path $ConfigFile | ConvertFrom-Json 176 | if ($config) { 177 | if (!($Channel) -and (Get-Member -Name 'channel' -InputObject $config)) { [string] $Channel = $config.channel } 178 | if (!($ToolsSource) -and (Get-Member -Name 'toolsSource' -InputObject $config)) { [string] $ToolsSource = $config.toolsSource} 179 | } 180 | } 181 | catch { 182 | Write-Host -ForegroundColor Red $Error[0] 183 | Write-Error "$ConfigFile contains invalid JSON." 184 | exit 1 185 | } 186 | } 187 | 188 | if (!$DotNetHome) { 189 | $DotNetHome = if ($env:DOTNET_HOME) { $env:DOTNET_HOME } ` 190 | elseif ($env:USERPROFILE) { Join-Path $env:USERPROFILE '.dotnet'} ` 191 | elseif ($env:HOME) {Join-Path $env:HOME '.dotnet'}` 192 | else { Join-Path $PSScriptRoot '.dotnet'} 193 | } 194 | 195 | if (!$Channel) { $Channel = 'master' } 196 | if (!$ToolsSource) { $ToolsSource = 'https://aspnetcore.blob.core.windows.net/buildtools' } 197 | 198 | # Execute 199 | 200 | $korebuildPath = Get-KoreBuild 201 | Import-Module -Force -Scope Local (Join-Path $korebuildPath 'KoreBuild.psd1') 202 | 203 | try { 204 | Set-KoreBuildSettings -ToolsSource $ToolsSource -DotNetHome $DotNetHome -RepoPath $Path -ConfigFile $ConfigFile -CI:$CI 205 | Invoke-KoreBuildCommand $Command @Arguments 206 | } 207 | finally { 208 | Remove-Module 'KoreBuild' -ErrorAction Ignore 209 | } 210 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # 6 | # variables 7 | # 8 | 9 | RESET="\033[0m" 10 | RED="\033[0;31m" 11 | YELLOW="\033[0;33m" 12 | MAGENTA="\033[0;95m" 13 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 14 | [ -z "${DOTNET_HOME:-}" ] && DOTNET_HOME="$HOME/.dotnet" 15 | verbose=false 16 | update=false 17 | reinstall=false 18 | repo_path="$DIR" 19 | channel='' 20 | tools_source='' 21 | tools_source_suffix='' 22 | ci=false 23 | 24 | # 25 | # Functions 26 | # 27 | __usage() { 28 | echo "Usage: $(basename "${BASH_SOURCE[0]}") command [options] [[--] ...]" 29 | echo "" 30 | echo "Arguments:" 31 | echo " command The command to be run." 32 | echo " ... Arguments passed to the command. Variable number of arguments allowed." 33 | echo "" 34 | echo "Options:" 35 | echo " --verbose Show verbose output." 36 | echo " -c|--channel The channel of KoreBuild to download. Overrides the value from the config file.." 37 | echo " --config-file The path to the configuration file that stores values. Defaults to korebuild.json." 38 | echo " -d|--dotnet-home The directory where .NET Core tools will be stored. Defaults to '\$DOTNET_HOME' or '\$HOME/.dotnet." 39 | echo " --path The directory to build. Defaults to the directory containing the script." 40 | echo " -s|--tools-source|-ToolsSource The base url where build tools can be downloaded. Overrides the value from the config file." 41 | echo " --tools-source-suffix|-ToolsSourceSuffix The suffix to append to tools-source. Useful for query strings." 42 | echo " -u|--update Update to the latest KoreBuild even if the lock file is present." 43 | echo " --reinstall Reinstall KoreBuild." 44 | echo " --ci Apply CI specific settings and environment variables." 45 | echo "" 46 | echo "Description:" 47 | echo " This function will create a file \$DIR/korebuild-lock.txt. This lock file can be committed to source, but does not have to be." 48 | echo " When the lockfile is not present, KoreBuild will create one using latest available version from \$channel." 49 | 50 | if [[ "${1:-}" != '--no-exit' ]]; then 51 | exit 2 52 | fi 53 | } 54 | 55 | get_korebuild() { 56 | local version 57 | local lock_file="$repo_path/korebuild-lock.txt" 58 | if [ ! -f "$lock_file" ] || [ "$update" = true ]; then 59 | __get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lock_file" "$tools_source_suffix" 60 | fi 61 | version="$(grep 'version:*' -m 1 "$lock_file")" 62 | if [[ "$version" == '' ]]; then 63 | __error "Failed to parse version from $lock_file. Expected a line that begins with 'version:'" 64 | return 1 65 | fi 66 | version="$(echo "${version#version:}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" 67 | local korebuild_path="$DOTNET_HOME/buildtools/korebuild/$version" 68 | 69 | if [ "$reinstall" = true ] && [ -d "$korebuild_path" ]; then 70 | rm -rf "$korebuild_path" 71 | fi 72 | 73 | { 74 | if [ ! -d "$korebuild_path" ]; then 75 | mkdir -p "$korebuild_path" 76 | local remote_path="$tools_source/korebuild/artifacts/$version/korebuild.$version.zip" 77 | tmpfile="$(mktemp)" 78 | echo -e "${MAGENTA}Downloading KoreBuild ${version}${RESET}" 79 | if __get_remote_file "$remote_path" "$tmpfile" "$tools_source_suffix"; then 80 | unzip -q -d "$korebuild_path" "$tmpfile" 81 | fi 82 | rm "$tmpfile" || true 83 | fi 84 | 85 | source "$korebuild_path/KoreBuild.sh" 86 | } || { 87 | if [ -d "$korebuild_path" ]; then 88 | echo "Cleaning up after failed installation" 89 | rm -rf "$korebuild_path" || true 90 | fi 91 | return 1 92 | } 93 | } 94 | 95 | __error() { 96 | echo -e "${RED}error: $*${RESET}" 1>&2 97 | } 98 | 99 | __warn() { 100 | echo -e "${YELLOW}warning: $*${RESET}" 101 | } 102 | 103 | __machine_has() { 104 | hash "$1" > /dev/null 2>&1 105 | return $? 106 | } 107 | 108 | __get_remote_file() { 109 | local remote_path=$1 110 | local local_path=$2 111 | local remote_path_suffix=$3 112 | 113 | if [[ "$remote_path" != 'http'* ]]; then 114 | cp "$remote_path" "$local_path" 115 | return 0 116 | fi 117 | 118 | local failed=false 119 | if __machine_has wget; then 120 | wget --tries 10 --quiet -O "$local_path" "${remote_path}${remote_path_suffix}" || failed=true 121 | else 122 | failed=true 123 | fi 124 | 125 | if [ "$failed" = true ] && __machine_has curl; then 126 | failed=false 127 | curl --retry 10 -sSL -f --create-dirs -o "$local_path" "${remote_path}${remote_path_suffix}" || failed=true 128 | fi 129 | 130 | if [ "$failed" = true ]; then 131 | __error "Download failed: $remote_path" 1>&2 132 | return 1 133 | fi 134 | } 135 | 136 | # 137 | # main 138 | # 139 | 140 | command="${1:-}" 141 | shift 142 | 143 | while [[ $# -gt 0 ]]; do 144 | case $1 in 145 | -\?|-h|--help) 146 | __usage --no-exit 147 | exit 0 148 | ;; 149 | -c|--channel|-Channel) 150 | shift 151 | channel="${1:-}" 152 | [ -z "$channel" ] && __usage 153 | ;; 154 | --config-file|-ConfigFile) 155 | shift 156 | config_file="${1:-}" 157 | [ -z "$config_file" ] && __usage 158 | if [ ! -f "$config_file" ]; then 159 | __error "Invalid value for --config-file. $config_file does not exist." 160 | exit 1 161 | fi 162 | ;; 163 | -d|--dotnet-home|-DotNetHome) 164 | shift 165 | DOTNET_HOME="${1:-}" 166 | [ -z "$DOTNET_HOME" ] && __usage 167 | ;; 168 | --path|-Path) 169 | shift 170 | repo_path="${1:-}" 171 | [ -z "$repo_path" ] && __usage 172 | ;; 173 | -s|--tools-source|-ToolsSource) 174 | shift 175 | tools_source="${1:-}" 176 | [ -z "$tools_source" ] && __usage 177 | ;; 178 | --tools-source-suffix|-ToolsSourceSuffix) 179 | shift 180 | tools_source_suffix="${1:-}" 181 | [ -z "$tools_source_suffix" ] && __usage 182 | ;; 183 | -u|--update|-Update) 184 | update=true 185 | ;; 186 | --reinstall|-[Rr]einstall) 187 | reinstall=true 188 | ;; 189 | --ci|-[Cc][Ii]) 190 | ci=true 191 | ;; 192 | --verbose|-Verbose) 193 | verbose=true 194 | ;; 195 | --) 196 | shift 197 | break 198 | ;; 199 | *) 200 | break 201 | ;; 202 | esac 203 | shift 204 | done 205 | 206 | if ! __machine_has unzip; then 207 | __error 'Missing required command: unzip' 208 | exit 1 209 | fi 210 | 211 | if ! __machine_has curl && ! __machine_has wget; then 212 | __error 'Missing required command. Either wget or curl is required.' 213 | exit 1 214 | fi 215 | 216 | [ -z "${config_file:-}" ] && config_file="$repo_path/korebuild.json" 217 | if [ -f "$config_file" ]; then 218 | if __machine_has jq ; then 219 | if jq '.' "$config_file" >/dev/null ; then 220 | config_channel="$(jq -r 'select(.channel!=null) | .channel' "$config_file")" 221 | config_tools_source="$(jq -r 'select(.toolsSource!=null) | .toolsSource' "$config_file")" 222 | else 223 | __error "$config_file contains invalid JSON." 224 | exit 1 225 | fi 226 | elif __machine_has python ; then 227 | if python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then 228 | config_channel="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" 229 | config_tools_source="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" 230 | else 231 | __error "$config_file contains invalid JSON." 232 | exit 1 233 | fi 234 | elif __machine_has python3 ; then 235 | if python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then 236 | config_channel="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" 237 | config_tools_source="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" 238 | else 239 | __error "$config_file contains invalid JSON." 240 | exit 1 241 | fi 242 | else 243 | __error 'Missing required command: jq or python. Could not parse the JSON file.' 244 | exit 1 245 | fi 246 | 247 | [ ! -z "${config_channel:-}" ] && channel="$config_channel" 248 | [ ! -z "${config_tools_source:-}" ] && tools_source="$config_tools_source" 249 | fi 250 | 251 | [ -z "$channel" ] && channel='master' 252 | [ -z "$tools_source" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools' 253 | 254 | get_korebuild 255 | set_korebuildsettings "$tools_source" "$DOTNET_HOME" "$repo_path" "$config_file" "$ci" 256 | invoke_korebuild_command "$command" "$@" 257 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/AntiforgeryOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace Microsoft.AspNetCore.Antiforgery 8 | { 9 | /// 10 | /// Provides programmatic configuration for the antiforgery token system. 11 | /// 12 | public class AntiforgeryOptions 13 | { 14 | private const string AntiforgeryTokenFieldName = "__RequestVerificationToken"; 15 | private const string AntiforgeryTokenHeaderName = "RequestVerificationToken"; 16 | 17 | private string _formFieldName = AntiforgeryTokenFieldName; 18 | 19 | private CookieBuilder _cookieBuilder = new CookieBuilder 20 | { 21 | SameSite = SameSiteMode.Strict, 22 | HttpOnly = true, 23 | 24 | // Check the comment on CookieBuilder for more details 25 | IsEssential = true, 26 | 27 | // Some browsers do not allow non-secure endpoints to set cookies with a 'secure' flag or overwrite cookies 28 | // whose 'secure' flag is set (http://httpwg.org/http-extensions/draft-ietf-httpbis-cookie-alone.html). 29 | // Since mixing secure and non-secure endpoints is a common scenario in applications, we are relaxing the 30 | // restriction on secure policy on some cookies by setting to 'None'. Cookies related to authentication or 31 | // authorization use a stronger policy than 'None'. 32 | SecurePolicy = CookieSecurePolicy.None, 33 | }; 34 | 35 | /// 36 | /// The default cookie prefix, which is ".AspNetCore.Antiforgery.". 37 | /// 38 | public static readonly string DefaultCookiePrefix = ".AspNetCore.Antiforgery."; 39 | 40 | /// 41 | /// Determines the settings used to create the antiforgery cookies. 42 | /// 43 | /// 44 | /// 45 | /// If an explicit is not provided, the system will automatically generate a 46 | /// unique name that begins with . 47 | /// 48 | /// 49 | /// defaults to . 50 | /// defaults to true. 51 | /// defaults to true. The cookie used by the antiforgery system 52 | /// is part of a security system that is necessary when using cookie-based authentication. It should be 53 | /// considered required for the application to function. 54 | /// defaults to . 55 | /// 56 | /// 57 | public CookieBuilder Cookie 58 | { 59 | get => _cookieBuilder; 60 | set => _cookieBuilder = value ?? throw new ArgumentNullException(nameof(value)); 61 | } 62 | 63 | /// 64 | /// Specifies the name of the antiforgery token field that is used by the antiforgery system. 65 | /// 66 | public string FormFieldName 67 | { 68 | get => _formFieldName; 69 | set => _formFieldName = value ?? throw new ArgumentNullException(nameof(value)); 70 | } 71 | 72 | /// 73 | /// Specifies the name of the header value that is used by the antiforgery system. If null then 74 | /// antiforgery validation will only consider form data. 75 | /// 76 | public string HeaderName { get; set; } = AntiforgeryTokenHeaderName; 77 | 78 | /// 79 | /// Specifies whether to suppress the generation of X-Frame-Options header 80 | /// which is used to prevent ClickJacking. By default, the X-Frame-Options 81 | /// header is generated with the value SAMEORIGIN. If this setting is 'true', 82 | /// the X-Frame-Options header will not be generated for the response. 83 | /// 84 | public bool SuppressXFrameOptionsHeader { get; set; } 85 | 86 | #region Obsolete API 87 | /// 88 | /// 89 | /// This property is obsolete and will be removed in a future version. The recommended alternative is on . 90 | /// 91 | /// 92 | /// Specifies the name of the cookie that is used by the antiforgery system. 93 | /// 94 | /// 95 | /// 96 | /// If an explicit name is not provided, the system will automatically generate a 97 | /// unique name that begins with . 98 | /// 99 | [Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.Name) + ".")] 100 | public string CookieName { get => Cookie.Name; set => Cookie.Name = value; } 101 | 102 | /// 103 | /// 104 | /// This property is obsolete and will be removed in a future version. The recommended alternative is on . 105 | /// 106 | /// 107 | /// The path set on the cookie. If set to null, the "path" attribute on the cookie is set to the current 108 | /// request's value. If the value of is 109 | /// null or empty, then the "path" attribute is set to the value of . 110 | /// 111 | /// 112 | [Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.Path) + ".")] 113 | public PathString? CookiePath { get => Cookie.Path; set => Cookie.Path = value; } 114 | 115 | /// 116 | /// 117 | /// This property is obsolete and will be removed in a future version. The recommended alternative is on . 118 | /// 119 | /// 120 | /// The domain set on the cookie. By default its null which results in the "domain" attribute not being set. 121 | /// 122 | /// 123 | [Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.Domain) + ".")] 124 | public string CookieDomain { get => Cookie.Domain; set => Cookie.Domain = value; } 125 | 126 | 127 | /// 128 | /// 129 | /// This property is obsolete and will be removed in a future version. 130 | /// The recommended alternative is to set on . 131 | /// 132 | /// 133 | /// true is equivalent to . 134 | /// false is equivalent to . 135 | /// 136 | /// 137 | /// Specifies whether SSL is required for the antiforgery system 138 | /// to operate. If this setting is 'true' and a non-SSL request 139 | /// comes into the system, all antiforgery APIs will fail. 140 | /// 141 | /// 142 | [Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is to set " + nameof(Cookie) + "." + nameof(CookieBuilder.SecurePolicy) + ".")] 143 | public bool RequireSsl 144 | { 145 | get => Cookie.SecurePolicy == CookieSecurePolicy.Always; 146 | set => Cookie.SecurePolicy = value ? CookieSecurePolicy.Always : CookieSecurePolicy.None; 147 | } 148 | #endregion 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/AntiforgeryServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using Microsoft.AspNetCore.Antiforgery; 6 | using Microsoft.AspNetCore.Antiforgery.Internal; 7 | using Microsoft.Extensions.DependencyInjection.Extensions; 8 | using Microsoft.Extensions.ObjectPool; 9 | using Microsoft.Extensions.Options; 10 | 11 | namespace Microsoft.Extensions.DependencyInjection 12 | { 13 | /// 14 | /// Extension methods for setting up antiforgery services in an . 15 | /// 16 | public static class AntiforgeryServiceCollectionExtensions 17 | { 18 | /// 19 | /// Adds antiforgery services to the specified . 20 | /// 21 | /// The to add services to. 22 | /// The so that additional calls can be chained. 23 | public static IServiceCollection AddAntiforgery(this IServiceCollection services) 24 | { 25 | if (services == null) 26 | { 27 | throw new ArgumentNullException(nameof(services)); 28 | } 29 | 30 | services.AddDataProtection(); 31 | 32 | // Don't overwrite any options setups that a user may have added. 33 | services.TryAddEnumerable( 34 | ServiceDescriptor.Transient, AntiforgeryOptionsSetup>()); 35 | 36 | services.TryAddSingleton(); 37 | services.TryAddSingleton(); 38 | services.TryAddSingleton(); 39 | services.TryAddSingleton(); 40 | services.TryAddSingleton(); 41 | services.TryAddSingleton(); 42 | 43 | services.TryAddSingleton>(serviceProvider => 44 | { 45 | var provider = serviceProvider.GetRequiredService(); 46 | var policy = new AntiforgerySerializationContextPooledObjectPolicy(); 47 | return provider.Create(policy); 48 | }); 49 | 50 | return services; 51 | } 52 | 53 | /// 54 | /// Adds antiforgery services to the specified . 55 | /// 56 | /// The to add services to. 57 | /// An to configure the provided . 58 | /// The so that additional calls can be chained. 59 | public static IServiceCollection AddAntiforgery(this IServiceCollection services, Action setupAction) 60 | { 61 | if (services == null) 62 | { 63 | throw new ArgumentNullException(nameof(services)); 64 | } 65 | 66 | if (setupAction == null) 67 | { 68 | throw new ArgumentNullException(nameof(setupAction)); 69 | } 70 | 71 | services.AddAntiforgery(); 72 | services.Configure(setupAction); 73 | return services; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/AntiforgeryTokenSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.AspNetCore.Antiforgery 7 | { 8 | /// 9 | /// The antiforgery token pair (cookie and request token) for a request. 10 | /// 11 | public class AntiforgeryTokenSet 12 | { 13 | /// 14 | /// Creates the antiforgery token pair (cookie and request token) for a request. 15 | /// 16 | /// The token that is supplied in the request. 17 | /// The token that is supplied in the request cookie. 18 | /// The name of the form field used for the request token. 19 | /// The name of the header used for the request token. 20 | public AntiforgeryTokenSet( 21 | string requestToken, 22 | string cookieToken, 23 | string formFieldName, 24 | string headerName) 25 | { 26 | if (formFieldName == null) 27 | { 28 | throw new ArgumentNullException(nameof(formFieldName)); 29 | } 30 | 31 | RequestToken = requestToken; 32 | CookieToken = cookieToken; 33 | FormFieldName = formFieldName; 34 | HeaderName = headerName; 35 | } 36 | 37 | /// 38 | /// Gets the request token. 39 | /// 40 | public string RequestToken { get; } 41 | 42 | /// 43 | /// Gets the name of the form field used for the request token. 44 | /// 45 | public string FormFieldName { get; } 46 | 47 | /// 48 | /// Gets the name of the header used for the request token. 49 | /// 50 | public string HeaderName { get; } 51 | 52 | /// 53 | /// Gets the cookie token. 54 | /// 55 | public string CookieToken { get; } 56 | } 57 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/AntiforgeryValidationException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | 6 | namespace Microsoft.AspNetCore.Antiforgery 7 | { 8 | /// 9 | /// The that is thrown when the antiforgery token validation fails. 10 | /// 11 | public class AntiforgeryValidationException : Exception 12 | { 13 | /// 14 | /// Creates a new instance of with the specified 15 | /// exception message. 16 | /// 17 | /// The message that describes the error. 18 | public AntiforgeryValidationException(string message) 19 | : base(message) 20 | { 21 | } 22 | 23 | /// 24 | /// Creates a new instance of with the specified 25 | /// exception message and inner exception. 26 | /// 27 | /// The message that describes the error. 28 | /// The inner . 29 | public AntiforgeryValidationException(string message, Exception innerException) 30 | : base(message, innerException) 31 | { 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/IAntiforgery.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace Microsoft.AspNetCore.Antiforgery 8 | { 9 | /// 10 | /// Provides access to the antiforgery system, which provides protection against 11 | /// Cross-site Request Forgery (XSRF, also called CSRF) attacks. 12 | /// 13 | public interface IAntiforgery 14 | { 15 | /// 16 | /// Generates an for this request and stores the cookie token 17 | /// in the response. This operation also sets the "Cache-control" and "Pragma" headers to "no-cache" and 18 | /// the "X-Frame-Options" header to "SAMEORIGIN". 19 | /// 20 | /// The associated with the current request. 21 | /// An with tokens for the response. 22 | /// 23 | /// This method has a side effect: 24 | /// A response cookie is set if there is no valid cookie associated with the request. 25 | /// 26 | AntiforgeryTokenSet GetAndStoreTokens(HttpContext httpContext); 27 | 28 | /// 29 | /// Generates an for this request. 30 | /// 31 | /// The associated with the current request. 32 | /// 33 | /// Unlike , this method has no side effect. The caller 34 | /// is responsible for setting the response cookie and injecting the returned 35 | /// form token as appropriate. 36 | /// 37 | AntiforgeryTokenSet GetTokens(HttpContext httpContext); 38 | 39 | /// 40 | /// Asynchronously returns a value indicating whether the request passes antiforgery validation. If the 41 | /// request uses a safe HTTP method (GET, HEAD, OPTIONS, TRACE), the antiforgery token is not validated. 42 | /// 43 | /// The associated with the current request. 44 | /// 45 | /// A that, when completed, returns true if the request uses a safe HTTP 46 | /// method or contains a valid antiforgery token, otherwise returns false. 47 | /// 48 | Task IsRequestValidAsync(HttpContext httpContext); 49 | 50 | /// 51 | /// Validates an antiforgery token that was supplied as part of the request. 52 | /// 53 | /// The associated with the current request. 54 | /// 55 | /// Thrown when the request does not include a valid antiforgery token. 56 | /// 57 | Task ValidateRequestAsync(HttpContext httpContext); 58 | 59 | /// 60 | /// Generates and stores an antiforgery cookie token if one is not available or not valid. 61 | /// 62 | /// The associated with the current request. 63 | void SetCookieTokenAndHeader(HttpContext httpContext); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/IAntiforgeryAdditionalDataProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Microsoft.AspNetCore.Http; 5 | 6 | namespace Microsoft.AspNetCore.Antiforgery 7 | { 8 | /// 9 | /// Allows providing or validating additional custom data for antiforgery tokens. 10 | /// For example, the developer could use this to supply a nonce when the token is 11 | /// generated, then he could validate the nonce when the token is validated. 12 | /// 13 | /// 14 | /// The antiforgery system already embeds the client's username within the 15 | /// generated tokens. This interface provides and consumes supplemental 16 | /// data. If an incoming antiforgery token contains supplemental data but no 17 | /// additional data provider is configured, the supplemental data will not be 18 | /// validated. 19 | /// 20 | public interface IAntiforgeryAdditionalDataProvider 21 | { 22 | /// 23 | /// Provides additional data to be stored for the antiforgery tokens generated 24 | /// during this request. 25 | /// 26 | /// Information about the current request. 27 | /// Supplemental data to embed within the antiforgery token. 28 | string GetAdditionalData(HttpContext context); 29 | 30 | /// 31 | /// Validates additional data that was embedded inside an incoming antiforgery 32 | /// token. 33 | /// 34 | /// Information about the current request. 35 | /// Supplemental data that was embedded within the token. 36 | /// True if the data is valid; false if the data is invalid. 37 | bool ValidateAdditionalData(HttpContext context, string additionalData); 38 | } 39 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/AntiforgeryFeature.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace Microsoft.AspNetCore.Antiforgery.Internal 5 | { 6 | /// 7 | /// Used to hold per-request state. 8 | /// 9 | public class AntiforgeryFeature : IAntiforgeryFeature 10 | { 11 | public bool HaveDeserializedCookieToken { get; set; } 12 | 13 | public AntiforgeryToken CookieToken { get; set; } 14 | 15 | public bool HaveDeserializedRequestToken { get; set; } 16 | 17 | public AntiforgeryToken RequestToken { get; set; } 18 | 19 | public bool HaveGeneratedNewCookieToken { get; set; } 20 | 21 | // After HaveGeneratedNewCookieToken is true, remains null if CookieToken is valid. 22 | public AntiforgeryToken NewCookieToken { get; set; } 23 | 24 | // After HaveGeneratedNewCookieToken is true, remains null if CookieToken is valid. 25 | public string NewCookieTokenString { get; set; } 26 | 27 | public AntiforgeryToken NewRequestToken { get; set; } 28 | 29 | public string NewRequestTokenString { get; set; } 30 | 31 | // Always false if NewCookieToken is null. Never store null cookie token or re-store cookie token from request. 32 | public bool HaveStoredNewCookieToken { get; set; } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/AntiforgeryLoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace Microsoft.AspNetCore.Antiforgery.Internal 8 | { 9 | internal static class AntiforgeryLoggerExtensions 10 | { 11 | private static readonly Action _failedToDeserialzeTokens; 12 | private static readonly Action _validationFailed; 13 | private static readonly Action _validated; 14 | private static readonly Action _missingCookieToken; 15 | private static readonly Action _missingRequestToken; 16 | private static readonly Action _newCookieToken; 17 | private static readonly Action _reusedCookieToken; 18 | private static readonly Action _tokenDeserializeException; 19 | private static readonly Action _responseCacheHeadersOverridenToNoCache; 20 | 21 | static AntiforgeryLoggerExtensions() 22 | { 23 | _validationFailed = LoggerMessage.Define( 24 | LogLevel.Warning, 25 | 1, 26 | "Antiforgery validation failed with message '{Message}'."); 27 | _validated = LoggerMessage.Define( 28 | LogLevel.Debug, 29 | 2, 30 | "Antiforgery successfully validated a request."); 31 | _missingCookieToken = LoggerMessage.Define( 32 | LogLevel.Warning, 33 | 3, 34 | "The required antiforgery cookie '{CookieName}' is not present."); 35 | _missingRequestToken = LoggerMessage.Define( 36 | LogLevel.Warning, 37 | 4, 38 | "The required antiforgery request token was not provided in either form field '{FormFieldName}' " 39 | + "or header '{HeaderName}'."); 40 | _newCookieToken = LoggerMessage.Define( 41 | LogLevel.Debug, 42 | 5, 43 | "A new antiforgery cookie token was created."); 44 | _reusedCookieToken = LoggerMessage.Define( 45 | LogLevel.Debug, 46 | 6, 47 | "An antiforgery cookie token was reused."); 48 | _tokenDeserializeException = LoggerMessage.Define( 49 | LogLevel.Error, 50 | 7, 51 | "An exception was thrown while deserializing the token."); 52 | _responseCacheHeadersOverridenToNoCache = LoggerMessage.Define( 53 | LogLevel.Warning, 54 | 8, 55 | "The 'Cache-Control' and 'Pragma' headers have been overridden and set to 'no-cache, no-store' and " + 56 | "'no-cache' respectively to prevent caching of this response. Any response that uses antiforgery " + 57 | "should not be cached."); 58 | _failedToDeserialzeTokens = LoggerMessage.Define( 59 | LogLevel.Debug, 60 | 9, 61 | "Failed to deserialize antiforgery tokens."); 62 | } 63 | 64 | public static void ValidationFailed(this ILogger logger, string message) 65 | { 66 | _validationFailed(logger, message, null); 67 | } 68 | 69 | public static void ValidatedAntiforgeryToken(this ILogger logger) 70 | { 71 | _validated(logger, null); 72 | } 73 | 74 | public static void MissingCookieToken(this ILogger logger, string cookieName) 75 | { 76 | _missingCookieToken(logger, cookieName, null); 77 | } 78 | 79 | public static void MissingRequestToken(this ILogger logger, string formFieldName, string headerName) 80 | { 81 | _missingRequestToken(logger, formFieldName, headerName, null); 82 | } 83 | 84 | public static void NewCookieToken(this ILogger logger) 85 | { 86 | _newCookieToken(logger, null); 87 | } 88 | 89 | public static void ReusedCookieToken(this ILogger logger) 90 | { 91 | _reusedCookieToken(logger, null); 92 | } 93 | 94 | public static void TokenDeserializeException(this ILogger logger, Exception exception) 95 | { 96 | _tokenDeserializeException(logger, exception); 97 | } 98 | 99 | public static void ResponseCacheHeadersOverridenToNoCache(this ILogger logger) 100 | { 101 | _responseCacheHeadersOverridenToNoCache(logger, null); 102 | } 103 | 104 | public static void FailedToDeserialzeTokens(this ILogger logger, Exception exception) 105 | { 106 | _failedToDeserialzeTokens(logger, exception); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/AntiforgeryOptionsSetup.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Linq; 5 | using System.Text; 6 | using Microsoft.AspNetCore.DataProtection; 7 | using Microsoft.AspNetCore.WebUtilities; 8 | using Microsoft.Extensions.Options; 9 | 10 | namespace Microsoft.AspNetCore.Antiforgery.Internal 11 | { 12 | public class AntiforgeryOptionsSetup : ConfigureOptions 13 | { 14 | public AntiforgeryOptionsSetup(IOptions dataProtectionOptionsAccessor) 15 | : base((options) => ConfigureOptions(options, dataProtectionOptionsAccessor.Value)) 16 | { 17 | } 18 | 19 | public static void ConfigureOptions(AntiforgeryOptions options, DataProtectionOptions dataProtectionOptions) 20 | { 21 | if (options.Cookie.Name == null) 22 | { 23 | var applicationId = dataProtectionOptions.ApplicationDiscriminator ?? string.Empty; 24 | options.Cookie.Name = AntiforgeryOptions.DefaultCookiePrefix + ComputeCookieName(applicationId); 25 | } 26 | } 27 | 28 | private static string ComputeCookieName(string applicationId) 29 | { 30 | using (var sha256 = CryptographyAlgorithms.CreateSHA256()) 31 | { 32 | var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(applicationId)); 33 | var subHash = hash.Take(8).ToArray(); 34 | return WebEncoders.Base64UrlEncode(subHash); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/AntiforgerySerializationContext.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.IO; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | 8 | namespace Microsoft.AspNetCore.Antiforgery.Internal 9 | { 10 | public class AntiforgerySerializationContext 11 | { 12 | // Avoid allocating 256 bytes (the default) and using 18 (the AntiforgeryToken minimum). 64 bytes is enough for 13 | // a short username or claim UID and some additional data. MemoryStream bumps capacity to 256 if exceeded. 14 | private const int InitialStreamSize = 64; 15 | 16 | // Don't let the MemoryStream grow beyond 1 MB. 17 | private const int MaximumStreamSize = 0x100000; 18 | 19 | // Start _chars off with length 256 (18 bytes is protected into 116 bytes then encoded into 156 characters). 20 | // Double length from there if necessary. 21 | private const int InitialCharsLength = 256; 22 | 23 | // Don't let _chars grow beyond 512k characters. 24 | private const int MaximumCharsLength = 0x80000; 25 | 26 | private char[] _chars; 27 | private MemoryStream _stream; 28 | private BinaryReader _reader; 29 | private BinaryWriter _writer; 30 | private SHA256 _sha256; 31 | 32 | public MemoryStream Stream 33 | { 34 | get 35 | { 36 | if (_stream == null) 37 | { 38 | _stream = new MemoryStream(InitialStreamSize); 39 | } 40 | 41 | return _stream; 42 | } 43 | private set 44 | { 45 | _stream = value; 46 | } 47 | } 48 | 49 | public BinaryReader Reader 50 | { 51 | get 52 | { 53 | if (_reader == null) 54 | { 55 | // Leave open to clean up correctly even if only one of the reader or writer has been created. 56 | _reader = new BinaryReader(Stream, Encoding.UTF8, leaveOpen: true); 57 | } 58 | 59 | return _reader; 60 | } 61 | private set 62 | { 63 | _reader = value; 64 | } 65 | } 66 | 67 | public BinaryWriter Writer 68 | { 69 | get 70 | { 71 | if (_writer == null) 72 | { 73 | // Leave open to clean up correctly even if only one of the reader or writer has been created. 74 | _writer = new BinaryWriter(Stream, Encoding.UTF8, leaveOpen: true); 75 | } 76 | 77 | return _writer; 78 | } 79 | private set 80 | { 81 | _writer = value; 82 | } 83 | } 84 | 85 | public SHA256 Sha256 86 | { 87 | get 88 | { 89 | if (_sha256 == null) 90 | { 91 | _sha256 = CryptographyAlgorithms.CreateSHA256(); 92 | } 93 | 94 | return _sha256; 95 | } 96 | private set 97 | { 98 | _sha256 = value; 99 | } 100 | } 101 | 102 | public char[] GetChars(int count) 103 | { 104 | if (_chars == null || _chars.Length < count) 105 | { 106 | var newLength = _chars == null ? InitialCharsLength : checked(_chars.Length * 2); 107 | while (newLength < count) 108 | { 109 | newLength = checked(newLength * 2); 110 | } 111 | 112 | _chars = new char[newLength]; 113 | } 114 | 115 | return _chars; 116 | } 117 | 118 | public void Reset() 119 | { 120 | if (_chars != null && _chars.Length > MaximumCharsLength) 121 | { 122 | _chars = null; 123 | } 124 | 125 | if (_stream != null) 126 | { 127 | if (Stream.Capacity > MaximumStreamSize) 128 | { 129 | Stream = null; 130 | Reader = null; 131 | Writer = null; 132 | } 133 | else 134 | { 135 | Stream.Position = 0L; 136 | Stream.SetLength(0L); 137 | } 138 | } 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/AntiforgerySerializationContextPooledObjectPolicy.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Microsoft.Extensions.ObjectPool; 5 | 6 | namespace Microsoft.AspNetCore.Antiforgery.Internal 7 | { 8 | public class AntiforgerySerializationContextPooledObjectPolicy 9 | : IPooledObjectPolicy 10 | { 11 | public AntiforgerySerializationContext Create() 12 | { 13 | return new AntiforgerySerializationContext(); 14 | } 15 | 16 | public bool Return(AntiforgerySerializationContext obj) 17 | { 18 | obj.Reset(); 19 | 20 | return true; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/AntiforgeryToken.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace Microsoft.AspNetCore.Antiforgery.Internal 5 | { 6 | public sealed class AntiforgeryToken 7 | { 8 | internal const int SecurityTokenBitLength = 128; 9 | internal const int ClaimUidBitLength = 256; 10 | 11 | private string _additionalData = string.Empty; 12 | private string _username = string.Empty; 13 | private BinaryBlob _securityToken; 14 | 15 | public string AdditionalData 16 | { 17 | get { return _additionalData; } 18 | set 19 | { 20 | _additionalData = value ?? string.Empty; 21 | } 22 | } 23 | 24 | public BinaryBlob ClaimUid { get; set; } 25 | 26 | public bool IsCookieToken { get; set; } 27 | 28 | public BinaryBlob SecurityToken 29 | { 30 | get 31 | { 32 | if (_securityToken == null) 33 | { 34 | _securityToken = new BinaryBlob(SecurityTokenBitLength); 35 | } 36 | return _securityToken; 37 | } 38 | set 39 | { 40 | _securityToken = value; 41 | } 42 | } 43 | 44 | public string Username 45 | { 46 | get { return _username; } 47 | set 48 | { 49 | _username = value ?? string.Empty; 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/BinaryBlob.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Diagnostics; 6 | using System.Globalization; 7 | using System.Runtime.CompilerServices; 8 | using System.Security.Cryptography; 9 | using System.Text; 10 | 11 | namespace Microsoft.AspNetCore.Antiforgery.Internal 12 | { 13 | // Represents a binary blob (token) that contains random data. 14 | // Useful for binary data inside a serialized stream. 15 | [DebuggerDisplay("{DebuggerString}")] 16 | public sealed class BinaryBlob : IEquatable 17 | { 18 | private static readonly RandomNumberGenerator _randomNumberGenerator = RandomNumberGenerator.Create(); 19 | private readonly byte[] _data; 20 | 21 | // Generates a new token using a specified bit length. 22 | public BinaryBlob(int bitLength) 23 | : this(bitLength, GenerateNewToken(bitLength)) 24 | { 25 | } 26 | 27 | // Generates a token using an existing binary value. 28 | public BinaryBlob(int bitLength, byte[] data) 29 | { 30 | if (bitLength < 32 || bitLength % 8 != 0) 31 | { 32 | throw new ArgumentOutOfRangeException("bitLength"); 33 | } 34 | if (data == null || data.Length != bitLength / 8) 35 | { 36 | throw new ArgumentOutOfRangeException("data"); 37 | } 38 | 39 | _data = data; 40 | } 41 | 42 | public int BitLength 43 | { 44 | get 45 | { 46 | return checked(_data.Length * 8); 47 | } 48 | } 49 | 50 | private string DebuggerString 51 | { 52 | get 53 | { 54 | var sb = new StringBuilder("0x", 2 + (_data.Length * 2)); 55 | for (var i = 0; i < _data.Length; i++) 56 | { 57 | sb.AppendFormat(CultureInfo.InvariantCulture, "{0:x2}", _data[i]); 58 | } 59 | return sb.ToString(); 60 | } 61 | } 62 | 63 | public override bool Equals(object obj) 64 | { 65 | return Equals(obj as BinaryBlob); 66 | } 67 | 68 | public bool Equals(BinaryBlob other) 69 | { 70 | if (other == null) 71 | { 72 | return false; 73 | } 74 | 75 | Debug.Assert(_data.Length == other._data.Length); 76 | return AreByteArraysEqual(_data, other._data); 77 | } 78 | 79 | public byte[] GetData() 80 | { 81 | return _data; 82 | } 83 | 84 | public override int GetHashCode() 85 | { 86 | // Since data should contain uniformly-distributed entropy, the 87 | // first 32 bits can serve as the hash code. 88 | Debug.Assert(_data != null && _data.Length >= (32 / 8)); 89 | return BitConverter.ToInt32(_data, 0); 90 | } 91 | 92 | private static byte[] GenerateNewToken(int bitLength) 93 | { 94 | var data = new byte[bitLength / 8]; 95 | _randomNumberGenerator.GetBytes(data); 96 | return data; 97 | } 98 | 99 | // Need to mark it with NoInlining and NoOptimization attributes to ensure that the 100 | // operation runs in constant time. 101 | [MethodImplAttribute(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] 102 | private static bool AreByteArraysEqual(byte[] a, byte[] b) 103 | { 104 | if (a == null || b == null || a.Length != b.Length) 105 | { 106 | return false; 107 | } 108 | 109 | var areEqual = true; 110 | for (var i = 0; i < a.Length; i++) 111 | { 112 | areEqual &= (a[i] == b[i]); 113 | } 114 | return areEqual; 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/CryptographyAlgorithms.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Security.Cryptography; 5 | 6 | namespace Microsoft.AspNetCore.Antiforgery.Internal 7 | { 8 | public static class CryptographyAlgorithms 9 | { 10 | public static SHA256 CreateSHA256() 11 | { 12 | try 13 | { 14 | return SHA256.Create(); 15 | } 16 | // SHA256.Create is documented to throw this exception on FIPS compliant machines. 17 | // See: https://msdn.microsoft.com/en-us/library/z08hz7ad%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396 18 | catch (System.Reflection.TargetInvocationException) 19 | { 20 | // Fallback to a FIPS compliant SHA256 algorithm. 21 | return new SHA256CryptoServiceProvider(); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryAdditionalDataProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Microsoft.AspNetCore.Http; 5 | 6 | namespace Microsoft.AspNetCore.Antiforgery.Internal 7 | { 8 | /// 9 | /// A default implementation. 10 | /// 11 | public class DefaultAntiforgeryAdditionalDataProvider : IAntiforgeryAdditionalDataProvider 12 | { 13 | /// 14 | public virtual string GetAdditionalData(HttpContext context) 15 | { 16 | return string.Empty; 17 | } 18 | 19 | /// 20 | public virtual bool ValidateAdditionalData(HttpContext context, string additionalData) 21 | { 22 | // Default implementation does not understand anything but empty data. 23 | return string.IsNullOrEmpty(additionalData); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenGenerator.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Security.Claims; 7 | using System.Security.Principal; 8 | using Microsoft.AspNetCore.Http; 9 | 10 | namespace Microsoft.AspNetCore.Antiforgery.Internal 11 | { 12 | public class DefaultAntiforgeryTokenGenerator : IAntiforgeryTokenGenerator 13 | { 14 | private readonly IClaimUidExtractor _claimUidExtractor; 15 | private readonly IAntiforgeryAdditionalDataProvider _additionalDataProvider; 16 | 17 | public DefaultAntiforgeryTokenGenerator( 18 | IClaimUidExtractor claimUidExtractor, 19 | IAntiforgeryAdditionalDataProvider additionalDataProvider) 20 | { 21 | _claimUidExtractor = claimUidExtractor; 22 | _additionalDataProvider = additionalDataProvider; 23 | } 24 | 25 | /// 26 | public AntiforgeryToken GenerateCookieToken() 27 | { 28 | return new AntiforgeryToken() 29 | { 30 | // SecurityToken will be populated automatically. 31 | IsCookieToken = true 32 | }; 33 | } 34 | 35 | /// 36 | public AntiforgeryToken GenerateRequestToken( 37 | HttpContext httpContext, 38 | AntiforgeryToken cookieToken) 39 | { 40 | if (httpContext == null) 41 | { 42 | throw new ArgumentNullException(nameof(httpContext)); 43 | } 44 | 45 | if (cookieToken == null) 46 | { 47 | throw new ArgumentNullException(nameof(cookieToken)); 48 | } 49 | 50 | if (!IsCookieTokenValid(cookieToken)) 51 | { 52 | throw new ArgumentException( 53 | Resources.Antiforgery_CookieToken_IsInvalid, 54 | nameof(cookieToken)); 55 | } 56 | 57 | var requestToken = new AntiforgeryToken() 58 | { 59 | SecurityToken = cookieToken.SecurityToken, 60 | IsCookieToken = false 61 | }; 62 | 63 | var isIdentityAuthenticated = false; 64 | 65 | // populate Username and ClaimUid 66 | var authenticatedIdentity = GetAuthenticatedIdentity(httpContext.User); 67 | if (authenticatedIdentity != null) 68 | { 69 | isIdentityAuthenticated = true; 70 | requestToken.ClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(httpContext.User)); 71 | 72 | if (requestToken.ClaimUid == null) 73 | { 74 | requestToken.Username = authenticatedIdentity.Name; 75 | } 76 | } 77 | 78 | // populate AdditionalData 79 | if (_additionalDataProvider != null) 80 | { 81 | requestToken.AdditionalData = _additionalDataProvider.GetAdditionalData(httpContext); 82 | } 83 | 84 | if (isIdentityAuthenticated 85 | && string.IsNullOrEmpty(requestToken.Username) 86 | && requestToken.ClaimUid == null 87 | && string.IsNullOrEmpty(requestToken.AdditionalData)) 88 | { 89 | // Application says user is authenticated, but we have no identifier for the user. 90 | throw new InvalidOperationException( 91 | Resources.FormatAntiforgeryTokenValidator_AuthenticatedUserWithoutUsername( 92 | authenticatedIdentity.GetType(), 93 | nameof(IIdentity.IsAuthenticated), 94 | "true", 95 | nameof(IIdentity.Name), 96 | nameof(IAntiforgeryAdditionalDataProvider), 97 | nameof(DefaultAntiforgeryAdditionalDataProvider))); 98 | } 99 | 100 | return requestToken; 101 | } 102 | 103 | /// 104 | public bool IsCookieTokenValid(AntiforgeryToken cookieToken) 105 | { 106 | return cookieToken != null && cookieToken.IsCookieToken; 107 | } 108 | 109 | /// 110 | public bool TryValidateTokenSet( 111 | HttpContext httpContext, 112 | AntiforgeryToken cookieToken, 113 | AntiforgeryToken requestToken, 114 | out string message) 115 | { 116 | if (httpContext == null) 117 | { 118 | throw new ArgumentNullException(nameof(httpContext)); 119 | } 120 | 121 | if (cookieToken == null) 122 | { 123 | throw new ArgumentNullException( 124 | nameof(cookieToken), 125 | Resources.Antiforgery_CookieToken_MustBeProvided_Generic); 126 | } 127 | 128 | if (requestToken == null) 129 | { 130 | throw new ArgumentNullException( 131 | nameof(requestToken), 132 | Resources.Antiforgery_RequestToken_MustBeProvided_Generic); 133 | } 134 | 135 | // Do the tokens have the correct format? 136 | if (!cookieToken.IsCookieToken || requestToken.IsCookieToken) 137 | { 138 | message = Resources.AntiforgeryToken_TokensSwapped; 139 | return false; 140 | } 141 | 142 | // Are the security tokens embedded in each incoming token identical? 143 | if (!object.Equals(cookieToken.SecurityToken, requestToken.SecurityToken)) 144 | { 145 | message = Resources.AntiforgeryToken_SecurityTokenMismatch; 146 | return false; 147 | } 148 | 149 | // Is the incoming token meant for the current user? 150 | var currentUsername = string.Empty; 151 | BinaryBlob currentClaimUid = null; 152 | 153 | var authenticatedIdentity = GetAuthenticatedIdentity(httpContext.User); 154 | if (authenticatedIdentity != null) 155 | { 156 | currentClaimUid = GetClaimUidBlob(_claimUidExtractor.ExtractClaimUid(httpContext.User)); 157 | if (currentClaimUid == null) 158 | { 159 | currentUsername = authenticatedIdentity.Name ?? string.Empty; 160 | } 161 | } 162 | 163 | // OpenID and other similar authentication schemes use URIs for the username. 164 | // These should be treated as case-sensitive. 165 | var comparer = StringComparer.OrdinalIgnoreCase; 166 | if (currentUsername.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || 167 | currentUsername.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) 168 | { 169 | comparer = StringComparer.Ordinal; 170 | } 171 | 172 | if (!comparer.Equals(requestToken.Username, currentUsername)) 173 | { 174 | message = Resources.FormatAntiforgeryToken_UsernameMismatch(requestToken.Username, currentUsername); 175 | return false; 176 | } 177 | 178 | if (!object.Equals(requestToken.ClaimUid, currentClaimUid)) 179 | { 180 | message = Resources.AntiforgeryToken_ClaimUidMismatch; 181 | return false; 182 | } 183 | 184 | // Is the AdditionalData valid? 185 | if (_additionalDataProvider != null && 186 | !_additionalDataProvider.ValidateAdditionalData(httpContext, requestToken.AdditionalData)) 187 | { 188 | message = Resources.AntiforgeryToken_AdditionalDataCheckFailed; 189 | return false; 190 | } 191 | 192 | message = null; 193 | return true; 194 | } 195 | 196 | private static BinaryBlob GetClaimUidBlob(string base64ClaimUid) 197 | { 198 | if (base64ClaimUid == null) 199 | { 200 | return null; 201 | } 202 | 203 | return new BinaryBlob(256, Convert.FromBase64String(base64ClaimUid)); 204 | } 205 | 206 | private static ClaimsIdentity GetAuthenticatedIdentity(ClaimsPrincipal claimsPrincipal) 207 | { 208 | if (claimsPrincipal == null) 209 | { 210 | return null; 211 | } 212 | 213 | var identitiesList = claimsPrincipal.Identities as List; 214 | if (identitiesList != null) 215 | { 216 | for (var i = 0; i < identitiesList.Count; i++) 217 | { 218 | if (identitiesList[i].IsAuthenticated) 219 | { 220 | return identitiesList[i]; 221 | } 222 | } 223 | } 224 | else 225 | { 226 | foreach (var identity in claimsPrincipal.Identities) 227 | { 228 | if (identity.IsAuthenticated) 229 | { 230 | return identity; 231 | } 232 | } 233 | } 234 | 235 | return null; 236 | } 237 | } 238 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenSerializer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.IO; 6 | using Microsoft.AspNetCore.DataProtection; 7 | using Microsoft.AspNetCore.WebUtilities; 8 | using Microsoft.Extensions.ObjectPool; 9 | 10 | namespace Microsoft.AspNetCore.Antiforgery.Internal 11 | { 12 | public class DefaultAntiforgeryTokenSerializer : IAntiforgeryTokenSerializer 13 | { 14 | private static readonly string Purpose = "Microsoft.AspNetCore.Antiforgery.AntiforgeryToken.v1"; 15 | private const byte TokenVersion = 0x01; 16 | 17 | private readonly IDataProtector _cryptoSystem; 18 | private readonly ObjectPool _pool; 19 | 20 | public DefaultAntiforgeryTokenSerializer( 21 | IDataProtectionProvider provider, 22 | ObjectPool pool) 23 | { 24 | if (provider == null) 25 | { 26 | throw new ArgumentNullException(nameof(provider)); 27 | } 28 | 29 | if (pool == null) 30 | { 31 | throw new ArgumentNullException(nameof(pool)); 32 | } 33 | 34 | _cryptoSystem = provider.CreateProtector(Purpose); 35 | _pool = pool; 36 | } 37 | 38 | public AntiforgeryToken Deserialize(string serializedToken) 39 | { 40 | var serializationContext = _pool.Get(); 41 | 42 | Exception innerException = null; 43 | try 44 | { 45 | var count = serializedToken.Length; 46 | var charsRequired = WebEncoders.GetArraySizeRequiredToDecode(count); 47 | var chars = serializationContext.GetChars(charsRequired); 48 | var tokenBytes = WebEncoders.Base64UrlDecode( 49 | serializedToken, 50 | offset: 0, 51 | buffer: chars, 52 | bufferOffset: 0, 53 | count: count); 54 | 55 | var unprotectedBytes = _cryptoSystem.Unprotect(tokenBytes); 56 | var stream = serializationContext.Stream; 57 | stream.Write(unprotectedBytes, offset: 0, count: unprotectedBytes.Length); 58 | stream.Position = 0L; 59 | 60 | var reader = serializationContext.Reader; 61 | var token = Deserialize(reader); 62 | if (token != null) 63 | { 64 | return token; 65 | } 66 | } 67 | catch (Exception ex) 68 | { 69 | // swallow all exceptions - homogenize error if something went wrong 70 | innerException = ex; 71 | } 72 | finally 73 | { 74 | _pool.Return(serializationContext); 75 | } 76 | 77 | // if we reached this point, something went wrong deserializing 78 | throw new AntiforgeryValidationException(Resources.AntiforgeryToken_DeserializationFailed, innerException); 79 | } 80 | 81 | /* The serialized format of the anti-XSRF token is as follows: 82 | * Version: 1 byte integer 83 | * SecurityToken: 16 byte binary blob 84 | * IsCookieToken: 1 byte Boolean 85 | * [if IsCookieToken != true] 86 | * +- IsClaimsBased: 1 byte Boolean 87 | * | [if IsClaimsBased = true] 88 | * | `- ClaimUid: 32 byte binary blob 89 | * | [if IsClaimsBased = false] 90 | * | `- Username: UTF-8 string with 7-bit integer length prefix 91 | * `- AdditionalData: UTF-8 string with 7-bit integer length prefix 92 | */ 93 | private static AntiforgeryToken Deserialize(BinaryReader reader) 94 | { 95 | // we can only consume tokens of the same serialized version that we generate 96 | var embeddedVersion = reader.ReadByte(); 97 | if (embeddedVersion != TokenVersion) 98 | { 99 | return null; 100 | } 101 | 102 | var deserializedToken = new AntiforgeryToken(); 103 | var securityTokenBytes = reader.ReadBytes(AntiforgeryToken.SecurityTokenBitLength / 8); 104 | deserializedToken.SecurityToken = 105 | new BinaryBlob(AntiforgeryToken.SecurityTokenBitLength, securityTokenBytes); 106 | deserializedToken.IsCookieToken = reader.ReadBoolean(); 107 | 108 | if (!deserializedToken.IsCookieToken) 109 | { 110 | var isClaimsBased = reader.ReadBoolean(); 111 | if (isClaimsBased) 112 | { 113 | var claimUidBytes = reader.ReadBytes(AntiforgeryToken.ClaimUidBitLength / 8); 114 | deserializedToken.ClaimUid = new BinaryBlob(AntiforgeryToken.ClaimUidBitLength, claimUidBytes); 115 | } 116 | else 117 | { 118 | deserializedToken.Username = reader.ReadString(); 119 | } 120 | 121 | deserializedToken.AdditionalData = reader.ReadString(); 122 | } 123 | 124 | // if there's still unconsumed data in the stream, fail 125 | if (reader.BaseStream.ReadByte() != -1) 126 | { 127 | return null; 128 | } 129 | 130 | // success 131 | return deserializedToken; 132 | } 133 | 134 | public string Serialize(AntiforgeryToken token) 135 | { 136 | if (token == null) 137 | { 138 | throw new ArgumentNullException(nameof(token)); 139 | } 140 | 141 | var serializationContext = _pool.Get(); 142 | 143 | try 144 | { 145 | var writer = serializationContext.Writer; 146 | writer.Write(TokenVersion); 147 | writer.Write(token.SecurityToken.GetData()); 148 | writer.Write(token.IsCookieToken); 149 | 150 | if (!token.IsCookieToken) 151 | { 152 | if (token.ClaimUid != null) 153 | { 154 | writer.Write(true /* isClaimsBased */); 155 | writer.Write(token.ClaimUid.GetData()); 156 | } 157 | else 158 | { 159 | writer.Write(false /* isClaimsBased */); 160 | writer.Write(token.Username); 161 | } 162 | 163 | writer.Write(token.AdditionalData); 164 | } 165 | 166 | writer.Flush(); 167 | var stream = serializationContext.Stream; 168 | var bytes = _cryptoSystem.Protect(stream.ToArray()); 169 | 170 | var count = bytes.Length; 171 | var charsRequired = WebEncoders.GetArraySizeRequiredToEncode(count); 172 | var chars = serializationContext.GetChars(charsRequired); 173 | var outputLength = WebEncoders.Base64UrlEncode( 174 | bytes, 175 | offset: 0, 176 | output: chars, 177 | outputOffset: 0, 178 | count: count); 179 | 180 | return new string(chars, startIndex: 0, length: outputLength); 181 | } 182 | finally 183 | { 184 | _pool.Return(serializationContext); 185 | } 186 | } 187 | } 188 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultAntiforgeryTokenStore.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Diagnostics; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.Options; 9 | using Microsoft.Extensions.Primitives; 10 | 11 | namespace Microsoft.AspNetCore.Antiforgery.Internal 12 | { 13 | public class DefaultAntiforgeryTokenStore : IAntiforgeryTokenStore 14 | { 15 | private readonly AntiforgeryOptions _options; 16 | 17 | public DefaultAntiforgeryTokenStore(IOptions optionsAccessor) 18 | { 19 | if (optionsAccessor == null) 20 | { 21 | throw new ArgumentNullException(nameof(optionsAccessor)); 22 | } 23 | 24 | _options = optionsAccessor.Value; 25 | } 26 | 27 | public string GetCookieToken(HttpContext httpContext) 28 | { 29 | Debug.Assert(httpContext != null); 30 | 31 | var requestCookie = httpContext.Request.Cookies[_options.Cookie.Name]; 32 | if (string.IsNullOrEmpty(requestCookie)) 33 | { 34 | // unable to find the cookie. 35 | return null; 36 | } 37 | 38 | return requestCookie; 39 | } 40 | 41 | public async Task GetRequestTokensAsync(HttpContext httpContext) 42 | { 43 | Debug.Assert(httpContext != null); 44 | 45 | var cookieToken = httpContext.Request.Cookies[_options.Cookie.Name]; 46 | 47 | // We want to delay reading the form as much as possible, for example in case of large file uploads, 48 | // request token could be part of the header. 49 | StringValues requestToken; 50 | if (_options.HeaderName != null) 51 | { 52 | requestToken = httpContext.Request.Headers[_options.HeaderName]; 53 | } 54 | 55 | // Fall back to reading form instead 56 | if (requestToken.Count == 0 && httpContext.Request.HasFormContentType) 57 | { 58 | // Check the content-type before accessing the form collection to make sure 59 | // we report errors gracefully. 60 | var form = await httpContext.Request.ReadFormAsync(); 61 | requestToken = form[_options.FormFieldName]; 62 | } 63 | 64 | return new AntiforgeryTokenSet(requestToken, cookieToken, _options.FormFieldName, _options.HeaderName); 65 | } 66 | 67 | public void SaveCookieToken(HttpContext httpContext, string token) 68 | { 69 | Debug.Assert(httpContext != null); 70 | Debug.Assert(token != null); 71 | 72 | var options = _options.Cookie.Build(httpContext); 73 | 74 | if (_options.Cookie.Path != null) 75 | { 76 | options.Path = _options.Cookie.Path.ToString(); 77 | } 78 | else 79 | { 80 | var pathBase = httpContext.Request.PathBase.ToString(); 81 | if (!string.IsNullOrEmpty(pathBase)) 82 | { 83 | options.Path = pathBase; 84 | } 85 | } 86 | 87 | httpContext.Response.Cookies.Append(_options.Cookie.Name, token, options); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/DefaultClaimUidExtractor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.Security.Claims; 8 | using Microsoft.Extensions.ObjectPool; 9 | 10 | namespace Microsoft.AspNetCore.Antiforgery.Internal 11 | { 12 | /// 13 | /// Default implementation of . 14 | /// 15 | public class DefaultClaimUidExtractor : IClaimUidExtractor 16 | { 17 | private readonly ObjectPool _pool; 18 | 19 | public DefaultClaimUidExtractor(ObjectPool pool) 20 | { 21 | _pool = pool; 22 | } 23 | 24 | /// 25 | public string ExtractClaimUid(ClaimsPrincipal claimsPrincipal) 26 | { 27 | Debug.Assert(claimsPrincipal != null); 28 | 29 | var uniqueIdentifierParameters = GetUniqueIdentifierParameters(claimsPrincipal.Identities); 30 | if (uniqueIdentifierParameters == null) 31 | { 32 | // No authenticated identities containing claims found. 33 | return null; 34 | } 35 | 36 | var claimUidBytes = ComputeSha256(uniqueIdentifierParameters); 37 | return Convert.ToBase64String(claimUidBytes); 38 | } 39 | 40 | public static IList GetUniqueIdentifierParameters(IEnumerable claimsIdentities) 41 | { 42 | var identitiesList = claimsIdentities as List; 43 | if (identitiesList == null) 44 | { 45 | identitiesList = new List(claimsIdentities); 46 | } 47 | 48 | for (var i = 0; i < identitiesList.Count; i++) 49 | { 50 | var identity = identitiesList[i]; 51 | if (!identity.IsAuthenticated) 52 | { 53 | continue; 54 | } 55 | 56 | var subClaim = identity.FindFirst( 57 | claim => string.Equals("sub", claim.Type, StringComparison.Ordinal)); 58 | if (subClaim != null && !string.IsNullOrEmpty(subClaim.Value)) 59 | { 60 | return new string[] 61 | { 62 | subClaim.Type, 63 | subClaim.Value, 64 | subClaim.Issuer 65 | }; 66 | } 67 | 68 | var nameIdentifierClaim = identity.FindFirst( 69 | claim => string.Equals(ClaimTypes.NameIdentifier, claim.Type, StringComparison.Ordinal)); 70 | if (nameIdentifierClaim != null && !string.IsNullOrEmpty(nameIdentifierClaim.Value)) 71 | { 72 | return new string[] 73 | { 74 | nameIdentifierClaim.Type, 75 | nameIdentifierClaim.Value, 76 | nameIdentifierClaim.Issuer 77 | }; 78 | } 79 | 80 | var upnClaim = identity.FindFirst( 81 | claim => string.Equals(ClaimTypes.Upn, claim.Type, StringComparison.Ordinal)); 82 | if (upnClaim != null && !string.IsNullOrEmpty(upnClaim.Value)) 83 | { 84 | return new string[] 85 | { 86 | upnClaim.Type, 87 | upnClaim.Value, 88 | upnClaim.Issuer 89 | }; 90 | } 91 | } 92 | 93 | // We do not understand any of the ClaimsIdentity instances, fallback on serializing all claims in every claims Identity. 94 | var allClaims = new List(); 95 | for (var i = 0; i < identitiesList.Count; i++) 96 | { 97 | if (identitiesList[i].IsAuthenticated) 98 | { 99 | allClaims.AddRange(identitiesList[i].Claims); 100 | } 101 | } 102 | 103 | if (allClaims.Count == 0) 104 | { 105 | // No authenticated identities containing claims found. 106 | return null; 107 | } 108 | 109 | allClaims.Sort((a, b) => string.Compare(a.Type, b.Type, StringComparison.Ordinal)); 110 | 111 | var identifierParameters = new List(allClaims.Count * 3); 112 | for (var i = 0; i < allClaims.Count; i++) 113 | { 114 | var claim = allClaims[i]; 115 | identifierParameters.Add(claim.Type); 116 | identifierParameters.Add(claim.Value); 117 | identifierParameters.Add(claim.Issuer); 118 | } 119 | 120 | return identifierParameters; 121 | } 122 | 123 | private byte[] ComputeSha256(IEnumerable parameters) 124 | { 125 | var serializationContext = _pool.Get(); 126 | 127 | try 128 | { 129 | var writer = serializationContext.Writer; 130 | foreach (string parameter in parameters) 131 | { 132 | writer.Write(parameter); // also writes the length as a prefix; unambiguous 133 | } 134 | 135 | writer.Flush(); 136 | 137 | var sha256 = serializationContext.Sha256; 138 | var stream = serializationContext.Stream; 139 | var bytes = sha256.ComputeHash(stream.ToArray(), 0, checked((int)stream.Length)); 140 | 141 | return bytes; 142 | } 143 | finally 144 | { 145 | _pool.Return(serializationContext); 146 | } 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/IAntiforgeryFeature.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.AspNetCore.Antiforgery.Internal 2 | { 3 | public interface IAntiforgeryFeature 4 | { 5 | AntiforgeryToken CookieToken { get; set; } 6 | 7 | bool HaveDeserializedCookieToken { get; set; } 8 | 9 | bool HaveDeserializedRequestToken { get; set; } 10 | 11 | bool HaveGeneratedNewCookieToken { get; set; } 12 | 13 | bool HaveStoredNewCookieToken { get; set; } 14 | 15 | AntiforgeryToken NewCookieToken { get; set; } 16 | 17 | string NewCookieTokenString { get; set; } 18 | 19 | AntiforgeryToken NewRequestToken { get; set; } 20 | 21 | string NewRequestTokenString { get; set; } 22 | 23 | AntiforgeryToken RequestToken { get; set; } 24 | } 25 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/IAntiforgeryTokenGenerator.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Microsoft.AspNetCore.Http; 5 | 6 | namespace Microsoft.AspNetCore.Antiforgery.Internal 7 | { 8 | /// 9 | /// Generates and validates antiforgery tokens. 10 | /// 11 | public interface IAntiforgeryTokenGenerator 12 | { 13 | /// 14 | /// Generates a new random cookie token. 15 | /// 16 | /// An . 17 | AntiforgeryToken GenerateCookieToken(); 18 | 19 | /// 20 | /// Generates a request token corresponding to . 21 | /// 22 | /// The associated with the current request. 23 | /// A valid cookie token. 24 | /// An . 25 | AntiforgeryToken GenerateRequestToken(HttpContext httpContext, AntiforgeryToken cookieToken); 26 | 27 | /// 28 | /// Attempts to validate a cookie token. 29 | /// 30 | /// A valid cookie token. 31 | /// true if the cookie token is valid, otherwise false. 32 | bool IsCookieTokenValid(AntiforgeryToken cookieToken); 33 | 34 | /// 35 | /// Attempts to validate a cookie and request token set for the given . 36 | /// 37 | /// The associated with the current request. 38 | /// A cookie token. 39 | /// A request token. 40 | /// 41 | /// Will be set to the validation message if the tokens are invalid, otherwise null. 42 | /// 43 | /// true if the tokens are valid, otherwise false. 44 | bool TryValidateTokenSet( 45 | HttpContext httpContext, 46 | AntiforgeryToken cookieToken, 47 | AntiforgeryToken requestToken, 48 | out string message); 49 | } 50 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/IAntiforgeryTokenSerializer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | namespace Microsoft.AspNetCore.Antiforgery.Internal 5 | { 6 | // Abstracts out the serialization process for an antiforgery token 7 | public interface IAntiforgeryTokenSerializer 8 | { 9 | AntiforgeryToken Deserialize(string serializedToken); 10 | string Serialize(AntiforgeryToken token); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/IAntiforgeryTokenStore.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Http; 6 | 7 | namespace Microsoft.AspNetCore.Antiforgery.Internal 8 | { 9 | public interface IAntiforgeryTokenStore 10 | { 11 | string GetCookieToken(HttpContext httpContext); 12 | 13 | /// 14 | /// Gets the cookie and request tokens from the request. 15 | /// 16 | /// The for the current request. 17 | /// The . 18 | Task GetRequestTokensAsync(HttpContext httpContext); 19 | 20 | void SaveCookieToken(HttpContext httpContext, string token); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Internal/IClaimUidExtractor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Security.Claims; 6 | 7 | namespace Microsoft.AspNetCore.Antiforgery.Internal 8 | { 9 | /// 10 | /// This interface can extract unique identifers for a . 11 | /// 12 | public interface IClaimUidExtractor 13 | { 14 | /// 15 | /// Extracts claims identifier. 16 | /// 17 | /// The . 18 | /// The claims identifier. 19 | string ExtractClaimUid(ClaimsPrincipal claimsPrincipal); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Microsoft.AspNetCore.Antiforgery.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | An antiforgery system for ASP.NET Core designed to generate and validate tokens to prevent Cross-Site Request Forgery attacks. 5 | netstandard2.0 6 | $(NoWarn);CS1591 7 | true 8 | aspnetcore;antiforgery 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System.Runtime.CompilerServices; 5 | 6 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] 7 | [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Antiforgery.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] 8 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace Microsoft.AspNetCore.Antiforgery 3 | { 4 | using System.Globalization; 5 | using System.Reflection; 6 | using System.Resources; 7 | 8 | internal static class Resources 9 | { 10 | private static readonly ResourceManager _resourceManager 11 | = new ResourceManager("Microsoft.AspNetCore.Antiforgery.Resources", typeof(Resources).GetTypeInfo().Assembly); 12 | 13 | /// 14 | /// The provided identity of type '{0}' is marked {1} = {2} but does not have a value for {3}. By default, the antiforgery system requires that all authenticated identities have a unique {3}. If it is not possible to provide a unique {3} for this identity, consider extending {4} by overriding the {5} or a custom type that can provide some form of unique identifier for the current user. 15 | /// 16 | internal static string AntiforgeryTokenValidator_AuthenticatedUserWithoutUsername 17 | { 18 | get => GetString("AntiforgeryTokenValidator_AuthenticatedUserWithoutUsername"); 19 | } 20 | 21 | /// 22 | /// The provided identity of type '{0}' is marked {1} = {2} but does not have a value for {3}. By default, the antiforgery system requires that all authenticated identities have a unique {3}. If it is not possible to provide a unique {3} for this identity, consider extending {4} by overriding the {5} or a custom type that can provide some form of unique identifier for the current user. 23 | /// 24 | internal static string FormatAntiforgeryTokenValidator_AuthenticatedUserWithoutUsername(object p0, object p1, object p2, object p3, object p4, object p5) 25 | => string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryTokenValidator_AuthenticatedUserWithoutUsername"), p0, p1, p2, p3, p4, p5); 26 | 27 | /// 28 | /// The provided antiforgery token failed a custom data check. 29 | /// 30 | internal static string AntiforgeryToken_AdditionalDataCheckFailed 31 | { 32 | get => GetString("AntiforgeryToken_AdditionalDataCheckFailed"); 33 | } 34 | 35 | /// 36 | /// The provided antiforgery token failed a custom data check. 37 | /// 38 | internal static string FormatAntiforgeryToken_AdditionalDataCheckFailed() 39 | => GetString("AntiforgeryToken_AdditionalDataCheckFailed"); 40 | 41 | /// 42 | /// The provided antiforgery token was meant for a different claims-based user than the current user. 43 | /// 44 | internal static string AntiforgeryToken_ClaimUidMismatch 45 | { 46 | get => GetString("AntiforgeryToken_ClaimUidMismatch"); 47 | } 48 | 49 | /// 50 | /// The provided antiforgery token was meant for a different claims-based user than the current user. 51 | /// 52 | internal static string FormatAntiforgeryToken_ClaimUidMismatch() 53 | => GetString("AntiforgeryToken_ClaimUidMismatch"); 54 | 55 | /// 56 | /// The antiforgery token could not be decrypted. 57 | /// 58 | internal static string AntiforgeryToken_DeserializationFailed 59 | { 60 | get => GetString("AntiforgeryToken_DeserializationFailed"); 61 | } 62 | 63 | /// 64 | /// The antiforgery token could not be decrypted. 65 | /// 66 | internal static string FormatAntiforgeryToken_DeserializationFailed() 67 | => GetString("AntiforgeryToken_DeserializationFailed"); 68 | 69 | /// 70 | /// The antiforgery cookie token and request token do not match. 71 | /// 72 | internal static string AntiforgeryToken_SecurityTokenMismatch 73 | { 74 | get => GetString("AntiforgeryToken_SecurityTokenMismatch"); 75 | } 76 | 77 | /// 78 | /// The antiforgery cookie token and request token do not match. 79 | /// 80 | internal static string FormatAntiforgeryToken_SecurityTokenMismatch() 81 | => GetString("AntiforgeryToken_SecurityTokenMismatch"); 82 | 83 | /// 84 | /// Validation of the provided antiforgery token failed. The cookie token and the request token were swapped. 85 | /// 86 | internal static string AntiforgeryToken_TokensSwapped 87 | { 88 | get => GetString("AntiforgeryToken_TokensSwapped"); 89 | } 90 | 91 | /// 92 | /// Validation of the provided antiforgery token failed. The cookie token and the request token were swapped. 93 | /// 94 | internal static string FormatAntiforgeryToken_TokensSwapped() 95 | => GetString("AntiforgeryToken_TokensSwapped"); 96 | 97 | /// 98 | /// The provided antiforgery token was meant for user "{0}", but the current user is "{1}". 99 | /// 100 | internal static string AntiforgeryToken_UsernameMismatch 101 | { 102 | get => GetString("AntiforgeryToken_UsernameMismatch"); 103 | } 104 | 105 | /// 106 | /// The provided antiforgery token was meant for user "{0}", but the current user is "{1}". 107 | /// 108 | internal static string FormatAntiforgeryToken_UsernameMismatch(object p0, object p1) 109 | => string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryToken_UsernameMismatch"), p0, p1); 110 | 111 | /// 112 | /// The antiforgery cookie token is invalid. 113 | /// 114 | internal static string Antiforgery_CookieToken_IsInvalid 115 | { 116 | get => GetString("Antiforgery_CookieToken_IsInvalid"); 117 | } 118 | 119 | /// 120 | /// The antiforgery cookie token is invalid. 121 | /// 122 | internal static string FormatAntiforgery_CookieToken_IsInvalid() 123 | => GetString("Antiforgery_CookieToken_IsInvalid"); 124 | 125 | /// 126 | /// The required antiforgery cookie "{0}" is not present. 127 | /// 128 | internal static string Antiforgery_CookieToken_MustBeProvided 129 | { 130 | get => GetString("Antiforgery_CookieToken_MustBeProvided"); 131 | } 132 | 133 | /// 134 | /// The required antiforgery cookie "{0}" is not present. 135 | /// 136 | internal static string FormatAntiforgery_CookieToken_MustBeProvided(object p0) 137 | => string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_CookieToken_MustBeProvided"), p0); 138 | 139 | /// 140 | /// The required antiforgery cookie token must be provided. 141 | /// 142 | internal static string Antiforgery_CookieToken_MustBeProvided_Generic 143 | { 144 | get => GetString("Antiforgery_CookieToken_MustBeProvided_Generic"); 145 | } 146 | 147 | /// 148 | /// The required antiforgery cookie token must be provided. 149 | /// 150 | internal static string FormatAntiforgery_CookieToken_MustBeProvided_Generic() 151 | => GetString("Antiforgery_CookieToken_MustBeProvided_Generic"); 152 | 153 | /// 154 | /// The required antiforgery form field "{0}" is not present. 155 | /// 156 | internal static string Antiforgery_FormToken_MustBeProvided 157 | { 158 | get => GetString("Antiforgery_FormToken_MustBeProvided"); 159 | } 160 | 161 | /// 162 | /// The required antiforgery form field "{0}" is not present. 163 | /// 164 | internal static string FormatAntiforgery_FormToken_MustBeProvided(object p0) 165 | => string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_FormToken_MustBeProvided"), p0); 166 | 167 | /// 168 | /// The required antiforgery header value "{0}" is not present. 169 | /// 170 | internal static string Antiforgery_HeaderToken_MustBeProvided 171 | { 172 | get => GetString("Antiforgery_HeaderToken_MustBeProvided"); 173 | } 174 | 175 | /// 176 | /// The required antiforgery header value "{0}" is not present. 177 | /// 178 | internal static string FormatAntiforgery_HeaderToken_MustBeProvided(object p0) 179 | => string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_HeaderToken_MustBeProvided"), p0); 180 | 181 | /// 182 | /// The required antiforgery request token was not provided in either form field "{0}" or header value "{1}". 183 | /// 184 | internal static string Antiforgery_RequestToken_MustBeProvided 185 | { 186 | get => GetString("Antiforgery_RequestToken_MustBeProvided"); 187 | } 188 | 189 | /// 190 | /// The required antiforgery request token was not provided in either form field "{0}" or header value "{1}". 191 | /// 192 | internal static string FormatAntiforgery_RequestToken_MustBeProvided(object p0, object p1) 193 | => string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_RequestToken_MustBeProvided"), p0, p1); 194 | 195 | /// 196 | /// The required antiforgery request token must be provided. 197 | /// 198 | internal static string Antiforgery_RequestToken_MustBeProvided_Generic 199 | { 200 | get => GetString("Antiforgery_RequestToken_MustBeProvided_Generic"); 201 | } 202 | 203 | /// 204 | /// The required antiforgery request token must be provided. 205 | /// 206 | internal static string FormatAntiforgery_RequestToken_MustBeProvided_Generic() 207 | => GetString("Antiforgery_RequestToken_MustBeProvided_Generic"); 208 | 209 | /// 210 | /// The antiforgery system has the configuration value {optionName} = {value}, but the current request is not an SSL request. 211 | /// 212 | internal static string Antiforgery_RequiresSSL 213 | { 214 | get => GetString("Antiforgery_RequiresSSL"); 215 | } 216 | 217 | /// 218 | /// The antiforgery system has the configuration value {optionName} = {value}, but the current request is not an SSL request. 219 | /// 220 | internal static string FormatAntiforgery_RequiresSSL(object optionName, object value) 221 | => string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_RequiresSSL", "optionName", "value"), optionName, value); 222 | 223 | /// 224 | /// Value cannot be null or empty. 225 | /// 226 | internal static string ArgumentCannotBeNullOrEmpty 227 | { 228 | get => GetString("ArgumentCannotBeNullOrEmpty"); 229 | } 230 | 231 | /// 232 | /// Value cannot be null or empty. 233 | /// 234 | internal static string FormatArgumentCannotBeNullOrEmpty() 235 | => GetString("ArgumentCannotBeNullOrEmpty"); 236 | 237 | private static string GetString(string name, params string[] formatterNames) 238 | { 239 | var value = _resourceManager.GetString(name); 240 | 241 | System.Diagnostics.Debug.Assert(value != null); 242 | 243 | if (formatterNames != null) 244 | { 245 | for (var i = 0; i < formatterNames.Length; i++) 246 | { 247 | value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); 248 | } 249 | } 250 | 251 | return value; 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | The provided identity of type '{0}' is marked {1} = {2} but does not have a value for {3}. By default, the antiforgery system requires that all authenticated identities have a unique {3}. If it is not possible to provide a unique {3} for this identity, consider extending {4} by overriding the {5} or a custom type that can provide some form of unique identifier for the current user. 122 | 0 = typeof(identity), 1 = nameof(IsAuthenticated), 2 = bool.TrueString, 3 = nameof(Name), 4 = nameof(IAdditionalDataProvider), 5 = nameof(DefaultAdditionalDataProvider) 123 | 124 | 125 | The provided antiforgery token failed a custom data check. 126 | 127 | 128 | The provided antiforgery token was meant for a different claims-based user than the current user. 129 | 130 | 131 | The antiforgery token could not be decrypted. 132 | 133 | 134 | The antiforgery cookie token and request token do not match. 135 | 136 | 137 | Validation of the provided antiforgery token failed. The cookie token and the request token were swapped. 138 | 139 | 140 | The provided antiforgery token was meant for user "{0}", but the current user is "{1}". 141 | 142 | 143 | The antiforgery cookie token is invalid. 144 | 145 | 146 | The required antiforgery cookie "{0}" is not present. 147 | 148 | 149 | The required antiforgery cookie token must be provided. 150 | 151 | 152 | The required antiforgery form field "{0}" is not present. 153 | 154 | 155 | The required antiforgery header value "{0}" is not present. 156 | 157 | 158 | The required antiforgery request token was not provided in either form field "{0}" or header value "{1}". 159 | 160 | 161 | The required antiforgery request token must be provided. 162 | 163 | 164 | The antiforgery system has the configuration value {optionName} = {value}, but the current request is not an SSL request. 165 | 166 | 167 | Value cannot be null or empty. 168 | 169 | -------------------------------------------------------------------------------- /src/Microsoft.AspNetCore.Antiforgery/baseline.netcore.json: -------------------------------------------------------------------------------- 1 | { 2 | "AssemblyIdentity": "Microsoft.AspNetCore.Antiforgery, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", 3 | "Types": [ 4 | { 5 | "Name": "Microsoft.Extensions.DependencyInjection.AntiforgeryServiceCollectionExtensions", 6 | "Visibility": "Public", 7 | "Kind": "Class", 8 | "Abstract": true, 9 | "Static": true, 10 | "Sealed": true, 11 | "ImplementedInterfaces": [], 12 | "Members": [ 13 | { 14 | "Kind": "Method", 15 | "Name": "AddAntiforgery", 16 | "Parameters": [ 17 | { 18 | "Name": "services", 19 | "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" 20 | } 21 | ], 22 | "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", 23 | "Static": true, 24 | "Extension": true, 25 | "Visibility": "Public", 26 | "GenericParameter": [] 27 | }, 28 | { 29 | "Kind": "Method", 30 | "Name": "AddAntiforgery", 31 | "Parameters": [ 32 | { 33 | "Name": "services", 34 | "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" 35 | }, 36 | { 37 | "Name": "setupAction", 38 | "Type": "System.Action" 39 | } 40 | ], 41 | "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", 42 | "Static": true, 43 | "Extension": true, 44 | "Visibility": "Public", 45 | "GenericParameter": [] 46 | } 47 | ], 48 | "GenericParameters": [] 49 | }, 50 | { 51 | "Name": "Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions", 52 | "Visibility": "Public", 53 | "Kind": "Class", 54 | "ImplementedInterfaces": [], 55 | "Members": [ 56 | { 57 | "Kind": "Method", 58 | "Name": "get_Cookie", 59 | "Parameters": [], 60 | "ReturnType": "Microsoft.AspNetCore.Http.CookieBuilder", 61 | "Visibility": "Public", 62 | "GenericParameter": [] 63 | }, 64 | { 65 | "Kind": "Method", 66 | "Name": "set_Cookie", 67 | "Parameters": [ 68 | { 69 | "Name": "value", 70 | "Type": "Microsoft.AspNetCore.Http.CookieBuilder" 71 | } 72 | ], 73 | "ReturnType": "System.Void", 74 | "Visibility": "Public", 75 | "GenericParameter": [] 76 | }, 77 | { 78 | "Kind": "Method", 79 | "Name": "get_FormFieldName", 80 | "Parameters": [], 81 | "ReturnType": "System.String", 82 | "Visibility": "Public", 83 | "GenericParameter": [] 84 | }, 85 | { 86 | "Kind": "Method", 87 | "Name": "set_FormFieldName", 88 | "Parameters": [ 89 | { 90 | "Name": "value", 91 | "Type": "System.String" 92 | } 93 | ], 94 | "ReturnType": "System.Void", 95 | "Visibility": "Public", 96 | "GenericParameter": [] 97 | }, 98 | { 99 | "Kind": "Method", 100 | "Name": "get_HeaderName", 101 | "Parameters": [], 102 | "ReturnType": "System.String", 103 | "Visibility": "Public", 104 | "GenericParameter": [] 105 | }, 106 | { 107 | "Kind": "Method", 108 | "Name": "set_HeaderName", 109 | "Parameters": [ 110 | { 111 | "Name": "value", 112 | "Type": "System.String" 113 | } 114 | ], 115 | "ReturnType": "System.Void", 116 | "Visibility": "Public", 117 | "GenericParameter": [] 118 | }, 119 | { 120 | "Kind": "Method", 121 | "Name": "get_SuppressXFrameOptionsHeader", 122 | "Parameters": [], 123 | "ReturnType": "System.Boolean", 124 | "Visibility": "Public", 125 | "GenericParameter": [] 126 | }, 127 | { 128 | "Kind": "Method", 129 | "Name": "set_SuppressXFrameOptionsHeader", 130 | "Parameters": [ 131 | { 132 | "Name": "value", 133 | "Type": "System.Boolean" 134 | } 135 | ], 136 | "ReturnType": "System.Void", 137 | "Visibility": "Public", 138 | "GenericParameter": [] 139 | }, 140 | { 141 | "Kind": "Method", 142 | "Name": "get_CookieName", 143 | "Parameters": [], 144 | "ReturnType": "System.String", 145 | "Visibility": "Public", 146 | "GenericParameter": [] 147 | }, 148 | { 149 | "Kind": "Method", 150 | "Name": "set_CookieName", 151 | "Parameters": [ 152 | { 153 | "Name": "value", 154 | "Type": "System.String" 155 | } 156 | ], 157 | "ReturnType": "System.Void", 158 | "Visibility": "Public", 159 | "GenericParameter": [] 160 | }, 161 | { 162 | "Kind": "Method", 163 | "Name": "get_CookiePath", 164 | "Parameters": [], 165 | "ReturnType": "System.Nullable", 166 | "Visibility": "Public", 167 | "GenericParameter": [] 168 | }, 169 | { 170 | "Kind": "Method", 171 | "Name": "set_CookiePath", 172 | "Parameters": [ 173 | { 174 | "Name": "value", 175 | "Type": "System.Nullable" 176 | } 177 | ], 178 | "ReturnType": "System.Void", 179 | "Visibility": "Public", 180 | "GenericParameter": [] 181 | }, 182 | { 183 | "Kind": "Method", 184 | "Name": "get_CookieDomain", 185 | "Parameters": [], 186 | "ReturnType": "System.String", 187 | "Visibility": "Public", 188 | "GenericParameter": [] 189 | }, 190 | { 191 | "Kind": "Method", 192 | "Name": "set_CookieDomain", 193 | "Parameters": [ 194 | { 195 | "Name": "value", 196 | "Type": "System.String" 197 | } 198 | ], 199 | "ReturnType": "System.Void", 200 | "Visibility": "Public", 201 | "GenericParameter": [] 202 | }, 203 | { 204 | "Kind": "Method", 205 | "Name": "get_RequireSsl", 206 | "Parameters": [], 207 | "ReturnType": "System.Boolean", 208 | "Visibility": "Public", 209 | "GenericParameter": [] 210 | }, 211 | { 212 | "Kind": "Method", 213 | "Name": "set_RequireSsl", 214 | "Parameters": [ 215 | { 216 | "Name": "value", 217 | "Type": "System.Boolean" 218 | } 219 | ], 220 | "ReturnType": "System.Void", 221 | "Visibility": "Public", 222 | "GenericParameter": [] 223 | }, 224 | { 225 | "Kind": "Constructor", 226 | "Name": ".ctor", 227 | "Parameters": [], 228 | "Visibility": "Public", 229 | "GenericParameter": [] 230 | }, 231 | { 232 | "Kind": "Field", 233 | "Name": "DefaultCookiePrefix", 234 | "Parameters": [], 235 | "ReturnType": "System.String", 236 | "Static": true, 237 | "ReadOnly": true, 238 | "Visibility": "Public", 239 | "GenericParameter": [] 240 | } 241 | ], 242 | "GenericParameters": [] 243 | }, 244 | { 245 | "Name": "Microsoft.AspNetCore.Antiforgery.AntiforgeryTokenSet", 246 | "Visibility": "Public", 247 | "Kind": "Class", 248 | "ImplementedInterfaces": [], 249 | "Members": [ 250 | { 251 | "Kind": "Method", 252 | "Name": "get_RequestToken", 253 | "Parameters": [], 254 | "ReturnType": "System.String", 255 | "Visibility": "Public", 256 | "GenericParameter": [] 257 | }, 258 | { 259 | "Kind": "Method", 260 | "Name": "get_FormFieldName", 261 | "Parameters": [], 262 | "ReturnType": "System.String", 263 | "Visibility": "Public", 264 | "GenericParameter": [] 265 | }, 266 | { 267 | "Kind": "Method", 268 | "Name": "get_HeaderName", 269 | "Parameters": [], 270 | "ReturnType": "System.String", 271 | "Visibility": "Public", 272 | "GenericParameter": [] 273 | }, 274 | { 275 | "Kind": "Method", 276 | "Name": "get_CookieToken", 277 | "Parameters": [], 278 | "ReturnType": "System.String", 279 | "Visibility": "Public", 280 | "GenericParameter": [] 281 | }, 282 | { 283 | "Kind": "Constructor", 284 | "Name": ".ctor", 285 | "Parameters": [ 286 | { 287 | "Name": "requestToken", 288 | "Type": "System.String" 289 | }, 290 | { 291 | "Name": "cookieToken", 292 | "Type": "System.String" 293 | }, 294 | { 295 | "Name": "formFieldName", 296 | "Type": "System.String" 297 | }, 298 | { 299 | "Name": "headerName", 300 | "Type": "System.String" 301 | } 302 | ], 303 | "Visibility": "Public", 304 | "GenericParameter": [] 305 | } 306 | ], 307 | "GenericParameters": [] 308 | }, 309 | { 310 | "Name": "Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException", 311 | "Visibility": "Public", 312 | "Kind": "Class", 313 | "BaseType": "System.Exception", 314 | "ImplementedInterfaces": [], 315 | "Members": [ 316 | { 317 | "Kind": "Constructor", 318 | "Name": ".ctor", 319 | "Parameters": [ 320 | { 321 | "Name": "message", 322 | "Type": "System.String" 323 | } 324 | ], 325 | "Visibility": "Public", 326 | "GenericParameter": [] 327 | }, 328 | { 329 | "Kind": "Constructor", 330 | "Name": ".ctor", 331 | "Parameters": [ 332 | { 333 | "Name": "message", 334 | "Type": "System.String" 335 | }, 336 | { 337 | "Name": "innerException", 338 | "Type": "System.Exception" 339 | } 340 | ], 341 | "Visibility": "Public", 342 | "GenericParameter": [] 343 | } 344 | ], 345 | "GenericParameters": [] 346 | }, 347 | { 348 | "Name": "Microsoft.AspNetCore.Antiforgery.IAntiforgery", 349 | "Visibility": "Public", 350 | "Kind": "Interface", 351 | "Abstract": true, 352 | "ImplementedInterfaces": [], 353 | "Members": [ 354 | { 355 | "Kind": "Method", 356 | "Name": "GetAndStoreTokens", 357 | "Parameters": [ 358 | { 359 | "Name": "httpContext", 360 | "Type": "Microsoft.AspNetCore.Http.HttpContext" 361 | } 362 | ], 363 | "ReturnType": "Microsoft.AspNetCore.Antiforgery.AntiforgeryTokenSet", 364 | "GenericParameter": [] 365 | }, 366 | { 367 | "Kind": "Method", 368 | "Name": "GetTokens", 369 | "Parameters": [ 370 | { 371 | "Name": "httpContext", 372 | "Type": "Microsoft.AspNetCore.Http.HttpContext" 373 | } 374 | ], 375 | "ReturnType": "Microsoft.AspNetCore.Antiforgery.AntiforgeryTokenSet", 376 | "GenericParameter": [] 377 | }, 378 | { 379 | "Kind": "Method", 380 | "Name": "IsRequestValidAsync", 381 | "Parameters": [ 382 | { 383 | "Name": "httpContext", 384 | "Type": "Microsoft.AspNetCore.Http.HttpContext" 385 | } 386 | ], 387 | "ReturnType": "System.Threading.Tasks.Task", 388 | "GenericParameter": [] 389 | }, 390 | { 391 | "Kind": "Method", 392 | "Name": "ValidateRequestAsync", 393 | "Parameters": [ 394 | { 395 | "Name": "httpContext", 396 | "Type": "Microsoft.AspNetCore.Http.HttpContext" 397 | } 398 | ], 399 | "ReturnType": "System.Threading.Tasks.Task", 400 | "GenericParameter": [] 401 | }, 402 | { 403 | "Kind": "Method", 404 | "Name": "SetCookieTokenAndHeader", 405 | "Parameters": [ 406 | { 407 | "Name": "httpContext", 408 | "Type": "Microsoft.AspNetCore.Http.HttpContext" 409 | } 410 | ], 411 | "ReturnType": "System.Void", 412 | "GenericParameter": [] 413 | } 414 | ], 415 | "GenericParameters": [] 416 | }, 417 | { 418 | "Name": "Microsoft.AspNetCore.Antiforgery.IAntiforgeryAdditionalDataProvider", 419 | "Visibility": "Public", 420 | "Kind": "Interface", 421 | "Abstract": true, 422 | "ImplementedInterfaces": [], 423 | "Members": [ 424 | { 425 | "Kind": "Method", 426 | "Name": "GetAdditionalData", 427 | "Parameters": [ 428 | { 429 | "Name": "context", 430 | "Type": "Microsoft.AspNetCore.Http.HttpContext" 431 | } 432 | ], 433 | "ReturnType": "System.String", 434 | "GenericParameter": [] 435 | }, 436 | { 437 | "Kind": "Method", 438 | "Name": "ValidateAdditionalData", 439 | "Parameters": [ 440 | { 441 | "Name": "context", 442 | "Type": "Microsoft.AspNetCore.Http.HttpContext" 443 | }, 444 | { 445 | "Name": "additionalData", 446 | "Type": "System.String" 447 | } 448 | ], 449 | "ReturnType": "System.Boolean", 450 | "GenericParameter": [] 451 | } 452 | ], 453 | "GenericParameters": [] 454 | } 455 | ] 456 | } -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | netcoreapp2.2 6 | $(DeveloperBuildTestTfms) 7 | 8 | $(StandardTestTfms);net461 9 | 10 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Antiforgery.Test/Internal/AntiforgeryOptionsSetupTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Microsoft.AspNetCore.DataProtection; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Options; 8 | using Xunit; 9 | 10 | namespace Microsoft.AspNetCore.Antiforgery.Internal 11 | { 12 | public class AntiforgeryOptionsSetupTest 13 | { 14 | [Theory] 15 | [InlineData("HelloWorldApp", ".AspNetCore.Antiforgery.tGmK82_ckDw")] 16 | [InlineData("TodoCalendar", ".AspNetCore.Antiforgery.7mK1hBEBwYs")] 17 | public void AntiforgeryOptionsSetup_SetsDefaultCookieName_BasedOnApplicationId( 18 | string applicationId, 19 | string expectedCookieName) 20 | { 21 | // Arrange 22 | var serviceCollection = new ServiceCollection(); 23 | serviceCollection.AddAntiforgery(); 24 | serviceCollection 25 | .AddDataProtection() 26 | .SetApplicationName(applicationId); 27 | 28 | var services = serviceCollection.BuildServiceProvider(); 29 | var options = services.GetRequiredService>(); 30 | 31 | // Act 32 | var cookieName = options.Value.Cookie.Name; 33 | 34 | // Assert 35 | Assert.Equal(expectedCookieName, cookieName); 36 | } 37 | 38 | [Fact] 39 | public void AntiforgeryOptionsSetup_UserOptionsSetup_CanSetCookieName() 40 | { 41 | // Arrange 42 | var serviceCollection = new ServiceCollection(); 43 | serviceCollection.Configure(o => 44 | { 45 | Assert.Null(o.Cookie.Name); 46 | o.Cookie.Name = "antiforgery"; 47 | }); 48 | serviceCollection.AddAntiforgery(); 49 | serviceCollection 50 | .AddDataProtection() 51 | .SetApplicationName("HelloWorldApp"); 52 | 53 | var services = serviceCollection.BuildServiceProvider(); 54 | var options = services.GetRequiredService>(); 55 | 56 | // Act 57 | var cookieName = options.Value.Cookie.Name; 58 | 59 | // Assert 60 | Assert.Equal("antiforgery", cookieName); 61 | } 62 | 63 | [Fact] 64 | public void AntiforgeryOptions_SetsCookieSecurePolicy_ToNone_ByDefault() 65 | { 66 | // Arrange & Act 67 | var options = new AntiforgeryOptions(); 68 | 69 | // Assert 70 | Assert.Equal(CookieSecurePolicy.None, options.Cookie.SecurePolicy); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Antiforgery.Test/Internal/AntiforgeryTokenTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Xunit; 5 | 6 | namespace Microsoft.AspNetCore.Antiforgery.Internal 7 | { 8 | public class AntiforgeryTokenTest 9 | { 10 | [Fact] 11 | public void AdditionalDataProperty() 12 | { 13 | // Arrange 14 | var token = new AntiforgeryToken(); 15 | 16 | // Act & assert - 1 17 | Assert.Equal("", token.AdditionalData); 18 | 19 | // Act & assert - 2 20 | token.AdditionalData = "additional data"; 21 | Assert.Equal("additional data", token.AdditionalData); 22 | 23 | // Act & assert - 3 24 | token.AdditionalData = null; 25 | Assert.Equal("", token.AdditionalData); 26 | } 27 | 28 | [Fact] 29 | public void ClaimUidProperty() 30 | { 31 | // Arrange 32 | var token = new AntiforgeryToken(); 33 | 34 | // Act & assert - 1 35 | Assert.Null(token.ClaimUid); 36 | 37 | // Act & assert - 2 38 | BinaryBlob blob = new BinaryBlob(32); 39 | token.ClaimUid = blob; 40 | Assert.Equal(blob, token.ClaimUid); 41 | 42 | // Act & assert - 3 43 | token.ClaimUid = null; 44 | Assert.Null(token.ClaimUid); 45 | } 46 | 47 | [Fact] 48 | public void IsCookieTokenProperty() 49 | { 50 | // Arrange 51 | var token = new AntiforgeryToken(); 52 | 53 | // Act & assert - 1 54 | Assert.False(token.IsCookieToken); 55 | 56 | // Act & assert - 2 57 | token.IsCookieToken = true; 58 | Assert.True(token.IsCookieToken); 59 | 60 | // Act & assert - 3 61 | token.IsCookieToken = false; 62 | Assert.False(token.IsCookieToken); 63 | } 64 | 65 | [Fact] 66 | public void UsernameProperty() 67 | { 68 | // Arrange 69 | var token = new AntiforgeryToken(); 70 | 71 | // Act & assert - 1 72 | Assert.Equal("", token.Username); 73 | 74 | // Act & assert - 2 75 | token.Username = "my username"; 76 | Assert.Equal("my username", token.Username); 77 | 78 | // Act & assert - 3 79 | token.Username = null; 80 | Assert.Equal("", token.Username); 81 | } 82 | 83 | [Fact] 84 | public void SecurityTokenProperty_GetsAutopopulated() 85 | { 86 | // Arrange 87 | var token = new AntiforgeryToken(); 88 | 89 | // Act 90 | var securityToken = token.SecurityToken; 91 | 92 | // Assert 93 | Assert.NotNull(securityToken); 94 | Assert.Equal(AntiforgeryToken.SecurityTokenBitLength, securityToken.BitLength); 95 | 96 | // check that we're not making a new one each property call 97 | Assert.Equal(securityToken, token.SecurityToken); 98 | } 99 | 100 | [Fact] 101 | public void SecurityTokenProperty_PropertySetter_DoesNotUseDefaults() 102 | { 103 | // Arrange 104 | var token = new AntiforgeryToken(); 105 | 106 | // Act 107 | var securityToken = new BinaryBlob(64); 108 | token.SecurityToken = securityToken; 109 | 110 | // Assert 111 | Assert.Equal(securityToken, token.SecurityToken); 112 | } 113 | 114 | [Fact] 115 | public void SecurityTokenProperty_PropertySetter_DoesNotAllowNulls() 116 | { 117 | // Arrange 118 | var token = new AntiforgeryToken(); 119 | 120 | // Act 121 | token.SecurityToken = null; 122 | var securityToken = token.SecurityToken; 123 | 124 | // Assert 125 | Assert.NotNull(securityToken); 126 | Assert.Equal(AntiforgeryToken.SecurityTokenBitLength, securityToken.BitLength); 127 | 128 | // check that we're not making a new one each property call 129 | Assert.Equal(securityToken, token.SecurityToken); 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Antiforgery.Test/Internal/BinaryBlobTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using Xunit; 6 | 7 | namespace Microsoft.AspNetCore.Antiforgery.Internal 8 | { 9 | public class BinaryBlobTest 10 | { 11 | [Fact] 12 | public void Ctor_BitLength() 13 | { 14 | // Act 15 | var blob = new BinaryBlob(bitLength: 64); 16 | var data = blob.GetData(); 17 | 18 | // Assert 19 | Assert.Equal(64, blob.BitLength); 20 | Assert.Equal(64 / 8, data.Length); 21 | Assert.NotEqual(new byte[64 / 8], data); // should not be a zero-filled array 22 | } 23 | 24 | [Theory] 25 | [InlineData(24)] 26 | [InlineData(33)] 27 | public void Ctor_BitLength_Bad(int bitLength) 28 | { 29 | // Act & assert 30 | var ex = Assert.Throws(() => new BinaryBlob(bitLength)); 31 | Assert.Equal("bitLength", ex.ParamName); 32 | } 33 | 34 | [Fact] 35 | public void Ctor_BitLength_ProducesDifferentValues() 36 | { 37 | // Act 38 | var blobA = new BinaryBlob(bitLength: 64); 39 | var blobB = new BinaryBlob(bitLength: 64); 40 | 41 | // Assert 42 | Assert.NotEqual(blobA.GetData(), blobB.GetData()); 43 | } 44 | 45 | [Fact] 46 | public void Ctor_Data() 47 | { 48 | // Arrange 49 | var expectedData = new byte[] { 0x01, 0x02, 0x03, 0x04 }; 50 | 51 | // Act 52 | var blob = new BinaryBlob(32, expectedData); 53 | 54 | // Assert 55 | Assert.Equal(32, blob.BitLength); 56 | Assert.Equal(expectedData, blob.GetData()); 57 | } 58 | 59 | [Theory] 60 | [InlineData((object[])null)] 61 | [InlineData(new byte[] { 0x01, 0x02, 0x03 })] 62 | public void Ctor_Data_Bad(byte[] data) 63 | { 64 | // Act & assert 65 | var ex = Assert.Throws(() => new BinaryBlob(32, data)); 66 | Assert.Equal("data", ex.ParamName); 67 | } 68 | 69 | [Fact] 70 | public void Equals_DifferentData_ReturnsFalse() 71 | { 72 | // Arrange 73 | object blobA = new BinaryBlob(32, new byte[] { 0x01, 0x02, 0x03, 0x04 }); 74 | object blobB = new BinaryBlob(32, new byte[] { 0x04, 0x03, 0x02, 0x01 }); 75 | 76 | // Act & assert 77 | Assert.NotEqual(blobA, blobB); 78 | } 79 | 80 | [Fact] 81 | public void Equals_NotABlob_ReturnsFalse() 82 | { 83 | // Arrange 84 | object blobA = new BinaryBlob(32); 85 | object blobB = "hello"; 86 | 87 | // Act & assert 88 | Assert.NotEqual(blobA, blobB); 89 | } 90 | 91 | [Fact] 92 | public void Equals_Null_ReturnsFalse() 93 | { 94 | // Arrange 95 | object blobA = new BinaryBlob(32); 96 | object blobB = null; 97 | 98 | // Act & assert 99 | Assert.NotEqual(blobA, blobB); 100 | } 101 | 102 | [Fact] 103 | public void Equals_SameData_ReturnsTrue() 104 | { 105 | // Arrange 106 | object blobA = new BinaryBlob(32, new byte[] { 0x01, 0x02, 0x03, 0x04 }); 107 | object blobB = new BinaryBlob(32, new byte[] { 0x01, 0x02, 0x03, 0x04 }); 108 | 109 | // Act & assert 110 | Assert.Equal(blobA, blobB); 111 | } 112 | 113 | [Fact] 114 | public void GetHashCodeTest() 115 | { 116 | // Arrange 117 | var blobData = new byte[] { 0x01, 0x02, 0x03, 0x04 }; 118 | var expectedHashCode = BitConverter.ToInt32(blobData, 0); 119 | 120 | var blob = new BinaryBlob(32, blobData); 121 | 122 | // Act 123 | var actualHashCode = blob.GetHashCode(); 124 | 125 | // Assert 126 | Assert.Equal(expectedHashCode, actualHashCode); 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTokenSerializerTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using Microsoft.AspNetCore.DataProtection; 8 | using Microsoft.Extensions.ObjectPool; 9 | using Moq; 10 | using Xunit; 11 | 12 | namespace Microsoft.AspNetCore.Antiforgery.Internal 13 | { 14 | public class DefaultAntiforgeryTokenSerializerTest 15 | { 16 | private static readonly Mock _dataProtector = GetDataProtector(); 17 | private static readonly BinaryBlob _claimUid = new BinaryBlob(256, new byte[] { 0x6F, 0x16, 0x48, 0xE9, 0x72, 0x49, 0xAA, 0x58, 0x75, 0x40, 0x36, 0xA6, 0x7E, 0x24, 0x8C, 0xF0, 0x44, 0xF0, 0x7E, 0xCF, 0xB0, 0xED, 0x38, 0x75, 0x56, 0xCE, 0x02, 0x9A, 0x4F, 0x9A, 0x40, 0xE0 }); 18 | private static readonly BinaryBlob _securityToken = new BinaryBlob(128, new byte[] { 0x70, 0x5E, 0xED, 0xCC, 0x7D, 0x42, 0xF1, 0xD6, 0xB3, 0xB9, 0x8A, 0x59, 0x36, 0x25, 0xBB, 0x4C }); 19 | private static readonly ObjectPool _pool = 20 | new DefaultObjectPoolProvider().Create(new AntiforgerySerializationContextPooledObjectPolicy()); 21 | private const byte _salt = 0x05; 22 | 23 | [Theory] 24 | [InlineData( 25 | "01" // Version 26 | + "705EEDCC7D42F1D6B3B9" // SecurityToken 27 | // (WRONG!) Stream ends too early 28 | )] 29 | [InlineData( 30 | "01" // Version 31 | + "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken 32 | + "01" // IsCookieToken 33 | + "00" // (WRONG!) Too much data in stream 34 | )] 35 | [InlineData( 36 | "02" // (WRONG! - must be 0x01) Version 37 | + "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken 38 | + "01" // IsCookieToken 39 | )] 40 | [InlineData( 41 | "01" // Version 42 | + "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken 43 | + "00" // IsCookieToken 44 | + "00" // IsClaimsBased 45 | + "05" // Username length header 46 | + "0000" // (WRONG!) Too little data in stream 47 | )] 48 | public void Deserialize_BadToken_Throws(string serializedToken) 49 | { 50 | // Arrange 51 | var testSerializer = new DefaultAntiforgeryTokenSerializer(_dataProtector.Object, _pool); 52 | 53 | // Act & assert 54 | var ex = Assert.Throws(() => testSerializer.Deserialize(serializedToken)); 55 | Assert.Equal(@"The antiforgery token could not be decrypted.", ex.Message); 56 | } 57 | 58 | [Fact] 59 | public void Serialize_FieldToken_WithClaimUid_TokenRoundTripSuccessful() 60 | { 61 | // Arrange 62 | var testSerializer = new DefaultAntiforgeryTokenSerializer(_dataProtector.Object, _pool); 63 | 64 | //"01" // Version 65 | //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken 66 | //+ "00" // IsCookieToken 67 | //+ "01" // IsClaimsBased 68 | //+ "6F1648E97249AA58754036A67E248CF044F07ECFB0ED387556CE029A4F9A40E0" // ClaimUid 69 | //+ "05" // AdditionalData length header 70 | //+ "E282AC3437"; // AdditionalData ("€47") as UTF8 71 | var token = new AntiforgeryToken() 72 | { 73 | SecurityToken = _securityToken, 74 | IsCookieToken = false, 75 | ClaimUid = _claimUid, 76 | AdditionalData = "€47" 77 | }; 78 | 79 | // Act 80 | var actualSerializedData = testSerializer.Serialize(token); 81 | var deserializedToken = testSerializer.Deserialize(actualSerializedData); 82 | 83 | // Assert 84 | AssertTokensEqual(token, deserializedToken); 85 | _dataProtector.Verify(); 86 | } 87 | 88 | [Fact] 89 | public void Serialize_FieldToken_WithUsername_TokenRoundTripSuccessful() 90 | { 91 | // Arrange 92 | var testSerializer = new DefaultAntiforgeryTokenSerializer(_dataProtector.Object, _pool); 93 | 94 | //"01" // Version 95 | //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken 96 | //+ "00" // IsCookieToken 97 | //+ "00" // IsClaimsBased 98 | //+ "08" // Username length header 99 | //+ "4AC3A972C3B46D65" // Username ("Jérôme") as UTF8 100 | //+ "05" // AdditionalData length header 101 | //+ "E282AC3437"; // AdditionalData ("€47") as UTF8 102 | var token = new AntiforgeryToken() 103 | { 104 | SecurityToken = _securityToken, 105 | IsCookieToken = false, 106 | Username = "Jérôme", 107 | AdditionalData = "€47" 108 | }; 109 | 110 | // Act 111 | var actualSerializedData = testSerializer.Serialize(token); 112 | var deserializedToken = testSerializer.Deserialize(actualSerializedData); 113 | 114 | // Assert 115 | AssertTokensEqual(token, deserializedToken); 116 | _dataProtector.Verify(); 117 | } 118 | 119 | [Fact] 120 | public void Serialize_CookieToken_TokenRoundTripSuccessful() 121 | { 122 | // Arrange 123 | var testSerializer = new DefaultAntiforgeryTokenSerializer(_dataProtector.Object, _pool); 124 | 125 | //"01" // Version 126 | //+ "705EEDCC7D42F1D6B3B98A593625BB4C" // SecurityToken 127 | //+ "01"; // IsCookieToken 128 | var token = new AntiforgeryToken() 129 | { 130 | SecurityToken = _securityToken, 131 | IsCookieToken = true 132 | }; 133 | 134 | // Act 135 | string actualSerializedData = testSerializer.Serialize(token); 136 | var deserializedToken = testSerializer.Deserialize(actualSerializedData); 137 | 138 | // Assert 139 | AssertTokensEqual(token, deserializedToken); 140 | _dataProtector.Verify(); 141 | } 142 | 143 | private static Mock GetDataProtector() 144 | { 145 | var mockCryptoSystem = new Mock(); 146 | mockCryptoSystem.Setup(o => o.Protect(It.IsAny())) 147 | .Returns(Protect) 148 | .Verifiable(); 149 | mockCryptoSystem.Setup(o => o.Unprotect(It.IsAny())) 150 | .Returns(UnProtect) 151 | .Verifiable(); 152 | 153 | var provider = new Mock(); 154 | provider 155 | .Setup(p => p.CreateProtector(It.IsAny())) 156 | .Returns(mockCryptoSystem.Object); 157 | return provider; 158 | } 159 | 160 | private static byte[] Protect(byte[] data) 161 | { 162 | var input = new List(data); 163 | input.Add(_salt); 164 | return input.ToArray(); 165 | } 166 | 167 | private static byte[] UnProtect(byte[] data) 168 | { 169 | var salt = data[data.Length - 1]; 170 | if (salt != _salt) 171 | { 172 | throw new ArgumentException("Invalid salt value in data"); 173 | } 174 | 175 | return data.Take(data.Length - 1).ToArray(); 176 | } 177 | 178 | private static void AssertTokensEqual(AntiforgeryToken expected, AntiforgeryToken actual) 179 | { 180 | Assert.NotNull(expected); 181 | Assert.NotNull(actual); 182 | Assert.Equal(expected.AdditionalData, actual.AdditionalData); 183 | Assert.Equal(expected.ClaimUid, actual.ClaimUid); 184 | Assert.Equal(expected.IsCookieToken, actual.IsCookieToken); 185 | Assert.Equal(expected.SecurityToken, actual.SecurityToken); 186 | Assert.Equal(expected.Username, actual.Username); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultAntiforgeryTokenStoreTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Threading.Tasks; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.AspNetCore.Http.Internal; 9 | using Microsoft.Extensions.Primitives; 10 | using Moq; 11 | using Xunit; 12 | 13 | namespace Microsoft.AspNetCore.Antiforgery.Internal 14 | { 15 | public class DefaultAntiforgeryTokenStoreTest 16 | { 17 | private readonly string _cookieName = "cookie-name"; 18 | 19 | [Fact] 20 | public void GetCookieToken_CookieDoesNotExist_ReturnsNull() 21 | { 22 | // Arrange 23 | var httpContext = GetHttpContext(new RequestCookieCollection()); 24 | var options = new AntiforgeryOptions 25 | { 26 | Cookie = { Name = _cookieName } 27 | }; 28 | 29 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 30 | 31 | // Act 32 | var token = tokenStore.GetCookieToken(httpContext); 33 | 34 | // Assert 35 | Assert.Null(token); 36 | } 37 | 38 | [Fact] 39 | public void GetCookieToken_CookieIsEmpty_ReturnsNull() 40 | { 41 | // Arrange 42 | var httpContext = GetHttpContext(_cookieName, string.Empty); 43 | var options = new AntiforgeryOptions 44 | { 45 | Cookie = { Name = _cookieName } 46 | }; 47 | 48 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 49 | 50 | // Act 51 | var token = tokenStore.GetCookieToken(httpContext); 52 | 53 | // Assert 54 | Assert.Null(token); 55 | } 56 | 57 | [Fact] 58 | public void GetCookieToken_CookieIsNotEmpty_ReturnsToken() 59 | { 60 | // Arrange 61 | var expectedToken = "valid-value"; 62 | var httpContext = GetHttpContext(_cookieName, expectedToken); 63 | 64 | var options = new AntiforgeryOptions 65 | { 66 | Cookie = { Name = _cookieName } 67 | }; 68 | 69 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 70 | 71 | // Act 72 | var token = tokenStore.GetCookieToken(httpContext); 73 | 74 | // Assert 75 | Assert.Equal(expectedToken, token); 76 | } 77 | 78 | [Fact] 79 | public async Task GetRequestTokens_CookieIsEmpty_ReturnsNullTokens() 80 | { 81 | // Arrange 82 | var httpContext = GetHttpContext(new RequestCookieCollection()); 83 | httpContext.Request.Form = FormCollection.Empty; 84 | 85 | var options = new AntiforgeryOptions 86 | { 87 | Cookie = { Name = "cookie-name" }, 88 | FormFieldName = "form-field-name", 89 | }; 90 | 91 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 92 | 93 | // Act 94 | var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext); 95 | 96 | // Assert 97 | Assert.Null(tokenSet.CookieToken); 98 | Assert.Null(tokenSet.RequestToken); 99 | } 100 | 101 | [Fact] 102 | public async Task GetRequestTokens_HeaderTokenTakensPriority_OverFormToken() 103 | { 104 | // Arrange 105 | var httpContext = GetHttpContext("cookie-name", "cookie-value"); 106 | httpContext.Request.ContentType = "application/x-www-form-urlencoded"; 107 | httpContext.Request.Form = new FormCollection(new Dictionary 108 | { 109 | { "form-field-name", "form-value" }, 110 | }); // header value has priority. 111 | httpContext.Request.Headers.Add("header-name", "header-value"); 112 | 113 | var options = new AntiforgeryOptions 114 | { 115 | Cookie = { Name = "cookie-name" }, 116 | FormFieldName = "form-field-name", 117 | HeaderName = "header-name", 118 | }; 119 | 120 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 121 | 122 | // Act 123 | var tokens = await tokenStore.GetRequestTokensAsync(httpContext); 124 | 125 | // Assert 126 | Assert.Equal("cookie-value", tokens.CookieToken); 127 | Assert.Equal("header-value", tokens.RequestToken); 128 | } 129 | 130 | [Fact] 131 | public async Task GetRequestTokens_NoHeaderToken_FallsBackToFormToken() 132 | { 133 | // Arrange 134 | var httpContext = GetHttpContext("cookie-name", "cookie-value"); 135 | httpContext.Request.ContentType = "application/x-www-form-urlencoded"; 136 | httpContext.Request.Form = new FormCollection(new Dictionary 137 | { 138 | { "form-field-name", "form-value" }, 139 | }); 140 | 141 | var options = new AntiforgeryOptions 142 | { 143 | Cookie = { Name = "cookie-name" }, 144 | FormFieldName = "form-field-name", 145 | HeaderName = "header-name", 146 | }; 147 | 148 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 149 | 150 | // Act 151 | var tokens = await tokenStore.GetRequestTokensAsync(httpContext); 152 | 153 | // Assert 154 | Assert.Equal("cookie-value", tokens.CookieToken); 155 | Assert.Equal("form-value", tokens.RequestToken); 156 | } 157 | 158 | [Fact] 159 | public async Task GetRequestTokens_NonFormContentType_UsesHeaderToken() 160 | { 161 | // Arrange 162 | var httpContext = GetHttpContext("cookie-name", "cookie-value"); 163 | httpContext.Request.ContentType = "application/json"; 164 | httpContext.Request.Headers.Add("header-name", "header-value"); 165 | 166 | // Will not be accessed 167 | httpContext.Request.Form = null; 168 | 169 | var options = new AntiforgeryOptions 170 | { 171 | Cookie = { Name = "cookie-name" }, 172 | FormFieldName = "form-field-name", 173 | HeaderName = "header-name", 174 | }; 175 | 176 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 177 | 178 | // Act 179 | var tokens = await tokenStore.GetRequestTokensAsync(httpContext); 180 | 181 | // Assert 182 | Assert.Equal("cookie-value", tokens.CookieToken); 183 | Assert.Equal("header-value", tokens.RequestToken); 184 | } 185 | 186 | [Fact] 187 | public async Task GetRequestTokens_NoHeaderToken_NonFormContentType_ReturnsNullToken() 188 | { 189 | // Arrange 190 | var httpContext = GetHttpContext("cookie-name", "cookie-value"); 191 | httpContext.Request.ContentType = "application/json"; 192 | 193 | // Will not be accessed 194 | httpContext.Request.Form = null; 195 | 196 | var options = new AntiforgeryOptions 197 | { 198 | Cookie = { Name = "cookie-name" }, 199 | FormFieldName = "form-field-name", 200 | HeaderName = "header-name", 201 | }; 202 | 203 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 204 | 205 | // Act 206 | var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext); 207 | 208 | // Assert 209 | Assert.Equal("cookie-value", tokenSet.CookieToken); 210 | Assert.Null(tokenSet.RequestToken); 211 | } 212 | 213 | [Fact] 214 | public async Task GetRequestTokens_BothHeaderValueAndFormFieldsEmpty_ReturnsNullTokens() 215 | { 216 | // Arrange 217 | var httpContext = GetHttpContext("cookie-name", "cookie-value"); 218 | httpContext.Request.ContentType = "application/x-www-form-urlencoded"; 219 | httpContext.Request.Form = FormCollection.Empty; 220 | 221 | var options = new AntiforgeryOptions 222 | { 223 | Cookie = { Name = "cookie-name" }, 224 | FormFieldName = "form-field-name", 225 | HeaderName = "header-name", 226 | }; 227 | 228 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 229 | 230 | // Act 231 | var tokenSet = await tokenStore.GetRequestTokensAsync(httpContext); 232 | 233 | // Assert 234 | Assert.Equal("cookie-value", tokenSet.CookieToken); 235 | Assert.Null(tokenSet.RequestToken); 236 | } 237 | 238 | [Theory] 239 | [InlineData(false, CookieSecurePolicy.SameAsRequest, null)] 240 | [InlineData(true, CookieSecurePolicy.SameAsRequest, true)] 241 | [InlineData(false, CookieSecurePolicy.Always, true)] 242 | [InlineData(true, CookieSecurePolicy.Always, true)] 243 | [InlineData(false, CookieSecurePolicy.None, null)] 244 | [InlineData(true, CookieSecurePolicy.None, null)] 245 | public void SaveCookieToken_HonorsCookieSecurePolicy_OnOptions( 246 | bool isRequestSecure, 247 | CookieSecurePolicy policy, 248 | bool? expectedCookieSecureFlag) 249 | { 250 | // Arrange 251 | var token = "serialized-value"; 252 | bool defaultCookieSecureValue = expectedCookieSecureFlag ?? false; // pulled from config; set by ctor 253 | var cookies = new MockResponseCookieCollection(); 254 | 255 | var httpContext = new Mock(); 256 | httpContext 257 | .Setup(hc => hc.Request.IsHttps) 258 | .Returns(isRequestSecure); 259 | httpContext 260 | .Setup(o => o.Response.Cookies) 261 | .Returns(cookies); 262 | httpContext 263 | .SetupGet(hc => hc.Request.PathBase) 264 | .Returns("/"); 265 | 266 | var options = new AntiforgeryOptions() 267 | { 268 | Cookie = 269 | { 270 | Name = _cookieName, 271 | SecurePolicy = policy 272 | }, 273 | }; 274 | 275 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 276 | 277 | // Act 278 | tokenStore.SaveCookieToken(httpContext.Object, token); 279 | 280 | // Assert 281 | Assert.Equal(1, cookies.Count); 282 | Assert.NotNull(cookies); 283 | Assert.Equal(_cookieName, cookies.Key); 284 | Assert.Equal("serialized-value", cookies.Value); 285 | Assert.True(cookies.Options.HttpOnly); 286 | Assert.Equal(defaultCookieSecureValue, cookies.Options.Secure); 287 | } 288 | 289 | [Theory] 290 | [InlineData(null, "/")] 291 | [InlineData("", "/")] 292 | [InlineData("/", "/")] 293 | [InlineData("/vdir1", "/vdir1")] 294 | [InlineData("/vdir1/vdir2", "/vdir1/vdir2")] 295 | public void SaveCookieToken_SetsCookieWithApproriatePathBase(string requestPathBase, string expectedCookiePath) 296 | { 297 | // Arrange 298 | var token = "serialized-value"; 299 | var cookies = new MockResponseCookieCollection(); 300 | var httpContext = new Mock(); 301 | httpContext 302 | .Setup(hc => hc.Response.Cookies) 303 | .Returns(cookies); 304 | httpContext 305 | .SetupGet(hc => hc.Request.PathBase) 306 | .Returns(requestPathBase); 307 | httpContext 308 | .SetupGet(hc => hc.Request.Path) 309 | .Returns("/index.html"); 310 | var options = new AntiforgeryOptions 311 | { 312 | Cookie = { Name = _cookieName } 313 | }; 314 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 315 | 316 | // Act 317 | tokenStore.SaveCookieToken(httpContext.Object, token); 318 | 319 | // Assert 320 | Assert.Equal(1, cookies.Count); 321 | Assert.NotNull(cookies); 322 | Assert.Equal(_cookieName, cookies.Key); 323 | Assert.Equal("serialized-value", cookies.Value); 324 | Assert.True(cookies.Options.HttpOnly); 325 | Assert.Equal(expectedCookiePath, cookies.Options.Path); 326 | } 327 | 328 | [Fact] 329 | public void SaveCookieToken_NonNullAntiforgeryOptionsConfigureCookieOptionsPath_UsesCookieOptionsPath() 330 | { 331 | // Arrange 332 | var expectedCookiePath = "/"; 333 | var requestPathBase = "/vdir1"; 334 | var token = "serialized-value"; 335 | var cookies = new MockResponseCookieCollection(); 336 | var httpContext = new Mock(); 337 | httpContext 338 | .Setup(hc => hc.Response.Cookies) 339 | .Returns(cookies); 340 | httpContext 341 | .SetupGet(hc => hc.Request.PathBase) 342 | .Returns(requestPathBase); 343 | httpContext 344 | .SetupGet(hc => hc.Request.Path) 345 | .Returns("/index.html"); 346 | var options = new AntiforgeryOptions 347 | { 348 | Cookie = 349 | { 350 | Name = _cookieName, 351 | Path = expectedCookiePath 352 | } 353 | }; 354 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 355 | 356 | // Act 357 | tokenStore.SaveCookieToken(httpContext.Object, token); 358 | 359 | // Assert 360 | Assert.Equal(1, cookies.Count); 361 | Assert.NotNull(cookies); 362 | Assert.Equal(_cookieName, cookies.Key); 363 | Assert.Equal("serialized-value", cookies.Value); 364 | Assert.True(cookies.Options.HttpOnly); 365 | Assert.Equal(expectedCookiePath, cookies.Options.Path); 366 | } 367 | 368 | [Fact] 369 | public void SaveCookieToken_NonNullAntiforgeryOptionsConfigureCookieOptionsDomain_UsesCookieOptionsDomain() 370 | { 371 | // Arrange 372 | var expectedCookieDomain = "microsoft.com"; 373 | var token = "serialized-value"; 374 | var cookies = new MockResponseCookieCollection(); 375 | var httpContext = new Mock(); 376 | httpContext 377 | .Setup(hc => hc.Response.Cookies) 378 | .Returns(cookies); 379 | httpContext 380 | .SetupGet(hc => hc.Request.PathBase) 381 | .Returns("/vdir1"); 382 | httpContext 383 | .SetupGet(hc => hc.Request.Path) 384 | .Returns("/index.html"); 385 | var options = new AntiforgeryOptions 386 | { 387 | Cookie = 388 | { 389 | Name = _cookieName, 390 | Domain = expectedCookieDomain 391 | } 392 | }; 393 | var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); 394 | 395 | // Act 396 | tokenStore.SaveCookieToken(httpContext.Object, token); 397 | 398 | // Assert 399 | Assert.Equal(1, cookies.Count); 400 | Assert.NotNull(cookies); 401 | Assert.Equal(_cookieName, cookies.Key); 402 | Assert.Equal("serialized-value", cookies.Value); 403 | Assert.True(cookies.Options.HttpOnly); 404 | Assert.Equal("/vdir1", cookies.Options.Path); 405 | Assert.Equal(expectedCookieDomain, cookies.Options.Domain); 406 | } 407 | 408 | private HttpContext GetHttpContext(string cookieName, string cookieValue) 409 | { 410 | var cookies = new RequestCookieCollection(new Dictionary 411 | { 412 | { cookieName, cookieValue }, 413 | }); 414 | 415 | return GetHttpContext(cookies); 416 | } 417 | 418 | private HttpContext GetHttpContext(IRequestCookieCollection cookies) 419 | { 420 | var httpContext = new DefaultHttpContext(); 421 | httpContext.Request.Cookies = cookies; 422 | 423 | return httpContext; 424 | } 425 | 426 | private class MockResponseCookieCollection : IResponseCookies 427 | { 428 | public string Key { get; set; } 429 | public string Value { get; set; } 430 | public CookieOptions Options { get; set; } 431 | public int Count { get; set; } 432 | 433 | public void Append(string key, string value, CookieOptions options) 434 | { 435 | Key = key; 436 | Value = value; 437 | Options = options; 438 | Count++; 439 | } 440 | 441 | public void Append(string key, string value) 442 | { 443 | throw new NotImplementedException(); 444 | } 445 | 446 | public void Delete(string key, CookieOptions options) 447 | { 448 | throw new NotImplementedException(); 449 | } 450 | 451 | public void Delete(string key) 452 | { 453 | throw new NotImplementedException(); 454 | } 455 | } 456 | } 457 | } 458 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Antiforgery.Test/Internal/DefaultClaimUidExtractorTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Security.Claims; 8 | using Microsoft.Extensions.ObjectPool; 9 | using Moq; 10 | using Xunit; 11 | 12 | namespace Microsoft.AspNetCore.Antiforgery.Internal 13 | { 14 | public class DefaultClaimUidExtractorTest 15 | { 16 | private static readonly ObjectPool _pool = 17 | new DefaultObjectPoolProvider().Create(new AntiforgerySerializationContextPooledObjectPolicy()); 18 | 19 | [Fact] 20 | public void ExtractClaimUid_Unauthenticated() 21 | { 22 | // Arrange 23 | var extractor = new DefaultClaimUidExtractor(_pool); 24 | 25 | var mockIdentity = new Mock(); 26 | mockIdentity.Setup(o => o.IsAuthenticated) 27 | .Returns(false); 28 | 29 | // Act 30 | var claimUid = extractor.ExtractClaimUid(new ClaimsPrincipal(mockIdentity.Object)); 31 | 32 | // Assert 33 | Assert.Null(claimUid); 34 | } 35 | 36 | [Fact] 37 | public void ExtractClaimUid_ClaimsIdentity() 38 | { 39 | // Arrange 40 | var mockIdentity = new Mock(); 41 | mockIdentity.Setup(o => o.IsAuthenticated) 42 | .Returns(true); 43 | mockIdentity.Setup(o => o.Claims).Returns(new Claim[] { new Claim(ClaimTypes.Name, "someName") }); 44 | 45 | var extractor = new DefaultClaimUidExtractor(_pool); 46 | 47 | // Act 48 | var claimUid = extractor.ExtractClaimUid(new ClaimsPrincipal(mockIdentity.Object )); 49 | 50 | // Assert 51 | Assert.NotNull(claimUid); 52 | Assert.Equal("yhXE+2v4zSXHtRHmzm4cmrhZca2J0g7yTUwtUerdeF4=", claimUid); 53 | } 54 | 55 | [Fact] 56 | public void DefaultUniqueClaimTypes_NotPresent_SerializesAllClaimTypes() 57 | { 58 | var identity = new ClaimsIdentity("someAuthentication"); 59 | identity.AddClaim(new Claim(ClaimTypes.Email, "someone@antifrogery.com")); 60 | identity.AddClaim(new Claim(ClaimTypes.GivenName, "some")); 61 | identity.AddClaim(new Claim(ClaimTypes.Surname, "one")); 62 | identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, string.Empty)); 63 | 64 | // Arrange 65 | var claimsIdentity = (ClaimsIdentity)identity; 66 | 67 | // Act 68 | var identiferParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(new ClaimsIdentity[] { claimsIdentity }) 69 | .ToArray(); 70 | var claims = claimsIdentity.Claims.ToList(); 71 | claims.Sort((a, b) => string.Compare(a.Type, b.Type, StringComparison.Ordinal)); 72 | 73 | // Assert 74 | int index = 0; 75 | foreach (var claim in claims) 76 | { 77 | Assert.Equal(identiferParameters[index++], claim.Type); 78 | Assert.Equal(identiferParameters[index++], claim.Value); 79 | Assert.Equal(identiferParameters[index++], claim.Issuer); 80 | } 81 | } 82 | 83 | [Fact] 84 | public void DefaultUniqueClaimTypes_Present() 85 | { 86 | // Arrange 87 | var identity = new ClaimsIdentity("someAuthentication"); 88 | identity.AddClaim(new Claim("fooClaim", "fooClaimValue")); 89 | identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "nameIdentifierValue")); 90 | 91 | // Act 92 | var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(new ClaimsIdentity[] { identity }); 93 | 94 | // Assert 95 | Assert.Equal(new string[] 96 | { 97 | ClaimTypes.NameIdentifier, 98 | "nameIdentifierValue", 99 | "LOCAL AUTHORITY", 100 | }, uniqueIdentifierParameters); 101 | } 102 | 103 | [Fact] 104 | public void GetUniqueIdentifierParameters_PrefersSubClaimOverNameIdentifierAndUpn() 105 | { 106 | // Arrange 107 | var identity = new ClaimsIdentity("someAuthentication"); 108 | identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "nameIdentifierValue")); 109 | identity.AddClaim(new Claim("sub", "subClaimValue")); 110 | identity.AddClaim(new Claim(ClaimTypes.Upn, "upnClaimValue")); 111 | 112 | // Act 113 | var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(new ClaimsIdentity[] { identity }); 114 | 115 | // Assert 116 | Assert.Equal(new string[] 117 | { 118 | "sub", 119 | "subClaimValue", 120 | "LOCAL AUTHORITY", 121 | }, uniqueIdentifierParameters); 122 | } 123 | 124 | [Fact] 125 | public void GetUniqueIdentifierParameters_PrefersNameIdentifierOverUpn() 126 | { 127 | // Arrange 128 | var identity = new ClaimsIdentity("someAuthentication"); 129 | identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "nameIdentifierValue")); 130 | identity.AddClaim(new Claim(ClaimTypes.Upn, "upnClaimValue")); 131 | 132 | // Act 133 | var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(new ClaimsIdentity[] { identity }); 134 | 135 | // Assert 136 | Assert.Equal(new string[] 137 | { 138 | ClaimTypes.NameIdentifier, 139 | "nameIdentifierValue", 140 | "LOCAL AUTHORITY", 141 | }, uniqueIdentifierParameters); 142 | } 143 | 144 | [Fact] 145 | public void GetUniqueIdentifierParameters_UsesUpnIfPresent() 146 | { 147 | // Arrange 148 | var identity = new ClaimsIdentity("someAuthentication"); 149 | identity.AddClaim(new Claim("fooClaim", "fooClaimValue")); 150 | identity.AddClaim(new Claim(ClaimTypes.Upn, "upnClaimValue")); 151 | 152 | // Act 153 | var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(new ClaimsIdentity[] { identity }); 154 | 155 | // Assert 156 | Assert.Equal(new string[] 157 | { 158 | ClaimTypes.Upn, 159 | "upnClaimValue", 160 | "LOCAL AUTHORITY", 161 | }, uniqueIdentifierParameters); 162 | } 163 | 164 | [Fact] 165 | public void GetUniqueIdentifierParameters_MultipleIdentities_UsesOnlyAuthenticatedIdentities() 166 | { 167 | // Arrange 168 | var identity1 = new ClaimsIdentity(); // no authentication 169 | identity1.AddClaim(new Claim("sub", "subClaimValue")); 170 | var identity2 = new ClaimsIdentity("someAuthentication"); 171 | identity2.AddClaim(new Claim(ClaimTypes.NameIdentifier, "nameIdentifierValue")); 172 | 173 | // Act 174 | var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters(new ClaimsIdentity[] { identity1, identity2 }); 175 | 176 | // Assert 177 | Assert.Equal(new string[] 178 | { 179 | ClaimTypes.NameIdentifier, 180 | "nameIdentifierValue", 181 | "LOCAL AUTHORITY", 182 | }, uniqueIdentifierParameters); 183 | } 184 | 185 | [Fact] 186 | public void GetUniqueIdentifierParameters_NoKnownClaimTypesFound_SortsAndReturnsAllClaimsFromAuthenticatedIdentities() 187 | { 188 | // Arrange 189 | var identity1 = new ClaimsIdentity(); // no authentication 190 | identity1.AddClaim(new Claim("sub", "subClaimValue")); 191 | var identity2 = new ClaimsIdentity("someAuthentication"); 192 | identity2.AddClaim(new Claim(ClaimTypes.Email, "email@domain.com")); 193 | var identity3 = new ClaimsIdentity("someAuthentication"); 194 | identity3.AddClaim(new Claim(ClaimTypes.Country, "countryValue")); 195 | var identity4 = new ClaimsIdentity("someAuthentication"); 196 | identity4.AddClaim(new Claim(ClaimTypes.Name, "claimName")); 197 | 198 | // Act 199 | var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters( 200 | new ClaimsIdentity[] { identity1, identity2, identity3, identity4 }); 201 | 202 | // Assert 203 | Assert.Equal(new List 204 | { 205 | ClaimTypes.Country, 206 | "countryValue", 207 | "LOCAL AUTHORITY", 208 | ClaimTypes.Email, 209 | "email@domain.com", 210 | "LOCAL AUTHORITY", 211 | ClaimTypes.Name, 212 | "claimName", 213 | "LOCAL AUTHORITY", 214 | }, uniqueIdentifierParameters); 215 | } 216 | 217 | [Fact] 218 | public void GetUniqueIdentifierParameters_PrefersNameFromFirstIdentity_OverSubFromSecondIdentity() 219 | { 220 | // Arrange 221 | var identity1 = new ClaimsIdentity("someAuthentication"); 222 | identity1.AddClaim(new Claim(ClaimTypes.NameIdentifier, "nameIdentifierValue")); 223 | var identity2 = new ClaimsIdentity("someAuthentication"); 224 | identity2.AddClaim(new Claim("sub", "subClaimValue")); 225 | 226 | // Act 227 | var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters( 228 | new ClaimsIdentity[] { identity1, identity2 }); 229 | 230 | // Assert 231 | Assert.Equal(new string[] 232 | { 233 | ClaimTypes.NameIdentifier, 234 | "nameIdentifierValue", 235 | "LOCAL AUTHORITY", 236 | }, uniqueIdentifierParameters); 237 | } 238 | 239 | [Fact] 240 | public void GetUniqueIdentifierParameters_PrefersUpnFromFirstIdentity_OverNameFromSecondIdentity() 241 | { 242 | // Arrange 243 | var identity1 = new ClaimsIdentity("someAuthentication"); 244 | identity1.AddClaim(new Claim(ClaimTypes.Upn, "upnValue")); 245 | var identity2 = new ClaimsIdentity("someAuthentication"); 246 | identity2.AddClaim(new Claim(ClaimTypes.NameIdentifier, "nameIdentifierValue")); 247 | 248 | // Act 249 | var uniqueIdentifierParameters = DefaultClaimUidExtractor.GetUniqueIdentifierParameters( 250 | new ClaimsIdentity[] { identity1, identity2 }); 251 | 252 | // Assert 253 | Assert.Equal(new string[] 254 | { 255 | ClaimTypes.Upn, 256 | "upnValue", 257 | "LOCAL AUTHORITY", 258 | }, uniqueIdentifierParameters); 259 | } 260 | } 261 | } -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Antiforgery.Test/Microsoft.AspNetCore.Antiforgery.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(StandardTestTfms) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/Microsoft.AspNetCore.Antiforgery.Test/TestOptionsManager.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using Microsoft.Extensions.Options; 5 | 6 | namespace Microsoft.AspNetCore.Antiforgery 7 | { 8 | public class TestOptionsManager : IOptions 9 | { 10 | public TestOptionsManager() 11 | { 12 | } 13 | 14 | public TestOptionsManager(AntiforgeryOptions options) 15 | { 16 | Value = options; 17 | } 18 | 19 | public AntiforgeryOptions Value { get; set; } = new AntiforgeryOptions(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /version.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 3.0.0 4 | alpha1 5 | $(VersionPrefix) 6 | $(VersionPrefix)-$(VersionSuffix)-final 7 | t000 8 | a- 9 | $(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-')) 10 | $(VersionSuffix)-$(BuildNumber) 11 | 12 | 13 | --------------------------------------------------------------------------------