Replace CSRF cookie with CrossOriginProtection (#36183)

Removes the CSRF cookie in favor of
[`CrossOriginProtection`](https://pkg.go.dev/net/http#CrossOriginProtection)
which relies purely on HTTP headers.

Fixes: https://github.com/go-gitea/gitea/issues/11188
Fixes: https://github.com/go-gitea/gitea/issues/30333
Helps: https://github.com/go-gitea/gitea/issues/35107

TODOs:

- [x] Fix tests
- [ ] Ideally add tests to validates the protection

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
silverwind
2025-12-25 11:33:34 +01:00
committed by GitHub
parent eddf875992
commit 42d294941c
207 changed files with 178 additions and 1196 deletions
-1
View File
@@ -3,7 +3,6 @@
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form ignore-dirty tw-max-w-2xl tw-m-auto" action="{{AppSubUrl}}/user/activate" method="post">
{{.CsrfTokenHtml}}
<h2 class="ui top attached header">
{{ctx.Locale.Tr "auth.active_your_account"}}
</h2>
@@ -6,7 +6,6 @@
</h4>
<div class="ui attached segment">
<form class="ui form tw-max-w-2xl tw-m-auto" action="{{.ChangePasscodeLink}}" method="post">
{{.CsrfTokenHtml}}
<div class="required field {{if and (.Err_Password) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
<label for="password">{{ctx.Locale.Tr "password"}}</label>
<input id="password" name="password" type="password" value="{{.password}}" autocomplete="new-password" required>
-1
View File
@@ -3,7 +3,6 @@
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form ignore-dirty" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<h2 class="ui top attached header">
{{ctx.Locale.Tr "auth.forgot_password_title"}}
</h2>
-1
View File
@@ -19,7 +19,6 @@
</div>
<div class="ui attached segment tw-text-center">
<form method="post" action="{{AppSubUrl}}/login/oauth/grant">
{{.CsrfTokenHtml}}
<input type="hidden" name="client_id" value="{{.Application.ClientID}}">
<input type="hidden" name="state" value="{{.State}}">
<input type="hidden" name="scope" value="{{.Scope}}">
-1
View File
@@ -3,7 +3,6 @@
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form ignore-dirty" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<input name="code" type="hidden" value="{{.Code}}">
<h2 class="ui top attached header">
{{ctx.Locale.Tr "auth.reset_password"}}
-1
View File
@@ -12,7 +12,6 @@
<div class="ui attached segment">
{{if .EnablePasswordSignInForm}}
<form class="ui form" action="{{.SignInLink}}" method="post">
{{.CsrfTokenHtml}}
<div class="required field {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}">
<label for="user_name">{{ctx.Locale.Tr "home.uname_holder"}}</label>
<input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required tabindex="1">
-1
View File
@@ -14,7 +14,6 @@
</h4>
<div class="ui attached segment">
<form class="ui form tw-m-auto" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div class="inline field">
{{ctx.Locale.Tr "auth.openid_signin_desc"}}
</div>
-1
View File
@@ -11,7 +11,6 @@
<p>{{ctx.Locale.Tr "auth.sign_up_tip"}}</p>
{{end}}
<form class="ui form" action="{{.SignUpLink}}" method="post">
{{.CsrfTokenHtml}}
{{if or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeRegister)}}
{{template "base/alert" .}}
{{end}}
@@ -8,7 +8,6 @@
</h4>
<div class="ui attached segment">
<form class="ui form left-right-form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div class="inline field">
<span class="help">{{ctx.Locale.Tr "auth.openid_connect_desc"}}</span>
</div>
@@ -11,7 +11,6 @@
{{ctx.Locale.Tr "auth.openid_register_desc"}}
</p>
<form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_UserName}}error{{end}}">
<label for="user_name">{{ctx.Locale.Tr "username"}}</label>
<input id="user_name" type="text" name="user_name" value="{{.user_name}}" autofocus required>
-1
View File
@@ -3,7 +3,6 @@
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form tw-max-w-2xl tw-m-auto" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<h3 class="ui top attached header">
{{ctx.Locale.Tr "twofa"}}
</h3>
-1
View File
@@ -3,7 +3,6 @@
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form tw-max-w-2xl tw-m-auto" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<h3 class="ui top attached header">
{{ctx.Locale.Tr "twofa_scratch"}}
</h3>
@@ -15,7 +15,6 @@
</div>
{{if and (not $pageTypeIsRead) $notificationUnreadCount}}
<form action="{{AppSubUrl}}/notifications/purge" method="post">
{{$.CsrfTokenHtml}}
<button class="ui mini button primary tw-mr-0" title="{{ctx.Locale.Tr "notification.mark_all_as_read"}}">
{{svg "octicon-checklist"}}
</button>
@@ -57,7 +56,6 @@
<form class="notifications-buttons" action="{{AppSubUrl}}/notifications/status?type={{$.PageType}}&page={{$.Page.Paginater.Current}}&perPage={{$.Page.Paginater.PagingNum}}" method="post"
hx-boost="true" hx-target="#notification_div" hx-swap="outerHTML"
>
{{$.CsrfTokenHtml}}
<input type="hidden" name="notification_id" value="{{$one.ID}}">
{{if ne $one.Status $statusPinned}}
<button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "notification.pin"}}"
-5
View File
@@ -7,7 +7,6 @@
{{if and (not ($.UserDisabledFeatures.Contains "manage_credentials")) (or (.SignedUser.IsLocal) (.SignedUser.IsOAuth2))}}
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/user/settings/account" method="post">
{{template "base/disable_form_autofill"}}
{{.CsrfTokenHtml}}
{{if .SignedUser.IsPasswordSet}}
<div class="required field {{if .Err_OldPassword}}error{{end}}">
<label for="old_password">{{ctx.Locale.Tr "settings.old_password"}}</label>
@@ -62,7 +61,6 @@
</button>
{{if .CanBePrimary}}
<form action="{{AppSubUrl}}/user/settings/account/email" method="post">
{{$.CsrfTokenHtml}}
<input name="_method" type="hidden" value="PRIMARY">
<input name="id" type="hidden" value="{{.ID}}">
<button class="ui primary tiny button">{{ctx.Locale.Tr "settings.primary_email"}}</button>
@@ -71,7 +69,6 @@
{{end}}
{{if not .IsActivated}}
<form action="{{AppSubUrl}}/user/settings/account/email" method="post">
{{$.CsrfTokenHtml}}
<input name="_method" type="hidden" value="SENDACTIVATION">
<input name="id" type="hidden" value="{{.ID}}">
{{if $.ActivationsPending}}
@@ -92,7 +89,6 @@
{{if not ($.UserDisabledFeatures.Contains "manage_credentials")}}
<div class="ui bottom attached segment">
<form class="ui form" action="{{AppSubUrl}}/user/settings/account/email" method="post">
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_Email}}error{{end}}">
<label for="email">{{ctx.Locale.Tr "settings.add_new_email"}}</label>
<input id="email" name="email" type="email" required {{if not .CanAddEmails}}disabled{{end}}>
@@ -121,7 +117,6 @@
</div>
<form class="ui form ignore-dirty" id="delete-form" action="{{AppSubUrl}}/user/settings/account/delete" method="post">
{{template "base/disable_form_autofill"}}
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_Password}}error{{end}}">
<label for="password-confirmation">{{ctx.Locale.Tr "password"}}</label>
<input id="password-confirmation" name="password" type="password" autocomplete="off" required>
-3
View File
@@ -7,7 +7,6 @@
</h4>
<div class="ui attached segment">
<form class="ui form" action="{{.Link}}/theme" method="post">
{{.CsrfTokenHtml}}
<div class="field">
{{ctx.Locale.Tr "settings.theme_desc"}}
<a class="muted" target="_blank" href="https://github.com/go-gitea/gitea/blob/main/web_src/css/themes/" data-tooltip-content="{{ctx.Locale.Tr "settings.theme_colorblindness_prompt"}}">
@@ -42,7 +41,6 @@
</h4>
<div class="ui attached segment">
<form class="ui form" action="{{.Link}}/language" method="post">
{{.CsrfTokenHtml}}
<div class="field">
<div class="ui language selection dropdown" id="language">
<input name="language" type="hidden" value="{{.SignedUser.Language}}">
@@ -70,7 +68,6 @@
{{ctx.Locale.Tr "settings.hidden_comment_types_description"}}
</p>
<form class="ui form" action="{{.Link}}/hidden_comments" method="post">
{{.CsrfTokenHtml}}
<div class="inline field">
<div class="ui checkbox" data-tooltip-content="{{ctx.Locale.Tr "settings.hidden_comment_types.ref_tooltip"}}">
<input name="reference" type="checkbox" {{if(call .IsCommentTypeGroupChecked "reference")}}checked{{end}}>
@@ -53,7 +53,6 @@
<details {{if or .name (not .Tokens)}}open{{end}}>
<summary><h4 class="ui header tw-inline-block tw-my-2">{{ctx.Locale.Tr "settings.generate_new_token"}}</h4></summary>
<form class="ui form ignore-dirty" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div class="field {{if .Err_Name}}error{{end}}">
<label for="name">{{ctx.Locale.Tr "settings.token_name"}}</label>
<input id="name" name="name" value="{{.name}}" required maxlength="255">
@@ -5,7 +5,6 @@
<p>{{ctx.Locale.Tr "settings.oauth2_application_create_description"}}</p>
</div>
<div class="ui attached segment form ignore-dirty">
{{.CsrfTokenHtml}}
<div class="field">
<label for="client-id">{{ctx.Locale.Tr "settings.oauth2_client_id"}}</label>
<input id="client-id" readonly value="{{.App.ClientID}}">
@@ -24,7 +23,6 @@
<div class="item">
<!-- TODO add regenerate secret functionality */ -->
<form class="ui form ignore-dirty" action="{{.FormActionPath}}/regenerate_secret" method="post">
{{.CsrfTokenHtml}}
{{ctx.Locale.Tr "settings.oauth2_regenerate_secret_hint"}}
<button class="ui mini button tw-ml-2" type="submit">{{ctx.Locale.Tr "settings.oauth2_regenerate_secret"}}</button>
</form>
@@ -32,7 +30,6 @@
</div>
<div class="ui bottom attached segment">
<form class="ui form ignore-dirty" action="{{.FormActionPath}}" method="post">
{{.CsrfTokenHtml}}
<div class="field {{if .Err_AppName}}error{{end}}">
<label for="application-name">{{ctx.Locale.Tr "settings.oauth2_application_name"}}</label>
<input id="application-name" value="{{.App.Name}}" name="application_name" required maxlength="255">
@@ -51,7 +51,6 @@
<details {{if .application_name}}open{{end}}>
<summary><h4 class="ui header tw-inline-block tw-my-2">{{ctx.Locale.Tr "settings.create_oauth2_application"}}</h4></summary>
<form class="ui form ignore-dirty" action="{{.Link}}/oauth2" method="post">
{{.CsrfTokenHtml}}
<div class="field {{if .Err_AppName}}error{{end}}">
<label for="application-name">{{ctx.Locale.Tr "settings.oauth2_application_name"}}</label>
<input id="application-name" name="application_name" value="{{.application_name}}" required maxlength="255">
-2
View File
@@ -7,7 +7,6 @@
<div class="ui attached segment">
<div class="{{if not .HasGPGError}}tw-hidden{{end}} tw-mb-4" id="add-gpg-key-panel">
<form class="ui form{{if .HasGPGError}} error{{end}}" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<input type="hidden" name="title" value="none">
<div class="field {{if .Err_Content}}error{{end}}">
<label for="gpg-key-content">{{ctx.Locale.Tr "settings.key_content"}}</label>
@@ -81,7 +80,6 @@
<div class="ui segment">
<h4>{{ctx.Locale.Tr "settings.gpg_token_required"}}</h4>
<form class="ui form{{if $.HasGPGVerifyError}} error{{end}}" action="{{$.Link}}" method="post">
{{$.CsrfTokenHtml}}
<input type="hidden" name="title" value="none">
<input type="hidden" name="content" value="{{.KeyID}}">
<input type="hidden" name="key_id" value="{{.KeyID}}">
@@ -42,7 +42,6 @@
</h4>
<div class="ui attached segment">
<form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div class="field {{if .Err_Content}}error{{end}}">
<label for="ssh-principal-content">{{ctx.Locale.Tr "settings.principal_content"}}</label>
<input id="ssh-principal-content" name="content" value="{{.content}}" autofocus required>
-2
View File
@@ -9,7 +9,6 @@
<div class="ui attached segment">
<div class="{{if not .HasSSHError}}tw-hidden{{end}} tw-mb-4" id="add-ssh-key-panel">
<form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div class="field {{if .Err_Title}}error{{end}}">
<label for="ssh-key-title">{{ctx.Locale.Tr "settings.key_name"}}</label>
<input id="ssh-key-title" name="title" value="{{.title}}" autofocus required maxlength="50">
@@ -69,7 +68,6 @@
<div class="ui segment">
<h4>{{ctx.Locale.Tr "settings.ssh_token_required"}}</h4>
<form class="ui form{{if $.HasSSHVerifyError}} error{{end}}" action="{{$.Link}}" method="post">
{{$.CsrfTokenHtml}}
<input type="hidden" name="title" value="none">
<input type="hidden" name="content" value="{{.Content}}">
<input type="hidden" name="fingerprint" value="{{.Fingerprint}}">
@@ -7,7 +7,6 @@
<div class="ui list flex-items-block">
<div class="item">
<form class="ui form tw-w-full" action="{{AppSubUrl}}/user/settings/notifications/email" method="post">
{{$.CsrfTokenHtml}}
<div class="field">
<label>{{ctx.Locale.Tr "settings.email_desc"}}</label>
<div class="ui selection dropdown">
@@ -38,7 +37,6 @@
<div class="ui list flex-items-block">
<div class="item">
<form class="ui form tw-w-full" action="{{AppSubUrl}}/user/settings/notifications/actions" method="post">
{{$.CsrfTokenHtml}}
<div class="field">
<label>{{ctx.Locale.Tr "settings.email_notifications.actions.desc" "https://docs.gitea.com/usage/actions/overview/"}}</label>
<div class="ui selection dropdown">
@@ -24,7 +24,6 @@
</div>
<div class="flex-item-trailing">
<form>
{{$.CsrfTokenHtml}}
<button class="ui red button delete-button" data-modal-id="leave-organization"
data-url="{{.OrganisationLink}}/members/action/leave" data-datauid="{{$.SignedUser.ID}}"
data-name="{{$.SignedUser.DisplayName}}"
-1
View File
@@ -12,7 +12,6 @@
<label>{{ctx.Locale.Tr "packages.owner.settings.chef.keypair.description"}}</label>
</div>
<form class="field" action="{{.Link}}/chef/regenerate_keypair" method="post">
{{.CsrfTokenHtml}}
<button class="ui primary button">{{ctx.Locale.Tr "packages.owner.settings.chef.keypair"}}</button>
</form>
<div class="field">
-2
View File
@@ -6,7 +6,6 @@
<div class="ui attached segment">
<p>{{ctx.Locale.Tr "settings.profile_desc"}}</p>
<form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_Name}}error{{end}}">
<label for="username">{{ctx.Locale.Tr "username"}}
<span class="text red tw-hidden" id="name-change-prompt"> {{ctx.Locale.Tr "settings.change_username_prompt"}}</span>
@@ -102,7 +101,6 @@
</h4>
<div class="ui attached segment">
<form class="ui form" action="{{.Link}}/avatar" method="post" enctype="multipart/form-data">
{{.CsrfTokenHtml}}
{{if not .DisableGravatar}}
<div class="inline field">
<div class="ui radio checkbox">
-2
View File
@@ -43,7 +43,6 @@
<p>{{ctx.Locale.Tr "repo.adopt_preexisting_content" $dir}}</p>
</div>
<form class="ui form" method="post" action="{{AppSubUrl}}/user/settings/repos/unadopted">
{{$.CsrfTokenHtml}}
<input type="hidden" name="id" value="{{$dir}}">
<input type="hidden" name="action" value="adopt">
{{template "base/modal_actions_confirm" $}}
@@ -60,7 +59,6 @@
<p>{{ctx.Locale.Tr "repo.delete_preexisting_content" $dir}}</p>
</div>
<form class="ui form" method="post" action="{{AppSubUrl}}/user/settings/repos/unadopted">
{{$.CsrfTokenHtml}}
<input type="hidden" name="id" value="{{$dir}}">
<input type="hidden" name="action" value="delete">
{{template "base/modal_actions_confirm" $}}
@@ -16,7 +16,6 @@
</div>
<div class="flex-item-trailing">
<form action="{{AppSubUrl}}/user/settings/security/openid/toggle_visibility" method="post">
{{$.CsrfTokenHtml}}
<input name="id" type="hidden" value="{{.ID}}">
{{if .Show}}
<button class="ui tiny button">
@@ -40,7 +39,6 @@
</div>
<div class="ui bottom attached segment">
<form class="ui form" action="{{AppSubUrl}}/user/settings/security/openid" method="post">
{{.CsrfTokenHtml}}
<div class="required field {{if .Err_OpenID}}error{{end}}">
<label for="openid">{{ctx.Locale.Tr "settings.add_new_openid"}}</label>
<input id="openid" name="openid" type="text" required>
@@ -6,12 +6,10 @@
{{if .TOTPEnrolled}}
<p>{{ctx.Locale.Tr "settings.twofa_is_enrolled"}}</p>
<form class="ui form" action="{{AppSubUrl}}/user/settings/security/two_factor/regenerate_scratch" method="post" enctype="multipart/form-data">
{{.CsrfTokenHtml}}
<p>{{ctx.Locale.Tr "settings.regenerate_scratch_token_desc"}}</p>
<button class="ui primary button">{{ctx.Locale.Tr "settings.twofa_scratch_token_regenerate"}}</button>
</form>
<form class="ui form" action="{{AppSubUrl}}/user/settings/security/two_factor/disable" method="post" enctype="multipart/form-data" id="disable-form">
{{.CsrfTokenHtml}}
<p>{{ctx.Locale.Tr "settings.twofa_disable_note"}}</p>
<button class="ui red button delete-button" data-modal-id="disable-twofa" data-type="form" data-form="#disable-form">{{ctx.Locale.Tr "settings.twofa_disable"}}</button>
</form>
@@ -9,7 +9,6 @@
<p>{{ctx.Locale.Tr "settings.or_enter_secret" .TwofaSecret}}</p>
<p>{{ctx.Locale.Tr "settings.then_enter_passcode"}}</p>
<form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<div class="inline required field {{if .Err_Passcode}}error{{end}}">
<label for="passcode">{{ctx.Locale.Tr "passcode"}}</label>
<input id="passcode" name="passcode" autofocus required>