@arnonsang/secure-local-storage
v2.0.0
Published
Zero-config encrypted localStorage replacement using device fingerprinting
Maintainers
Readme
@arnonsang/secure-local-storage
Zero-config encrypted localStorage replacement using device fingerprinting for automatic key generation.
Features
- 🔐 Zero Configuration: No setup required, works out of the box
- 🔒 Device-Bound Encryption: Uses device fingerprinting for automatic key generation
- 🔄 Backward Compatible: Seamlessly migrates from legacy encryption methods
- 🚀 Drop-in Replacement: Same API as localStorage but encrypted
- 🛡️ AES-GCM Encryption: Military-grade encryption with PBKDF2 key derivation
- 🌐 TypeScript Support: Full TypeScript definitions included
- 📱 Browser Only: Designed for client-side browser storage
Installation
npm install @arnonsang/secure-local-storageUsage
Basic Usage (Default/Legacy - No Configuration Needed)
The simplest way to use the library - works exactly like v1.x with enhanced v2.0 security:
import secureLocalStorage from '@arnonsang/secure-local-storage';
// Save encrypted data
await secureLocalStorage.setItem('user-token', 'your-secret-token');
// Retrieve and decrypt data
const token = await secureLocalStorage.getItem('user-token');
console.log(token); // 'your-secret-token'
// Remove encrypted data
secureLocalStorage.removeItem('user-token');
// Clear all encrypted data
secureLocalStorage.clear();
// Check storage info
console.log('Storage length:', secureLocalStorage.length);
console.log('All keys:', secureLocalStorage.keys());
// Get device fingerprint (useful for debugging)
const fingerprint = await secureLocalStorage.getDeviceFingerprint();✅ This default instance automatically gets v2.0 security enhancements:
- Dynamic salt generation
- Enhanced device fingerprinting
- Secure fallback mechanisms
- Automatic migration from v1.x data
Advanced Usage (Custom Configuration - v2.0+)
For applications requiring enhanced security or custom settings, create your own configured instance:
import { SecureLocalStorage } from '@arnonsang/secure-local-storage';
// Create a custom configured instance
const customStorage = new SecureLocalStorage({
customSalt: 'your-unique-app-salt', // Custom salt for key derivation
additionalEntropy: 'user-specific-data', // Extra entropy for fingerprinting
keyPrefix: 'myapp_', // Custom prefix for localStorage keys
ivSuffix: '_vector', // Custom suffix for IV keys
iterations: 150000, // Higher PBKDF2 iterations
enableLegacySupport: false, // Disable legacy compatibility
enableDeprecationWarnings: true // Show deprecation warnings
});
// Use the custom instance exactly like the default one
await customStorage.setItem('secure-data', 'sensitive-information');
const value = await customStorage.getItem('secure-data');
// Custom instance has all the same methods
console.log('Custom storage length:', customStorage.length);
console.log('Custom storage keys:', customStorage.keys());
// Runtime configuration changes
customStorage.configure({
additionalEntropy: 'updated-context-data'
});
// Key rotation for enhanced security
await customStorage.rotateKeys();🔐 Benefits of Custom Configuration:
- Enhanced Security: Custom salts and entropy for your specific application
- Isolation: Separate storage instances for different security contexts
- Flexibility: Adjust security parameters based on your requirements
- Future-Proof: Easy to enhance security without changing your codebase
Choosing Between Default and Custom Configuration
| Feature | Default Instance (import default) | Custom Instance (new SecureLocalStorage) | |---------|-----------------------------------|-------------------------------------------| | Ease of Use | ✅ Simplest - zero configuration | ⚡ Requires configuration object | | Backward Compatibility | ✅ 100% compatible with v1.x | ✅ Fully compatible | | Security Level | ✅ Enhanced v2.0 security | 🔐 Maximum customizable security | | Custom Salts | ❌ Uses generated salt | ✅ Your own application-specific salt | | Multiple Instances | ❌ Single global instance | ✅ Multiple isolated instances | | Key Rotation | ✅ Available | ✅ Available | | Runtime Config | ❌ Fixed configuration | ✅ Runtime configuration changes |
💡 Recommendation:
- Use Default for: Simple projects, quick prototypes, migrating from v1.x
- Use Custom for: Production apps, high-security requirements, multi-tenant applications
Usage with React
Option 1: Default Instance (Simplest)
import { useEffect, useState } from 'react';
import secureLocalStorage from '@arnonsang/secure-local-storage';
function useSecureStorage<T>(key: string, defaultValue: T) {
const [value, setValue] = useState<T>(defaultValue);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadValue = async () => {
try {
const stored = await secureLocalStorage.getItem(key);
if (stored !== null) {
setValue(JSON.parse(stored));
}
} catch (error) {
console.error('Failed to load from secure storage:', error);
} finally {
setLoading(false);
}
};
loadValue();
}, [key]);
const setStoredValue = async (newValue: T) => {
try {
setValue(newValue);
await secureLocalStorage.setItem(key, JSON.stringify(newValue));
} catch (error) {
console.error('Failed to save to secure storage:', error);
}
};
return [value, setStoredValue, loading] as const;
}
// Usage in component
function MyComponent() {
const [userData, setUserData, loading] = useSecureStorage('user-data', null);
if (loading) return <div>Loading...</div>;
return (
<div>
<pre>{JSON.stringify(userData, null, 2)}</pre>
<button onClick={() => setUserData({ name: 'John', age: 30 })}>
Save User Data
</button>
</div>
);
}Option 2: Custom Configuration (Enhanced Security)
import { useEffect, useState, useContext, createContext } from 'react';
import { SecureLocalStorage } from '@arnonsang/secure-local-storage';
// Create a custom storage instance for your app
const appStorage = new SecureLocalStorage({
customSalt: 'my-react-app-salt-2024',
additionalEntropy: 'user-session-context',
keyPrefix: 'myapp_',
iterations: 200000 // Higher security
});
// Optional: Create React Context for storage
const StorageContext = createContext(appStorage);
function useCustomSecureStorage<T>(key: string, defaultValue: T) {
const storage = useContext(StorageContext);
const [value, setValue] = useState<T>(defaultValue);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadValue = async () => {
try {
const stored = await storage.getItem(key);
if (stored !== null) {
setValue(JSON.parse(stored));
}
} catch (error) {
console.error('Failed to load from secure storage:', error);
} finally {
setLoading(false);
}
};
loadValue();
}, [key, storage]);
const setStoredValue = async (newValue: T) => {
try {
setValue(newValue);
await storage.setItem(key, JSON.stringify(newValue));
} catch (error) {
console.error('Failed to save to secure storage:', error);
}
};
return [value, setStoredValue, loading] as const;
}Advanced Configuration (v2.0+)
For enhanced security, you can configure the library with custom settings:
import { SecureLocalStorage } from '@arnonsang/secure-local-storage';
const storage = new SecureLocalStorage({
customSalt: 'your-custom-salt', // Custom salt for key derivation
additionalEntropy: 'extra-entropy', // Additional entropy for fingerprinting
keyPrefix: 'myapp_', // Custom prefix for keys
ivSuffix: '_vector', // Custom suffix for IV keys
iterations: 150000, // PBKDF2 iterations (default: 100000)
enableLegacySupport: true, // Enable legacy compatibility (default: true)
enableDeprecationWarnings: false // Show deprecation warnings (default: true)
});
// Runtime configuration changes
storage.configure({
additionalEntropy: 'updated-entropy'
});
// Key rotation for enhanced security
await storage.rotateKeys();Security Best Practices
// For sensitive applications, consider:
const secureStorage = new SecureLocalStorage({
customSalt: 'your-unique-application-salt',
additionalEntropy: 'user-specific-data',
iterations: 200000, // Higher iterations for more security
});
// Implement periodic key rotation
setInterval(async () => {
await secureStorage.rotateKeys();
}, 24 * 60 * 60 * 1000); // Rotate dailyHow It Works
Encryption Process
Device Fingerprinting: Creates a unique hash based on:
- Canvas fingerprint
- Screen properties
- Browser/navigator information
- WebGL properties
- Timezone information
Key Derivation: Uses PBKDF2 with 100,000 iterations to derive encryption keys from device fingerprint
AES-GCM Encryption: Encrypts data using AES-GCM with 256-bit keys and random IVs
Storage: Stores encrypted data and IV separately in localStorage with prefixed keys
Security Features
- No Stored Keys: Encryption keys are never stored, only derived from device characteristics
- Device Binding: Data can only be decrypted on the same device it was encrypted on
- Enhanced Salt Generation: Dynamic salt generation using cryptographically secure random values
- Custom Entropy Support: Add your own entropy for enhanced security
- Key Rotation: Rotate encryption keys while preserving data
- Automatic Migration: Seamlessly upgrades from legacy encryption methods
- IV Randomization: Each encryption uses a fresh random IV for maximum security
- Configurable Security: Adjust iterations, salts, and other security parameters
API Reference
Methods
setItem(key: string, value: string): Promise<void>
Encrypts and stores a string value.
getItem(key: string): Promise<string | null>
Retrieves and decrypts a stored value. Returns null if not found or decryption fails.
removeItem(key: string): void
Removes an encrypted item and its associated IV.
clear(): void
Removes all encrypted items managed by secure-local-storage.
hasItem(key: string): Promise<boolean>
Checks if an encrypted item exists and can be decrypted.
keys(): Promise<string[]>
Returns all keys of encrypted items.
getDeviceFingerprint(): Promise<string>
Returns the current device fingerprint (useful for debugging).
configure(config: Partial<SecureStorageConfig>): void
Updates the storage configuration at runtime.
rotateKeys(): Promise<void>
Rotates encryption keys while preserving all stored data.
Properties
length: number
Returns the number of encrypted items stored.
key(index: number): string | null
Returns the key at the specified index.
Browser Compatibility
- ✅ Chrome 60+
- ✅ Firefox 55+
- ✅ Safari 11+
- ✅ Edge 79+
Requires crypto.subtle API support.
Security Considerations
- Device-Bound: Data is tied to the device it was encrypted on
- Re-authentication: Device changes may require users to re-authenticate
- Client-Side Only: This is designed for client-side storage security
- Salt Management: Use custom salts for production applications
- Key Rotation: Implement periodic key rotation for enhanced security
- Entropy Sources: Consider adding application-specific entropy
- Legacy Support: Disable legacy support in new applications for better security
- Not for Sensitive Data: Don't store highly sensitive data that requires server-side encryption
Migration from v1.x
The v2.0 upgrade is automatically backward compatible. Your existing code will continue to work without changes:
// v1.x code - still works in v2.x
import secureLocalStorage from '@arnonsang/secure-local-storage';
await secureLocalStorage.setItem('key', 'value');To take advantage of new security features:
// v2.x - enhanced security
import { SecureLocalStorage } from '@arnonsang/secure-local-storage';
const storage = new SecureLocalStorage({
customSalt: 'your-app-salt',
additionalEntropy: 'user-context'
});Migration from localStorage
// Before
localStorage.setItem('key', 'value');
const value = localStorage.getItem('key');
// After
await secureLocalStorage.setItem('key', 'value');
const value = await secureLocalStorage.getItem('key');License
MIT © arnonsang
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
