Zero-config encrypted localStorage replacement using device fingerprinting for automatic key generation.
- 🔐 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
npm install @arnonsang/secure-local-storage
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
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
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
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>
);
}
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;
}
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();
// 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 daily
-
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
- 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
Encrypts and stores a string value.
Retrieves and decrypts a stored value. Returns null
if not found or decryption fails.
Removes an encrypted item and its associated IV.
Removes all encrypted items managed by secure-local-storage.
Checks if an encrypted item exists and can be decrypted.
Returns all keys of encrypted items.
Returns the current device fingerprint (useful for debugging).
Updates the storage configuration at runtime.
Rotates encryption keys while preserving all stored data.
Returns the number of encrypted items stored.
Returns the key at the specified index.
- ✅ Chrome 60+
- ✅ Firefox 55+
- ✅ Safari 11+
- ✅ Edge 79+
Requires crypto.subtle
API support.
- 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
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'
});
// Before
localStorage.setItem('key', 'value');
const value = localStorage.getItem('key');
// After
await secureLocalStorage.setItem('key', 'value');
const value = await secureLocalStorage.getItem('key');
MIT © arnonsang
- 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