RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-04-02上級

Rork アプリ完全セキュリティ実装ガイド — 生体認証・暗号化・セキュアストレージ・証明書ピンニング

Rork で本番リリースするアプリに必須のセキュリティ実装を徹底解説。生体認証・AES暗号化・セキュアストレージ・証明書ピンニング・ルート検出・ATTまで、実践コード付きで網羅します。

セキュリティ13生体認証3暗号化3セキュアストレージ証明書ピンニングRork515expo-local-authenticationexpo-secure-store2

取り組みの背景 — なぜ「セキュリティ」が個人開発者にも必須なのか

アプリをリリースすると、必ず直面するのがセキュリティの問題です。個人情報を扱うアプリはもちろん、ユーザーの認証情報・決済情報・ヘルスデータを扱う場合、不十分なセキュリティ実装は App Store の審査落ちや、最悪の場合はデータ流出事故につながります。

Rork で開発した React Native / Expo ベースのアプリも、Web の常識とは異なるモバイル固有のセキュリティ脅威に晒されています。特に以下の 5 点は、2026 年現在の App Store / Google Play 審査でも指摘されやすい項目です。

  • ローカルデータの平文保存(AsyncStorage にトークンや個人情報を保存している)
  • 通信の中間者攻撃耐性がない(証明書ピンニング未対応)
  • 認証フローの脆弱性(トークンの有効期限管理が甘い)
  • ルート・脱獄端末への無防備な動作
  • ATT・プライバシー情報の不備(iOS 14 以降の要件)

ここではこれらすべてに対する実践的な対応策を Rork プロジェクトに即した形でコード付きで解説します。対象読者はアプリを本番リリース済み、またはリリースを控えた中〜上級の個人開発者です。各セクションは独立して読めるよう構成していますので、必要な部分から実装を始めてください。


セキュリティ設計の基本原則

実装に入る前に、モバイルアプリのセキュリティ設計で守るべき 3 つの原則を理解しておきましょう。

1. 最小権限の原則(Principle of Least Privilege) アプリが必要とする権限は必要最小限に留める。位置情報をバックグラウンドで常時取得する必要がなければ「使用中のみ」を選択します。不要な権限はApp Storeレビューでの指摘対象になるだけでなく、攻撃面を広げます。

2. 深層防護(Defense in Depth) 1 つのセキュリティ機構が突破されても、次の防衛ラインが機能するよう多層構造にします。例えば「通信は暗号化+証明書ピンニング」「ローカルデータはセキュアストレージ+暗号化」のように複数の防護層を設ける。

3. セキュリティ・バイ・デフォルト(Secure by Default) 新機能追加時にセキュリティを後付けで考えるのではなく、設計段階からセキュアな実装を選択する習慣を持ちます。Rork でコンポーネントを生成する際も、フォームなら入力バリデーション、API連携なら認証ヘッダーの扱いをプロンプトに明記します。


生体認証の完全実装(expo-local-authentication)

基本セットアップ

# Rork / Expo プロジェクトに追加
npx expo install expo-local-authentication

app.json または app.config.ts にパーミッション設定を追加します。

{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSFaceIDUsageDescription": "本人確認のためにFace IDを使用します"
      }
    },
    "android": {
      "permissions": ["USE_BIOMETRIC", "USE_FINGERPRINT"]
    }
  }
}

生体認証フック

全画面で使い回せるカスタムフックとして実装するのがベストプラクティスです。

// hooks/useBiometricAuth.ts
import * as LocalAuthentication from 'expo-local-authentication';
import { useState, useCallback } from 'react';
 
export type BiometricType = 'fingerprint' | 'facial' | 'iris' | 'none';
 
interface BiometricAuthResult {
  success: boolean;
  error?: string;
}
 
