├── .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 |
--------------------------------------------------------------------------------