首发于公众号 前端混合开发,欢迎关注。

作为应用开发者,管理我们应用的安全性至关重要。让我们以银行应用为例:实施加密组件并不是一件容易的事。我们必须:

  • 与团队讨论并达成一致,选择一个加密算法,例如RSA或三重DES
  • 使用复杂的数学来编写一个算法
  • 测试该功能并确保其能抵御密码学攻击
  • 最后,确保加密方法也具有高效性

这对小团队来说并不是一件容易的任务,因为它涉及到雇佣安全专业人员,这需要时间和金钱。相反,我们可能更应该使用一个已经为我们做了大部分工作的第三方库。

在这篇文章中,你将学习如何在React Native中使用加密库。我们将使用Expo来在这篇文章中设置我们的应用程序。要设置一个基础的Expo项目:

npx create-expo-app --template bare-minimum

我们开始吧

什么是RSA加密?

RSA 在密码学世界中是家喻户晓的名字;HTTPS和SSH使用RSA加密,因为它是一个强大的算法。它使用两个密钥,一个公钥和一个私钥,来加密和解密消息。

它是如何工作的?

该算法执行这些步骤来生成密钥并执行加密:

  1. 创建两个大的质数, pq 。数字越大,安全性越好。
  2. pq 相乘。将这个结果命名为 n:n = p*q 。
  3. 选择一个数字, e ,其中 e 应与 n 互质
  4. 使用此公式计算私钥, d :

image.png

  1. 对于加密,取信息, m ,将其转换为数字,然后像这样加密它:

image.png

  1. 要解密,可以使用这个公式:

image.png

既然我们已经学习了RSA加密的基础知识,那么让我们来学习一下初始化向量。

初始化向量

初始化向量(IV)是一个随机数,确保即使秘密密钥相同,密文也总是不同的。这防止了攻击者解码数据。

尽管 IV 不是秘密的,但对于每次加密步骤使用不同的 IV 是至关重要的。如果算法在加密操作中重用了一个 IV,即使攻击者不知道秘密密钥,也可能允许攻击者解密密文。

这里有一个更简单的类比:

  • 想象一下,你正在使用一种秘密代码对信息进行编码。你开始加密过程是通过写下一个随机的词:这就是 IV。
  • 使用不同的随机词重新加密相同的信息会产生不同的密文。这是因为随机词改变了加密的起始点。
  • 然而,如果你使用旧的初始化向量来加密你的信息,我们将得到一个与之前操作类似的密文,这就给攻击者一个机会去检查和解码消息的内容

盐值

盐值是在密码被散列之前添加到密码中的一串随机字符。这有助于保护密码不被破解,即使攻击者拥有一份预先计算好的散列列表,如彩虹表。

当用户创建密码时,网站或应用会生成一个随机的盐值,并在对密码进行哈希处理之前将其添加到密码中。然后,服务器将盐值和哈希密码存储在数据库中。

此外,盐值很重要,因为它们使得破解密码变得更加困难。即使攻击者有一份预先计算的哈希值列表,除非他们也有盐值,否则他们将无法破解密码。这是因为盐值改变了函数的输出,使得相同的哈希密码产生差异。

在本文的下一部分,我们将探索一些最常用的React Native加密库。

Expo SecureStore

由Expo团队引入的 SecureStore 是一个库,它允许开发者在设备存储中加密并存储键值对。

要使用 Expo SecureStore,首先在你的项目中安装:

npx expo install expo-secure-store

以下的代码使用 SecureStore 来加密,保存,并从存储中检索数据:

import React, { useEffect, useState } from "react";
import { Text, View } from "react-native";
import * as SecureStore from "expo-secure-store";

//首先使用密钥将数据存储在加密数据库中:
async function storeData() {
  await SecureStore.setItemAsync(
    "encryption_message_expo",
    "这条消息是使用 Expo Secure store 加密的"
  );
}

//现在用给定的密钥检索它:
async function retrieveData() {
  const result = await SecureStore.getItemAsync("encryption_message_expo");
  if (result) {
    return result;
  }
  alert("值无法保存");

}
export default function ExpoSecureStore() {
  const [obtainedData, setObtainedData] = useState("");
  const performEncryption = async () => {
    await storeData();
    const data = await retrieveData();
    setObtainedData(data);
  };

  //当组件首次挂载时,保存并检索值:
  useEffect(() => {
    performEncryption();
  }, []);

  //现在显示它:
  return (
    <View>
      <Text style={{ fontSize: 15 }}>
        'encryption_message_expo' 的加密值:
      </Text>
      <Text style={{ fontSize: 20 }}> {obtainedData}</Text>
    </View>
  );
}

要运行此应用,请运行此终端命令:

npm run start 

image.png

SecureStore的优点与缺点

以下是一些你可能想在你的项目中使用这项技术的原因:

  • 得到Expo的支持,这意味着有一个大团队正在积极地更新和优化这个库
  • 清晰易懂的文档
  • 定期更新
  • 支持常量以确保只在特定情况下解密数据,例如,如果设备已经解锁

然而,存在一些缺点:

  • 每个键值对的大小可以达到2MB。因此,你不能在加密存储中保存大文件或数据。
  • 不支持自定义钩子

React Native MMKV 存储

React Native MMKV Storage 是另一种帮助本地保存数据的选择。尽管默认情况下保存的数据是未加密的,但这个库使切换到安全存储变得容易。

