11 KiB
Frontend Hardening Notes
Purpose
This document records the frontend-only hardening currently applied on the main branch to raise the cost of scraping, asset reuse, and low-effort page reconstruction.
The goal is to make abuse less convenient, not to claim absolute protection.
Scope
All changes described here stay within the frontend project.
They do not rely on:
- backend API changes
- private OSS buckets
- signed image URLs
- server-side image rewriting
What Was Added
1. Protected page access control
Protected routes are blocked outside the official app environment in production-like modes.
Main files:
Behavior:
- public routes still open normally
- protected routes redirect to
/#/not_appoutside the app - protected API requests are rejected when the app bridge is unavailable
2. Runtime anti-debug and anti-copy restrictions
Protected pages add a lightweight runtime restriction layer in production-like modes.
Main files:
Behavior:
- blocks common devtools shortcuts
- blocks right-click, drag, copy, and text selection on protected pages
- reduces long-press image save convenience
3. Protected asset URL layer for Activities and Ranking
Most Activities/ and Ranking/ image references no longer expose raw OSS URLs directly in templates.
Main files:
- src/config/imagePaths.js
- src/utils/protectedAssets.js
- src/utils/protectedAssetRuntime.js
- src/directives/smartImage.js
- src/utils/image/imageCacheManager.js
Behavior:
getPngUrl()andgetWebpUrl()emitlikei-protected:...- runtime code resolves protected URLs only when rendering is needed
- most image rendering flows now end up using cached blob/object URLs
- pages that missed
v-smart-imgstill get a runtime fallback for protected imagesrc
4. background-image compatibility fixes
Several high-risk pages previously exposed asset URLs through inline background-image: url(...).
Adjusted files:
- src/views/Activities/LesserBairam/index.vue
- src/views/Activities/LuckyDollars/Season3/index.vue
- src/views/Activities/SpringFestival/index.vue
- src/views/Activities/SpringFestival/Task.vue
- src/views/Ranking/KingAndQueen/TopList.vue
Behavior:
- protected helpers are used before writing image values into CSS
- fewer raw asset URLs appear directly in template or style strings
5. Build artifact readability reduction
Build output names were changed from readable page/component names to hash-based filenames.
Main file:
Behavior:
- JS chunk names no longer reveal page names such as
TopList,WeeklyStar, orRanking - CSS files also use hash-based names
- static asset filenames are hash-based
6. Production log stripping with a troubleshooting switch
Production-like builds strip common debug output while still supporting a keep-logs build for app-side troubleshooting.
Main files:
Behavior:
productionbuilds removeconsole.logproductionbuilds removeconsole.debugproductionbuilds removeconsole.info- production-like builds remove
debugger console.warnandconsole.errorare preserved- Jenkins can keep using the original default build commands
Useful commands:
npm run build:prod
npm run build:prod:logs
7. Deferred main DOM mounting for selected high-risk pages
Some activity and ranking pages previously used v-show="!isLoading" for the main content. That kept the full DOM tree mounted early, which made initial inspection easier and also caused some observer timing issues.
These pages were updated to mount their main content with v-if="!isLoading" instead:
- src/views/Activities/LoginReward/index.vue
- src/views/Activities/LuckyDollars/Season4/index.vue
- src/views/Activities/SpringFestival/index.vue
- src/views/Ranking/Couple/index.vue
- src/views/Ranking/Overall/Ranking.vue
Extra handling was added where needed:
- pages with
IntersectionObservertargets now register observers only afterawait nextTick() - preload completion still clears
isLoadinginfinally, so preload failures do not leave the page permanently blank
8. Decorative background stacks
The shared background layer component previously supported a canvas rendering mode for stacked background images. This mode has now been disabled because large canvas backgrounds can increase memory and rendering pressure on low-end WebViews.
Main file:
Pages may still pass useCanvas, but the prop is now kept only for compatibility:
- src/views/Activities/LesserBairam/index.vue
- src/views/Activities/LoginReward/index.vue
- src/views/Activities/LuckyDollars/Season3/index.vue
- src/views/Ranking/Couple/index.vue
- src/views/Activities/LuckyDollars/Season4/index.vue
- src/views/Activities/SpringFestival/index.vue
- src/views/Ranking/GamesKing/index.vue
- src/views/Ranking/KingAndQueen/TopList.vue
- src/views/Ranking/Overall/Ranking.vue
- src/views/Ranking/WeeklyStar/WeeklyStar.vue
Behavior:
- stacked decorative backgrounds are rendered as normal
<img>elements throughv-smart-img - image loading still goes through protected asset resolution and the existing runtime/cache pipeline
- canvas background rendering is no longer used as a hardening layer because the UX and memory cost is higher than the practical protection gain
9. Stronger production minification and selector cleanup
Production-like builds use stricter esbuild minification flags, and a small set of highly readable page-local class names was reduced on sensitive pages.
Main files:
- vite.config.js
- src/views/Ranking/Couple/index.vue
- src/views/Activities/LuckyDollars/Season4/index.vue
- src/views/Activities/SpringFestival/index.vue
- src/views/Activities/SpringFestival/Ranking.vue
- src/views/Ranking/Overall/Ranking.vue
- src/views/Ranking/WeeklyStar/WeeklyStar.vue
10. Ranking/Couple comment cleanup and helper clarification
Recent cleanup also covered:
- src/views/Ranking/Couple/Ranking.vue
- src/components/BackgroundLayer.vue
- src/utils/protectedAssets.js
Behavior:
- garbled comments in
Ranking/Couple/Ranking.vuewere cleaned up and unified into Chinese comments useCanvasremains accepted for compatibility, butBackgroundLayerignores it and renders image backgroundstoCssBackgroundImage()stays as the CSS bridge for protected asset URLs
What This Helps Against
This frontend-only hardening is helpful against:
- direct browser opening of protected routes
- low-effort DOM scraping
- copying image URLs from templates or page source
- reading obvious page intent from build artifact names
- simple right-click, drag-save, long-press, and casual copying
- basic page reconstruction that depends on immediately available DOM structure
Current Limits
These changes still do not fully prevent:
- network-level image capture
- devtools users with enough time and persistence
- custom WebView, automation, or hook-based extraction
- rebuilding the same layout after visual inspection
- extracting DOM, CSS, and JS from any client that is allowed to render the page
If the client can render the page, the client must receive enough information to reproduce it. Frontend-only hardening can only increase cost, not provide absolute protection.
Recommended Testing
Protected route blocking
Use:
npm run dev:prod
or:
npm run build
npm run preview
Asset and page rendering checks
In production-like mode, verify:
ActivitiesandRankingpages still render correctly- protected page images still display normally
- protected asset URLs are less visible in runtime DOM and build output
- pages switched from
v-showtov-ifstill render after preload completes - pages with load-more observers still trigger lazy loading correctly
Build output checks
Run:
npm run build
Expected:
dist/jsfilenames are hash-baseddist/cssfilenames are hash-based- no obvious page names appear in bundle filenames
- production bundles no longer retain
console.log,console.debug,console.info, ordebugger