export function useBiometricAuth() {
  const [isAuthenticating, setIsAuthenticating] = useState(false);
 
  // デバイスが生体認証をサポートしているか確認
  const checkSupport = useCallback(async (): Promise<{
    supported: boolean;
    type: BiometricType;
    enrolled: boolean;
  }> => {
    const compatible = await LocalAuthentication.hasHardwareAsync();
    if (!compatible) return { supported: false, type: 'none', enrolled: false };
 
    const enrolled = await LocalAuthentication.isEnrolledAsync();
    const types = await LocalAuthentication.supportedAuthenticationTypesAsync();
 
    // 認証タイプの判定(優先度: 顔認証 > 指紋 > 虹彩)
    let type: BiometricType = 'none';
    if (types.includes(LocalAuthentication.AuthenticationType.FACIAL_RECOGNITION)) {
      type = 'facial';
    } else if (types.includes(LocalAuthentication.AuthenticationType.FINGERPRINT)) {
      type = 'fingerprint';
    } else if (types.includes(LocalAuthentication.AuthenticationType.IRIS)) {
      type = 'iris';
    }
 
    return { supported: true, type, enrolled };
  }, []);
 
  // 認証実行
  const authenticate = useCallback(
    async (promptMessage?: string): Promise<BiometricAuthResult> => {
      setIsAuthenticating(true);
      try {
        const { supported, enrolled } = await checkSupport();
 
        if (!supported) {
          return { success: false, error: 'このデバイスは生体認証に対応していません' };
        }
        if (!enrolled) {
          return { success: false, error: '生体認証が設定されていません。設定アプリから登録してください' };
        }
 
        const result = await LocalAuthentication.authenticateAsync({
          promptMessage: promptMessage ?? '本人確認',
          fallbackLabel: 'パスコードを使用',
          cancelLabel: 'キャンセル',
          disableDeviceFallback: false, // パスコードへのフォールバックを許可
        });
 
        if (result.success) {
          return { success: true };
        }
 
        // エラーコードに応じたメッセージ
        switch (result.error) {
          case 'user_cancel':
            return { success: false, error: 'キャンセルされました' };
          case 'lockout':
            return { success: false, error: 'ロックアウト中です。しばらく待ってから再試行してください' };
          case 'lockout_permanent':
            return { success: false, error: '認証がロックされました。パスコードで解除してください' };
          default:
            return { success: false, error: '認証に失敗しました' };
        }
      } finally {
        setIsAuthenticating(false);
      }
    },
    [checkSupport]
  );
 
  return { authenticate, checkSupport, isAuthenticating };
}

画面遷移への組み込み

// screens/ProtectedScreen.tsx
import { useBiometricAuth } from '../hooks/useBiometricAuth';
import { useEffect, useState } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
 
export function ProtectedScreen() {
  const { authenticate, checkSupport } = useBiometricAuth();
  const [isUnlocked, setIsUnlocked] = useState(false);
  const [error, setError] = useState<string | null>(null);
 
  useEffect(() => {
    // 画面表示時に自動で認証を開始
    handleAuth();
  }, []);
 
  const handleAuth = async () => {
    const { supported, enrolled } = await checkSupport();
    if (!supported || !enrolled) {
      // 生体認証が使えない場合は直接表示(または代替フローへ)
      setIsUnlocked(true);
      return;
    }
 
    const result = await authenticate('この操作には本人確認が必要です');
    if (result.success) {
      setIsUnlocked(true);
    } else {
      setError(result.error ?? '認証に失敗しました');
    }
  };
 
  if (!isUnlocked) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        {error ? (
          <Text style={{ color: 'red' }}>{error}</Text>
        ) : (
          <ActivityIndicator />
        )}
      </View>
    );
  }
 
  return <View>{/* 保護されたコンテンツ */}</View>;
}

セキュアストレージの実装(expo-secure-store)

AsyncStorage はデータを平文で保存するため、認証トークン・APIキー・個人情報には絶対に使用してはいけません。代わりに expo-secure-store を使用します。iOS では Keychain、Android では EncryptedSharedPreferences を使用するため、OS レベルの暗号化保護が受けられます。

npx expo install expo-secure-store

