Commit 6b0f90f3 authored by Johnny's avatar Johnny

chore: prevent sensitive data caching

parent eaef04a0
...@@ -238,6 +238,24 @@ func (s *APIV1Service) UpdateUser(ctx context.Context, request *v1pb.UpdateUserR ...@@ -238,6 +238,24 @@ func (s *APIV1Service) UpdateUser(ctx context.Context, request *v1pb.UpdateUserR
case "email": case "email":
update.Email = &request.User.Email update.Email = &request.User.Email
case "avatar_url": case "avatar_url":
// Validate avatar MIME type to prevent XSS during upload
if request.User.AvatarUrl != "" {
imageType, _, err := extractImageInfo(request.User.AvatarUrl)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid avatar format: %v", err)
}
// Only allow safe image formats for avatars
allowedAvatarTypes := map[string]bool{
"image/png": true,
"image/jpeg": true,
"image/jpg": true,
"image/gif": true,
"image/webp": true,
}
if !allowedAvatarTypes[imageType] {
return nil, status.Errorf(codes.InvalidArgument, "invalid avatar image type: %s. Only PNG, JPEG, GIF, and WebP are allowed", imageType)
}
}
update.AvatarURL = &request.User.AvatarUrl update.AvatarURL = &request.User.AvatarUrl
case "description": case "description":
update.Description = &request.User.Description update.Description = &request.User.Description
......
...@@ -35,8 +35,12 @@ func (*FrontendService) Serve(_ context.Context, e *echo.Echo) { ...@@ -35,8 +35,12 @@ func (*FrontendService) Serve(_ context.Context, e *echo.Echo) {
if util.HasPrefixes(c.Path(), "/api", "/memos.api.v1") { if util.HasPrefixes(c.Path(), "/api", "/memos.api.v1") {
return true return true
} }
// Skip setting cache headers for index.html // For index.html and root path, set no-cache headers to prevent browser caching
// This prevents sensitive data from being accessible via browser back button after logout
if c.Path() == "/" || c.Path() == "/index.html" { if c.Path() == "/" || c.Path() == "/index.html" {
c.Response().Header().Set(echo.HeaderCacheControl, "no-cache, no-store, must-revalidate")
c.Response().Header().Set("Pragma", "no-cache")
c.Response().Header().Set("Expires", "0")
return false return false
} }
// Set Cache-Control header for static assets. // Set Cache-Control header for static assets.
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" /> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/webp" href="/logo.webp" /> <link rel="icon" type="image/webp" href="/logo.webp" />
<link rel="manifest" href="/site.webmanifest" /> <link rel="manifest" href="/site.webmanifest" />
......
...@@ -64,7 +64,9 @@ const UserMenu = observer((props: Props) => { ...@@ -64,7 +64,9 @@ const UserMenu = observer((props: Props) => {
keysToRemove.forEach((key) => localStorage.removeItem(key)); keysToRemove.forEach((key) => localStorage.removeItem(key));
window.location.href = Routes.AUTH; // Use replace() instead of href to prevent back button from showing cached sensitive data
// This removes the current page from browser history
window.location.replace(Routes.AUTH);
}; };
return ( return (
......
...@@ -92,7 +92,9 @@ function getAuthFailureRedirect(currentPath: string): string | null { ...@@ -92,7 +92,9 @@ function getAuthFailureRedirect(currentPath: string): string | null {
function performRedirect(redirectUrl: string | null): void { function performRedirect(redirectUrl: string | null): void {
if (redirectUrl) { if (redirectUrl) {
window.location.href = redirectUrl; // Use replace() instead of href to prevent back button from showing cached sensitive data
// This removes the current page from browser history after authentication failure
window.location.replace(redirectUrl);
} }
} }
......
...@@ -24,12 +24,13 @@ const RootLayout = observer(() => { ...@@ -24,12 +24,13 @@ const RootLayout = observer(() => {
if (!currentUser) { if (!currentUser) {
// If disallowPublicVisibility is enabled, redirect to the login page if the user is not logged in. // If disallowPublicVisibility is enabled, redirect to the login page if the user is not logged in.
if (instanceStore.state.memoRelatedSetting.disallowPublicVisibility) { if (instanceStore.state.memoRelatedSetting.disallowPublicVisibility) {
window.location.href = Routes.AUTH; // Use replace() to prevent back button from showing cached sensitive data
window.location.replace(Routes.AUTH);
return; return;
} else if ( } else if (
([Routes.ROOT, Routes.ATTACHMENTS, Routes.INBOX, Routes.ARCHIVED, Routes.SETTING] as string[]).includes(location.pathname) ([Routes.ROOT, Routes.ATTACHMENTS, Routes.INBOX, Routes.ARCHIVED, Routes.SETTING] as string[]).includes(location.pathname)
) { ) {
window.location.href = Routes.EXPLORE; window.location.replace(Routes.EXPLORE);
return; return;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment