암호화 저장소 도입 - study-pals/frontend GitHub Wiki
refresh token을 관리하기 위해, EncryptedStorage
처럼 키-값 쌍을 암호화하여 저장할 수 있는 방법이 필요하다.
조사 결과 Android, iOS 환경에서만 동작한다.
공식 문서에 모바일 환경에서의 지원만이 명시되어 있으며, 관련 정보를 조사해본 결과 Android·iOS·Web까지만 네이티브 바인딩을 제공하고 있다.
따라서 Windows, Macos 환경을 커버할 수 없다.
react-native-async-storage
는 네 플랫폼에서 모두 동작한다. js 단에서 직접 암복호화를 구현하고, 이를 asyncStorage에 적용해보면 어떨까?
→ 암호화에 사용할 key값도 결국 어딘가에 저장해야 한다. key값을 네트워크 통신을 통해 서버에서 관리하는 게 아닌 한, 결국 key값을 위한 암호화된 저장소가 또 필요하다. 결국 문제는 원점으로 돌아간다.
Android, iOS 환경에서 암호화 저장소를 제공하며, Catalyst를 통해 macOS도 지원 가능하다.
https://mcodex.dev/react-native-sensitive-info/docs/
공식 문서 상으로는 네 가지 플랫폼을 모두 지원한다. 하지만 적용을 시도해 본 결과, Android와 Windows 둘 다에 적용 실패했다. 유지보수가 4년 전에 끊겨 더 이상 현재의 react native 아키텍처와 호환되지 않는 모양이다. 해당 레포지토리의 깃허브 이슈에도 관련하여 많은 이슈가 등록되어 있으나 해결되지 않고 있다.
플랫폼별로 암호화 저장소 로직을 분기시킨다.
- Android: keystore를 사용한다. (
react-native-keychain
으로 커버 가능) - iOS, Macos: keychain을 사용한다. (
react-native-keychain
으로 커버 가능) - Windows: 암호화 저장소 네이티브 모듈을 만들어 사용한다.
https://microsoft.github.io/react-native-windows/docs/native-platform-getting-started
대부분 공식 문서를 참고했다.
npx --yes [email protected] --react-native-version "0.78.2" rnw-secure-storage
- Library Type: Turbo module 선택
- Languages: Kotlin & Objective-C 선택
npx react-native init-windows --template cpp-lib --overwrite
// src/NativeRnwSecureStorage.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
get: (key: string) => Promise<string | null>;
set: (key: string, value: string) => Promise<void>;
delete: (key: string) => Promise<void>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('RnwSecureStorage');
codegen을 위해 package.json에 아래 config를 추가한다.
// package.json
{
"name": "rnw-secure-storage",
...
"codegenConfig": {
"name": "RnwSecureStorageSpec",
"type": "modules",
"jsSrcsDir": "src",
"includesGeneratedCode": true,
"windows": {
"namespace": "rnwSecureStorageCodegen",
"outputDirectory": "windows/rnwSecureStorage/codegen",
"separateDataTypes": true
},
...
이후 터미널에 npx react-native codegen-windows
를 실행하면 .clang-format, NativeRnwSecureStorageSpec.g.h 파일이 생성된다.
여기서 NativeRnwSecureStorageSpec.g.h는 JS와 통신할 API 정보를 가지는 구조체이다.
// NativeRnwSecureStorageSpec.g.h
struct RnwSecureStorageSpec : winrt::Microsoft::ReactNative::TurboModuleSpec {
static constexpr auto methods = std::tuple{
Method<void(std::string, Promise<std::optional<std::string>>) noexcept>{0, L"get"},
Method<void(std::string, std::string, Promise<void>) noexcept>{1, L"set"},
Method<void(std::string, Promise<void>) noexcept>{2, L"delete"},
};
rnwSecureStorage.cpp, rnwSecureStorage.h 파일에 실제 네이티브 모듈의 기능을 정의해야 한다. 이 부분은 AI의 도움을 받아 작성하였다.
Windows.Security.Credentials
API에서 제공하는 PasswordVault에 키-값 쌍을 저장하며, 내부적으로 DPAPI-AES 암호화를 사용한다.
tsc로 JS 코드를 번들링하고, rnw-secure-storage
라는 이름으로 npm에 배포하였다.
Internal error when a required entitlement isn't present.
iOS에서는 추가 설정 없이 잘 동작했으나, Macos에서 Keychain.setGenericPassword
호출 시 위 에러가 발생했다.
https://developer.apple.com/documentation/xcode/configuring-keychain-sharing
Xcode IDE 상에서 keychain sharing entitlement를 등록하여 문제를 해결하였다. 하지만 release 시에는 해당 Bundle Identifier를 사용하지 않으므로 추가로 문제가 발생할 여지가 있다. 배포 시 재점검할 필요가 있을 듯하다.