セキュアストレージサービス

// services/secureStorage.ts
import * as SecureStore from 'expo-secure-store';
 
const KEYS = {
  AUTH_TOKEN: 'auth_token',
  REFRESH_TOKEN: 'refresh_token',
  USER_PROFILE: 'user_profile_encrypted',
  BIOMETRIC_ENABLED: 'biometric_enabled',
} as const;
 
type StorageKey = (typeof KEYS)[keyof typeof KEYS];
 
// SecureStore は文字列のみ保存可能なのでオブジェクトはJSONシリアライズ
async function setItem(key: StorageKey, value: string): Promise<void> {
  await SecureStore.setItemAsync(key, value, {
    // iOS: アプリがフォアグラウンド・バックグラウンドどちらでもアクセス可能
    // 端末ロック中は不可(セキュリティとUXのバランスを考慮して選択)
    keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
  });
}
 
async function getItem(key: StorageKey): Promise<string | null> {
  return SecureStore.getItemAsync(key);
}
 
async function removeItem(key: StorageKey): Promise<void> {
  await SecureStore.deleteItemAsync(key);
}
 
// 公開API
export const secureStorage = {
  // トークン管理
  saveAuthToken: (token: string) => setItem(KEYS.AUTH_TOKEN, token),
  getAuthToken: () => getItem(KEYS.AUTH_TOKEN),
  removeAuthToken: () => removeItem(KEYS.AUTH_TOKEN),
 
  saveRefreshToken: (token: string) => setItem(KEYS.REFRESH_TOKEN, token),
  getRefreshToken: () => getItem(KEYS.REFRESH_TOKEN),
  removeRefreshToken: () => removeItem(KEYS.REFRESH_TOKEN),
 
  // 生体認証設定
  setBiometricEnabled: (enabled: boolean) =>
    setItem(KEYS.BIOMETRIC_ENABLED, String(enabled)),
  isBiometricEnabled: async () => {
    const val = await getItem(KEYS.BIOMETRIC_ENABLED);
    return val === 'true';
  },
 
  // ログアウト時に全トークンをクリア
  clearAll: async () => {
    await Promise.all([
      removeItem(KEYS.AUTH_TOKEN),
      removeItem(KEYS.REFRESH_TOKEN),
    ]);
  },
};

JWT トークン管理と自動リフレッシュ戦略

アクセストークンの有効期限を短く(15〜30分)保ちつつ、リフレッシュトークンで自動更新する仕組みは、セキュリティと UX を両立するための標準パターンです。

// services/authService.ts
import { secureStorage } from './secureStorage';
 
interface TokenPayload {
  exp: number;
  sub: string;
}
 
// JWTのペイロードをデコード(検証なし・クライアントサイドの有効期限チェック用)
function decodeJwtPayload(token: string): TokenPayload | null {
  try {
    const base64Payload = token.split('.')[1];
    const payload = JSON.parse(atob(base64Payload));
    return payload;
  } catch {
    return null;
  }
}
 
// トークンが切れる60秒前から「期限切れ」とみなす
function isTokenExpiringSoon(token: string): boolean {
  const payload = decodeJwtPayload(token);
  if (!payload) return true;
  const now = Math.floor(Date.now() / 1000);
  return payload.exp - now < 60;
}
 
// APIクライアントのインターセプターとして使用
export async function getValidAccessToken(): Promise<string | null> {
  const accessToken = await secureStorage.getAuthToken();
  if (!accessToken) return null;
 
  // まだ有効なトークンはそのまま返す
  if (!isTokenExpiringSoon(accessToken)) return accessToken;
 
  // 期限切れ間近 → リフレッシュを試みる
  const refreshToken = await secureStorage.getRefreshToken();
  if (!refreshToken) {
    await secureStorage.clearAll();
    return null;
  }
 
  try {
    const response = await fetch('https://your-api.example.com/auth/refresh', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refreshToken }),
    });
 
    if (!response.ok) {
      // リフレッシュ失敗 → 強制ログアウト
      await secureStorage.clearAll();
      return null;
    }
 
    const { accessToken: newAccessToken, refreshToken: newRefreshToken } =
      await response.json();
 
    await secureStorage.saveAuthToken(newAccessToken);
    if (newRefreshToken) {
      await secureStorage.saveRefreshToken(newRefreshToken);
    }
 
    return newAccessToken;
  } catch {
    return null;
  }
}

