# Frontend Hardening Notes ## Purpose This document records the frontend-only hardening work added to this repository 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 are limited to the frontend project. This work does not rely on: - backend API changes - private OSS buckets - signed image URLs - server-side watermark rendering ## What Was Added ### 1. Protected page access control Protected routes are blocked outside the official app environment in production-like modes. Main files: - [src/utils/routeGuard.js](/D:/programs/likei-h5/src/utils/routeGuard.js) - [src/utils/http.js](/D:/programs/likei-h5/src/utils/http.js) - [src/config/security.js](/D:/programs/likei-h5/src/config/security.js) Behavior: - public routes still open normally - protected routes redirect to `/#/not_app` outside the app - protected API requests are rejected when the app bridge is unavailable ### 2. Runtime anti-debug and anti-copy restrictions Protected pages now add a lightweight runtime restriction layer in production-like modes. Main files: - [src/utils/runtimeSecurity.js](/D:/programs/likei-h5/src/utils/runtimeSecurity.js) - [src/main.js](/D:/programs/likei-h5/src/main.js) - [src/App.vue](/D:/programs/likei-h5/src/App.vue) Behavior: - blocks common devtools shortcuts - blocks right-click, drag, copy, and text selection on protected pages - reduces long-press image save convenience - adds page-level dynamic watermark overlays ### 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](/D:/programs/likei-h5/src/config/imagePaths.js) - [src/utils/protectedAssets.js](/D:/programs/likei-h5/src/utils/protectedAssets.js) - [src/utils/protectedAssetRuntime.js](/D:/programs/likei-h5/src/utils/protectedAssetRuntime.js) - [src/directives/smartImage.js](/D:/programs/likei-h5/src/directives/smartImage.js) - [src/utils/image/imageCacheManager.js](/D:/programs/likei-h5/src/utils/image/imageCacheManager.js) Behavior: - `getPngUrl()` and `getWebpUrl()` emit `likei-protected:...` - runtime code resolves protected URLs only when rendering is needed - most image rendering flows now end up using cached blob/object URLs - even pages that missed `v-smart-img` still get a runtime fallback for protected image `src` ### 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](/D:/programs/likei-h5/src/views/Activities/LesserBairam/index.vue) - [src/views/Activities/LuckyDollars/Season3/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season3/index.vue) - [src/views/Activities/SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue) - [src/views/Activities/SpringFestival/Task.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/Task.vue) - [src/views/Ranking/KingAndQueen/TopList.vue](/D:/programs/likei-h5/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: - [vite.config.js](/D:/programs/likei-h5/vite.config.js) Behavior: - JS chunk names no longer reveal page names such as `TopList`, `WeeklyStar`, or `Ranking` - CSS files also use hash-based names - static asset filenames are hash-based This does not stop reverse engineering, but it lowers the convenience of reading page structure from `dist/`. ### 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: - [vite.config.js](/D:/programs/likei-h5/vite.config.js) - [package.json](/D:/programs/likei-h5/package.json) - [scripts/build-with-version.js](/D:/programs/likei-h5/scripts/build-with-version.js) Behavior: - `production` and `production-atu` builds remove `console.log` - `production` and `production-atu` builds remove `console.debug` - `production` and `production-atu` builds remove `console.info` - production-like builds remove `debugger` - `console.warn` and `console.error` are preserved - Jenkins can keep using the original default build commands Useful commands: ```bash npm run build:prod npm run build:prod:logs npm run build:atu-prod npm run build:atu-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](/D:/programs/likei-h5/src/views/Activities/LoginReward/index.vue) - [src/views/Activities/LuckyDollars/Season4/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season4/index.vue) - [src/views/Activities/SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue) - [src/views/Ranking/Couple/index.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/index.vue) - [src/views/Ranking/Overall/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Overall/Ranking.vue) Extra handling was added where needed: - pages with `IntersectionObserver` targets now register observers only after `await nextTick()` - preload completion still clears `isLoading` in `finally`, so preload failures do not leave the page permanently blank - this keeps the original lazy-load behavior more stable after switching from `v-show` to `v-if` Important limit: This is only a weak hardening step. It reduces early DOM exposure and makes initial inspection less direct, but it does not stop scraping once the page is fully rendered on the client. ### 8. Canvas-rendered decorative background stacks The shared background layer component now supports a canvas rendering mode for stacked background images. Main file: - [src/components/BackgroundLayer.vue](/D:/programs/likei-h5/src/components/BackgroundLayer.vue) Current high-risk pages using this mode: - [src/views/Activities/LesserBairam/index.vue](/D:/programs/likei-h5/src/views/Activities/LesserBairam/index.vue) - [src/views/Activities/LoginReward/index.vue](/D:/programs/likei-h5/src/views/Activities/LoginReward/index.vue) - [src/views/Activities/LuckyDollars/Season3/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season3/index.vue) - [src/views/Ranking/Couple/index.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/index.vue) - [src/views/Activities/LuckyDollars/Season4/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season4/index.vue) - [src/views/Activities/SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue) - [src/views/Ranking/GamesKing/index.vue](/D:/programs/likei-h5/src/views/Ranking/GamesKing/index.vue) - [src/views/Ranking/KingAndQueen/TopList.vue](/D:/programs/likei-h5/src/views/Ranking/KingAndQueen/TopList.vue) - [src/views/Ranking/Overall/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Overall/Ranking.vue) - [src/views/Ranking/WeeklyStar/WeeklyStar.vue](/D:/programs/likei-h5/src/views/Ranking/WeeklyStar/WeeklyStar.vue) Behavior: - stacked decorative backgrounds are drawn into a single canvas instead of a readable `` list - most page-level decorative background structure becomes less obvious in Elements inspection - protected asset resolution still goes through the existing runtime/cache pipeline This does not hide the pixels from a determined attacker, but it reduces direct DOM readability and low-effort structure copying. ### 9. Stronger production minification and selector cleanup Production-like builds now 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](/D:/programs/likei-h5/vite.config.js) - [src/views/Ranking/Couple/index.vue](/D:/programs/likei-h5/src/views/Ranking/Couple/index.vue) - [src/views/Activities/LuckyDollars/Season4/index.vue](/D:/programs/likei-h5/src/views/Activities/LuckyDollars/Season4/index.vue) - [src/views/Activities/SpringFestival/index.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/index.vue) - [src/views/Activities/SpringFestival/Ranking.vue](/D:/programs/likei-h5/src/views/Activities/SpringFestival/Ranking.vue) - [src/views/Ranking/Overall/Ranking.vue](/D:/programs/likei-h5/src/views/Ranking/Overall/Ranking.vue) - [src/views/Ranking/WeeklyStar/WeeklyStar.vue](/D:/programs/likei-h5/src/views/Ranking/WeeklyStar/WeeklyStar.vue) Behavior: - production-like builds explicitly minify identifiers, syntax, and whitespace - legal comments are removed from build output - some obvious local selector names were replaced with shorter neutral names - selector cleanup now also covers obvious ranking navigation and history labels on additional `Activities` and `Ranking` pages - `SpringFestival/Ranking` now waits for `nextTick()` before observing its load sentinel and disconnects the observer on unmount ## 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 Important rule: If the client can render the page, the client must receive enough information to reproduce the page. That is why frontend-only hardening can only increase cost, not provide absolute protection. ## Recommended Testing ### Protected route blocking Use: ```bash npm run dev:prod ``` or: ```bash npm run build npm run preview ``` Expected: - protected routes redirect to `/#/not_app` outside the app - public routes still open normally ### Asset and page rendering checks In production-like mode, verify: - `Activities` and `Ranking` pages 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-show` to `v-if` still render after preload completes - pages with load-more observers still trigger lazy loading correctly ### Build output checks Run: ```bash npm run build ``` Expected: - `dist/js` filenames are hash-based - `dist/css` filenames are hash-based - no obvious page names appear in bundle filenames - production bundles no longer retain `console.log`, `console.debug`, `console.info`, or `debugger` ## Suggested Next Steps If staying frontend-only: - move more decorative shells from plain DOM/CSS into canvas rendering - reduce semantic structure in the most sensitive event pages - keep replacing remaining raw `background-image` and direct image flows with protected helpers If minimal backend support becomes acceptable later: - move sensitive OSS paths to private read - issue short-lived signed URLs - watermark sensitive images per user on the server side - validate app session or device identity before returning sensitive assets