作为第一步,将 react-native-mmkv-storage 集成到你的Expo项目中,如下所示:

npx expo install react-native-mmkv-storage
npx expo prebuild #generate native code

然后,在你的项目中,创建一个名为 useStorage.js 的空白文件。在这里,编写以下代码:

// file name: useStorage.js. This file will hold our custom Hook 
// this hook will encrypt, save and retrieve our data.
import { MMKVLoader, useMMKVStorage } from "react-native-mmkv-storage";

//create a db. The 'withEncryption' function encrypts the database.
const MMKV = new MMKVLoader().withEncryption().initialize();

export const useStorage = (key, defaultValue) => {
  //our custom hook will get and store data:
  const [value, setValue] = useMMKVStorage(key, MMKV, defaultValue);
  return [value, setValue];
};

下面的代码片段展示了我们自定义钩子的使用:

import { View, Text } from "react-native";
import { useStorage } from "./useStorage"; //import the Hook

export default function ReactNativeMMKVStorage() {
    //access the database and set a field called 'user'. It's default value will be 'LoRocket'.
  const [data, setData] = useStorage("user", "LogRocket");

  return (
    <View>
      <Text>value for user in MMKV {data}</Text>
    </View>
  );
}

image.png

React Native MMKV存储的优点与缺点

以下是一些使其适合于你下一个项目的因素:

  • 使用JSI模块,使其性能出众
  • 小巧轻便
  • 清晰,简洁的文档
  • 支持自定义钩子
  • 定期更新

然而,我想指出一点:后续的更新引入了重大变化。这意味着你不能在不升级你的项目的 React Native 版本的情况下使用这个库的最新版本。

React Native 密钥链

由知名开发者 Oblador 构建的 React Native Keychain 允许开发者在他们的应用中安全地存储凭据。与本文中提到的其他库不同,Keychain 内置了对 Apple 的 FaceID 的支持。

在你的项目中安装 react-native-keychain ,请运行此终端命令:

npm install react-native-keychain

下面的代码块展示了 React Native Keychain 的基本使用方法:

import { useEffect, useState } from "react";
import { View, Text } from "react-native";
import * as Keychain from "react-native-keychain";

async function saveCredentials() {
  const username = "LogRocketName";
  const password = "LogPassword";
  //save credentials to database
  await Keychain.setGenericPassword(username, password);
}

async function getCredentials() {
  //now retrieve credentials
  const credentials = await Keychain.getGenericPassword();
  return credentials;
}

export default function ReactNativeKeychain() {
  const [unencryptedData, setUnencryptedData] = useState({});

  const performEncryption = async () => {
    saveCredentials();
    const data = await getCredentials();
    setUnencryptedData(data);
  };

  useEffect(() => {
    performEncryption();
  }, []);
//finally, display user data:
  return (
    <View>
      <Text>Your username is: {unencryptedData.username}</Text>
      <Text>Password is: {unencryptedData.password}</Text>
    </View>
  );
}

image.png

React Native Keychain的优点与缺点

关于它,我喜欢的有几点:

  • 频繁更新
  • 支持生物识别技术,如面部识别或指纹识别
  • 就像Expo的SecureStore一样,它也支持访问控制。这意味着该库在特定情况下也可以解码数据。
  • 优秀的文档

然而,存在一个主要的缺陷:钥匙串只针对存储凭证,而不是键值对。

React Native 加密存储(RNES)

RNES 使用平台原生的加密工具来加密和存储敏感数据。

在你的应用中安装 react-native-encrypted-storage

npm install react-native-encrypted-storage

这段代码片段展示了库的样例使用:

import EncryptedStorage from "react-native-encrypted-storage";
//为简洁起见,部分代码已移除
const [textData, setTextData] = useState(null);

//首先,将一个项目保存到加密数据库中
async function storeUserSession() {
  try {
    await EncryptedStorage.setItem("user_name", "LogRocket");
  } catch (error) {
    console.log("发生错误! ", error);
  }
}

//现在尝试检索这个项目:
async function retrieveUserSession() {
  try {
    const session = await EncryptedStorage.getItem("user_name");
    //如果找到项目,则将其存储在状态变量中
    if (session !== undefined) {
      setTextData(session);
    }
  } catch (error) {
    console.log(error);
  }
}

const performEncryption = async () => {
  await storeUserSession();
  await retrieveUserSession();
};

//在首次装载时,保存并检索 user_name 项目:
useEffect(() => {
  performEncryption();
}, []);

return (
  <View>
    <Text>'user_name' 的加密存储值:{textData}</Text>
    <StatusBar style="auto" />
  </View>
);

image.png

RNES的优点和缺点

以下原因可能使RNES非常适合你的项目:

  • 提供了一个用于加密的异步接口
  • 一个安全的异步存储替代方案

然而,这个库的一个主要缺点是该项目不经常更新。因此,这可能会在未来引入错误和依赖性冲突。

总结

在这篇文章中,你了解了 RSA 加密、初始化向量(IV)和盐值的基础知识。我们还探讨了一些可信赖的 React Native 安全库。

在我的项目中,我使用了React Native Keychain。这是因为它功能丰富,而且有出色的社区支持。

交流

首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。


王大冶
68.1k 声望105k 粉丝