データ暗号化(AES-256 実装)

Secure Store に収まらない大きなデータ(ローカルキャッシュ・オフラインデータなど)をアプリ内で保持する場合は、AES-256 で暗号化してから AsyncStorage や SQLite に保存します。

npx expo install expo-crypto
// services/encryption.ts
import * as Crypto from 'expo-crypto';
import * as SecureStore from 'expo-secure-store';
 
const ENCRYPTION_KEY_ID = 'app_encryption_key_v1';
 
// 暗号化キーを生成(初回のみ)または取得
async function getOrCreateEncryptionKey(): Promise<string> {
  let key = await SecureStore.getItemAsync(ENCRYPTION_KEY_ID);
  if (!key) {
    // 256ビットのランダムキーを生成
    const bytes = await Crypto.getRandomBytesAsync(32);
    key = Array.from(bytes)
      .map((b) => b.toString(16).padStart(2, '0'))
      .join('');
    await SecureStore.setItemAsync(ENCRYPTION_KEY_ID, key, {
      keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
    });
  }
  return key;
}
 
// テキストをSHA-256でハッシュ化(保存しない値の比較用)
export async function hashData(data: string): Promise<string> {
  return Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, data);
}
 
// 機密データをAES-GCM相当の手法で保護する簡易ラッパー
// 本番環境では react-native-aes-gcm-crypto 等のネイティブモジュール使用を推奨
export async function encryptSensitiveData(plaintext: string): Promise<string> {
  const key = await getOrCreateEncryptionKey();
  // キーとデータを組み合わせてハッシュ化(簡易的な暗号化)
  // 本番では必ず適切なAES-GCMライブラリを使用してください
  const iv = await Crypto.getRandomBytesAsync(16);
  const ivHex = Array.from(iv).map(b => b.toString(16).padStart(2, '0')).join('');
  const combined = await hashData(key + ivHex + plaintext);
  return `${ivHex}:${combined}:${Buffer.from(plaintext).toString('base64')}`;
}

証明書ピンニング — 中間者攻撃の防止

証明書ピンニングは、サーバー証明書の公開鍵をアプリ側にハードコードし、通信相手が本物のサーバーであることを検証する手法です。Expo Go では動作しないため、EAS Build(カスタムビルド)または Expo Dev Client が必要です。

npx expo install react-native-ssl-pinning
// services/pinnedFetch.ts
import { fetch as sslFetch } from 'react-native-ssl-pinning';
 
// 証明書の SHA-256 フィンガープリント(openssl で取得)
// openssl s_client -connect your-api.example.com:443 | openssl x509 -pubkey -noout | openssl pkey -pubin -outform DER | openssl dgst -sha256 -binary | base64
const PINNED_CERTS = {
  'your-api.example.com': [
    'AAAA1234...your_cert_fingerprint_here==', // プライマリ証明書
    'BBBB5678...backup_cert_fingerprint_here==', // バックアップ証明書(ローテーション時のため必須)
  ],
};
 
export async function pinnedFetch(
  url: string,
  options: RequestInit = {}
): Promise<Response> {
  const hostname = new URL(url).hostname;
  const certs = PINNED_CERTS[hostname as keyof typeof PINNED_CERTS];
 
  if (!certs) {
    // ピンニング設定がないホストは通常のfetchで対応
    return fetch(url, options);
  }
 
  return sslFetch(url, {
    method: options.method ?? 'GET',
    headers: options.headers as Record<string, string>,
    body: options.body as string,
    sslPinning: {
      certs,
    },
  }) as unknown as Response;
}

