xss vulnerability fix

This commit is contained in:
Hamid Reza Mohammadi 2026-09-25 17:46:22 +03:30
parent ce05b9be0e
commit af75cc18d9
8 changed files with 56 additions and 3 deletions

View File

@ -105,7 +105,10 @@ namespace GanjooRazor.Areas.User.Pages
{
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(-1)
Expires = DateTime.Now.AddDays(-1),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
foreach (var cookieName in new string[] { "UserId", "SessionId", "Token", "Username", "Name", "NickName", "CanEdit", "KeepHistory" })
{

View File

@ -67,6 +67,9 @@ namespace GanjooRazor.Areas.User.Pages
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(365),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
Response.Cookies.Append("KeepHistory", $"{TrackingIsEnabled}", cookieOption);
}
@ -206,6 +209,9 @@ namespace GanjooRazor.Areas.User.Pages
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(365),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
Response.Cookies.Append("KeepHistory", $"{false}", cookieOption);
@ -239,6 +245,9 @@ namespace GanjooRazor.Areas.User.Pages
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(365),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
Response.Cookies.Append("KeepHistory", $"{true}", cookieOption);

View File

@ -235,9 +235,16 @@ namespace GanjooRazor.Pages
LoggedOnUserModelEx loggedOnUser = JsonConvert.DeserializeObject<LoggedOnUserModelEx>(await response.Content.ReadAsStringAsync());
// Authentication-related cookies are never read by client-side JavaScript
// (only server-side C# reads Request.Cookies[...]), so they can safely be
// marked HttpOnly to stop them being exfiltrated via document.cookie in the
// event of an XSS bug. Secure/SameSite=Lax provide additional defense-in-depth.
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(365),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
Response.Cookies.Append("UserId", loggedOnUser.User.Id.ToString(), cookieOption);

View File

@ -247,9 +247,16 @@ namespace GanjooRazor.Pages
LoggedOnUserModelEx loggedOnUser = JsonConvert.DeserializeObject<LoggedOnUserModelEx>(await response.Content.ReadAsStringAsync());
// Authentication-related cookies are never read by client-side JavaScript
// (only server-side C# reads Request.Cookies[...]), so they can safely be
// marked HttpOnly to stop them being exfiltrated via document.cookie in the
// event of an XSS bug. Secure/SameSite=Lax provide additional defense-in-depth.
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(365),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
Response.Cookies.Append("UserId", loggedOnUser.User.Id.ToString(), cookieOption);

View File

@ -165,11 +165,18 @@ else if (Model.ReadOnlyMode)
@{
if (!string.IsNullOrEmpty(Model.PinterestUrl))
{
// PinterestUrl comes straight from the untrusted "pinterest_url" query string
// (see GanjoorPage.cshtml.cs). It must never be emitted with @Html.Raw into a
// <script> block - that allowed a reflected XSS via payloads such as
// "</script><script>alert(document.cookie)</script>". Instead it is serialized
// as a JSON string literal (which escapes '<', '>', '&', quotes, etc.) and then
// URL-encoded again before being appended to href values.
<script>
$(function() {
var pinterestUrl = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Model.PinterestUrl));
$("a").attr('href', function(i, h) {
if (h != null) {
return h + (h.indexOf('?') != -1 ? "&pinterest_url=@Html.Raw(Model.PinterestUrl)" : "?pinterest_url=@Html.Raw(Model.PinterestUrl)");
return h + (h.indexOf('?') != -1 ? "&pinterest_url=" + encodeURIComponent(pinterestUrl) : "?pinterest_url=" + encodeURIComponent(pinterestUrl));
}
return h;
});

View File

@ -1128,6 +1128,9 @@ namespace GanjooRazor.Pages
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(365),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
Response.Cookies.Append("KeepHistory", $"{false}", cookieOption);
}

View File

@ -136,7 +136,10 @@ namespace GanjooRazor.Pages
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(-1)
Expires = DateTime.Now.AddDays(-1),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
foreach (var cookieName in new string[] { "UserId", "SessionId", "Token", "Username", "Name", "NickName", "CanEdit", "KeepHistory", "CanTranslate" })
{
@ -177,9 +180,16 @@ namespace GanjooRazor.Pages
LoggedOnUserModelEx loggedOnUser = JsonConvert.DeserializeObject<LoggedOnUserModelEx>(await response.Content.ReadAsStringAsync());
// Authentication-related cookies are never read by client-side JavaScript
// (only server-side C# reads Request.Cookies[...]), so they can safely be
// marked HttpOnly to stop them being exfiltrated via document.cookie in the
// event of an XSS bug. Secure/SameSite=Lax provide additional defense-in-depth.
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(365),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
Response.Cookies.Append("UserId", loggedOnUser.User.Id.ToString(), cookieOption);

View File

@ -88,9 +88,16 @@ namespace GanjooRazor.Utils
LoggedOnUserModelEx loggedOnUser = JsonConvert.DeserializeObject<LoggedOnUserModelEx>(await reLoginResponse.Content.ReadAsStringAsync());
// Authentication-related cookies are never read by client-side JavaScript
// (only server-side C# reads Request.Cookies[...]), so they can safely be
// marked HttpOnly to stop them being exfiltrated via document.cookie in the
// event of an XSS bug. Secure/SameSite=Lax provide additional defense-in-depth.
var cookieOption = new CookieOptions()
{
Expires = DateTime.Now.AddDays(365),
HttpOnly = true,
Secure = true,
SameSite = SameSiteMode.Lax,
};
response.Cookies.Append("UserId", loggedOnUser.User.Id.ToString(), cookieOption);