Expo Web中用localStorage存储JWT是否安全?存在哪些潜在风险?
问题描述
我使用Expo Secure Store存储来自Supabase的JWT凭证,对应的适配器代码如下:
const ExpoSecureStoreAdapter = { getItem: (key: string) => { return SecureStore.getItemAsync(key); }, setItem: (key: string, value: string) => { SecureStore.setItemAsync(key, value); }, removeItem: (key: string) => { SecureStore.deleteItemAsync(key); }, };
该方案在iOS/Android端运行良好,但Expo Web并不支持。因此我改用localStorage作为Web客户端的存储方案,对应的适配器代码如下:
const WebLocalStorageAdapter = { getItem: async (key: string) => { return localStorage.getItem(key); }, setItem: async (key: string, value: string) => { return localStorage.setItem(key, value); }, removeItem: async (key: string) => { return localStorage.removeItem(key); }, }
随后通过以下代码根据平台选择对应的适配器:
const isWeb = Platform.OS === 'web'; const storageAdapter = isWeb ? WebLocalStorageAdapter : ExpoSecureStoreAdapter;
请问这种做法是否可取?是否存在我未察觉的攻击向量或其他问题?
回答
这种跨平台适配的思路是可行的,但Web端用localStorage存储JWT确实存在几个需要重点关注的风险和细节问题:
核心风险点
- XSS攻击隐患:
localStorage的内容可被同源下的任意JavaScript直接读取,如果你的站点存在XSS漏洞,攻击者注入的恶意脚本能轻松窃取JWT凭证,进而冒充用户执行操作。而原生端的Expo Secure Store是系统级安全存储,普通应用内脚本无法随意访问,安全性高出不少。 - 无自动过期机制:
localStorage不会主动清理存储内容,除非手动删除或用户清除浏览器缓存。即便JWT本身带过期时间,你也得额外实现逻辑去校验并移除过期凭证,否则会出现用失效token发起请求的情况。 - 跨标签页状态不同步:当用户打开多个站点标签页时,
localStorage的修改会同步到所有同源标签页。如果其中一个标签页登出并删除了JWT,其他标签页需要监听storage事件来同步状态,否则会出现用户已登出但其他标签页仍保持登录状态的不一致问题。
优化建议
- Web端优先用HttpOnly Cookie存JWT:如果Supabase后端支持,把JWT存入带有HttpOnly、Secure、SameSite属性的Cookie中,这样JavaScript无法读取该Cookie,能从根源上避免XSS攻击带来的凭证泄露问题。不过这种方式需要处理跨域请求的Cookie传递逻辑。
- 必须用localStorage时加防护:如果业务场景限制只能用
localStorage,要确保站点没有XSS漏洞——比如严格过滤用户输入、配置内容安全策略(CSP)限制脚本加载、禁用eval等危险API。同时给JWT设置较短的过期时间,配合刷新token机制,降低凭证泄露后的风险范围。 - 统一适配器的异步签名:注意到
ExpoSecureStoreAdapter的方法返回Promise但未显式标记async,而Web适配器标记了。虽然不会直接报错,但统一加上async能让代码风格更一致,避免潜在的类型校验问题:
const ExpoSecureStoreAdapter = { getItem: async (key: string) => { return SecureStore.getItemAsync(key); }, setItem: async (key: string, value: string) => { return SecureStore.setItemAsync(key, value); }, removeItem: async (key: string) => { return SecureStore.deleteItemAsync(key); }, };
内容的提问来源于stack exchange,提问作者alyx
相关产品推荐
相关产品推荐