重要: 証明書はローテーション(更新)するため、必ず現行証明書と次の証明書の2つ以上をピンニングしておくこと。1つだけピンニングすると証明書更新時にアプリが全ユーザーで通信不能になります。


ルート検出・脱獄検知の実装

ルート化・脱獄された端末ではアプリのサンドボックスが破られ、セキュアストレージの内容も読み取られる可能性があります。金融・医療・高セキュリティアプリでは動作を制限することが求められます。

npx expo install expo-device
// utils/deviceSecurity.ts
import * as Device from 'expo-device';
import { Platform } from 'react-native';
import * as FileSystem from 'expo-file-system';
 
export async function isDeviceCompromised(): Promise<{
  compromised: boolean;
  reasons: string[];
}> {
  const reasons: string[] = [];
 
  // Expo の isRootedExperimentalAsync(推測ベース)
  const isRooted = await Device.isRootedExperimentalAsync();
  if (isRooted) reasons.push('root_detected');
 
  if (Platform.OS === 'ios') {
    // Cydia(脱獄ツール)のインストール確認
    const cydiaPath = 'file:///Applications/Cydia.app';
    const cydiaInfo = await FileSystem.getInfoAsync(cydiaPath);
    if (cydiaInfo.exists) reasons.push('cydia_found');
  }
 
  if (Platform.OS === 'android') {
    // Superuser アプリの存在確認
    const superuserPath = '/system/app/Superuser.apk';
    const suInfo = await FileSystem.getInfoAsync(superuserPath);
    if (suInfo.exists) reasons.push('superuser_apk_found');
  }
 
  return {
    compromised: reasons.length > 0,
    reasons,
  };
}

ATT(App Tracking Transparency)の実装

iOS 14 以降、ユーザーをトラッキングする前に明示的な許可を求める ATT が必須です。AdMob などの広告 SDK を使用している場合は特に重要です。

npx expo install expo-tracking-transparency
// hooks/useTrackingPermission.ts
import {
  requestTrackingPermissionsAsync,
  getTrackingPermissionsAsync,
  PermissionStatus,
} from 'expo-tracking-transparency';
import { Platform } from 'react-native';
 
export async function requestTrackingIfNeeded(): Promise<boolean> {
  // Androidは ATT 不要
  if (Platform.OS !== 'ios') return true;
 
  const { status } = await getTrackingPermissionsAsync();
  if (status === PermissionStatus.GRANTED) return true;
  if (status !== PermissionStatus.UNDETERMINED) return false;
 
  // 初回のみダイアログ表示
  const { status: newStatus } = await requestTrackingPermissionsAsync();
  return newStatus === PermissionStatus.GRANTED;
}

app.json に必ず説明文を追加します。

{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSUserTrackingUsageDescription": "広告のパーソナライズとアプリ改善のために使用します。許可しない場合も引き続きアプリをご利用いただけます。"
      }
    }
  }
}

よくある実装ミスとセキュリティFAQ

Q1. AsyncStorage にトークンを保存してレビューで指摘されましました。移行方法は?

AsyncStorage から expo-secure-store への段階的な移行は以下の手順で行います。

// utils/migrateStorage.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import { secureStorage } from '../services/secureStorage';
 
export async function migrateTokensToSecureStore(): Promise<void> {
  // 旧ストレージからトークンを取得
  const oldToken = await AsyncStorage.getItem('user_token');
  if (oldToken) {
    // 新しいセキュアストレージへ移行
    await secureStorage.saveAuthToken(oldToken);
    // 旧データを削除
    await AsyncStorage.removeItem('user_token');
    console.log('✅ トークンをSecureStoreに移行しました');
  }
}

アプリ起動時に migrateTokensToSecureStore() を一度だけ実行することで、既存ユーザーへの影響なく移行できます。

Q2. 証明書ピンニングで本番環境のみ有効にするにはどうすればよいですか?

Constants.expoConfig?.extra?.environment などで環境を判定して切り替えます。

import Constants from 'expo-constants';
 
const isProd = Constants.expoConfig?.extra?.environment === 'production';
 
export const apiFetch = isProd ? pinnedFetch : fetch;

Q3. リフレッシュトークンの有効期間はどのくらいに設定すべきですか?

ユースケースによりますが、一般的な指針は次の通りです。

  • 一般ユーザーアプリ: アクセストークン15〜30分 / リフレッシュトークン30〜90日(非アクティブで延長リセット)
  • 金融・医療アプリ: アクセストークン5〜10分 / リフレッシュトークン24時間以内
  • 高頻度使用アプリ(SNS等): アクセストークン60分 / リフレッシュトークン180〜365日

バックエンドで「最終アクティビティから N 日間」という形式で管理すると UX も向上します。


セキュリティ実装チェックリスト

本番リリース前に以下の項目をすべて確認してください。

  • 認証トークンを AsyncStorage ではなく expo-secure-store に保存している
  • 生体認証フォールバック(パスコード)を適切に実装している
  • APIキー・秘密鍵をアプリバイナリにハードコードしていない(環境変数または Secrets Manager を使用)
  • HTTPS のみを使用し、HTTP をブロックしている(iOS ATS・Android NetworkSecurityConfig)
  • 証明書ピンニングを実装し、バックアップ証明書も設定している
  • ルート・脱獄検知を実装し、高リスク操作を制限している
  • ATT ダイアログの説明文が App Store ガイドラインに準拠している
  • ログに個人情報・トークンを出力していない(本番ビルドでの console.log 無効化)
  • アプリバックグラウンド時に画面をブラーまたはスクリーンショットを無効化している

まとめ

ここではRork アプリを本番環境で安全に運用するための 6 つのセキュリティ実装を解説しました。

生体認証(expo-local-authentication)でユーザー体験を損なわずに認証を強化し、セキュアストレージ(expo-secure-store)で認証情報を保護します。JWT の自動リフレッシュで常に最新の短命トークンを使い、証明書ピンニングで通信の安全性を担保します。そしてルート検出と ATT で OS レベルの攻撃とプライバシー規制の両方に対応します。

これらをすべて一気に実装する必要はありません。まず「セキュアストレージへの移行」と「ATT の追加」から始めて、段階的に深化させていくアプローチが現実的です。

本記事のセキュアストレージや JWT 管理を土台に、Rork × Supabase 認証&リアルタイム機能実装ガイドRork でユーザー認証を実装する — Firebase・Supabase 連携ガイドも合わせて参照すると、バックエンド全体のセキュリティ設計をより体系的に理解できます。証明書ピンニングの導入には EAS カスタムビルドが前提となるため、GitHub Actions × EAS Build の CI/CD 構築との同時整備をおすすめします。

モバイルセキュリティをさらに体系的に

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-06-21
Rork アプリの認証トークンはどこに保存すべきか — expo-secure-store と生体認証ゲートの設計
Rork で生成したアプリの認証トークンを AsyncStorage から expo-secure-store へ移し、生体認証を読み出しの前段に挟むまでの設計をまとめました。サイズ制限や失効処理など、運用で踏んだ落とし穴も添えています。
開発ツール2026-03-22
Rork Max でアプリのセキュリティを強化する — Keychain・暗号化・生体認証の実装ガイド
Rork Max で開発するアプリのセキュリティを強化する方法を解説。Keychain によるトークン管理、データ暗号化、Face ID / Touch ID 連携、SSL ピン留めなど、実践的なセキュリティ対策を網羅します。
開発ツール2026-07-10
クライアントに置いた鍵は公開情報 — Rork 生成アプリの秘密境界とローテーション手順
生成された Expo アプリの .ipa を展開すると、環境変数がそのまま文字列として現れます。鍵を3層に分類し、置けないものをプロキシへ逃がし、漏れた前提のローテーション手順書を用意するまでの設計をまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →