Биометрия и безопасность
Face ID, Touch ID, отпечаток пальца — защита приложения.
Expo Local Authentication
npx expo install expo-local-authentication
Проверка доступности
import * as LocalAuthentication from 'expo-local-authentication';
const hasHardware = await LocalAuthentication.hasHardwareAsync();
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
const supportedTypes = await LocalAuthentication.supportedAuthenticationTypesAsync();
// [FINGERPRINT, FACIAL_RECOGNITION, IRIS]
Аутентификация
async function authenticate() {
const result = await LocalAuthentication.authenticateAsync({
promptMessage: 'Войдите в приложение',
cancelLabel: 'Отмена',
disableDeviceFallback: false,
fallbackLabel: 'Использовать пароль',
});
if (result.success) {
// вход разрешён
}
}
Экран блокировки
function AppLock({ children }: { children: React.ReactNode }) {
const [unlocked, setUnlocked] = useState(false);
useEffect(() => {
async function check() {
const enrolled = await LocalAuthentication.isEnrolledAsync();
if (!enrolled) {
setUnlocked(true);
return;
}
const result = await LocalAuthentication.authenticateAsync({
promptMessage: 'Войдите',
});
setUnlocked(result.success);
}
check();
}, []);
if (!unlocked) return <LockScreen onUnlock={() => setUnlocked(true)} />;
return children;
}
При возврате в приложение
import { AppState } from 'react-native';
useEffect(() => {
const sub = AppState.addEventListener('change', (state) => {
if (state === 'background') {
setUnlocked(false);
}
});
return () => sub.remove();
}, []);
SecureStore
Токены — в SecureStore, не в AsyncStorage:
import * as SecureStore from 'expo-secure-store';
await SecureStore.setItemAsync('token', token, {
keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
Опции:
WHEN_UNLOCKED— доступен только при разблокированном устройствеWHEN_UNLOCKED_THIS_DEVICE_ONLY— не синхронизируется через iCloudAFTER_FIRST_UNLOCK— доступен после первой разблокировки
Скриншоты и запись
npx expo install expo-screen-capture
import * as ScreenCapture from 'expo-screen-capture';
// Запретить скриншоты
await ScreenCapture.preventScreenCaptureAsync();
// Отследить скриншот
const sub = ScreenCapture.addScreenshotListener(() => {
console.log('сделан скриншот');
});
Используется в банковских приложениях.
Сертификаты
Приложение должно проверять SSL-сертификаты. По умолчанию React Native это делает.
Для дополнительной защиты — certificate pinning:
npx expo install expo-build-properties
В app.json:
{
"plugins": [
[
"expo-build-properties",
{
"android": {
"usesCleartextTraffic": false
}
}
]
]
}
Не хранить в коде
- API-ключи (кроме публичных)
- Пароли
- Секретные ключи
Переменные окружения EXPO_PUBLIC_* попадают в бандл. Не клади туда секреты.
Работа с API
- Только HTTPS
- Токены в
Authorization - Валидация ответов
- Таймауты
- Обработка 401
Итоги
expo-local-authenticationдля биометрииAppStateдля блокировки в фонеSecureStoreс опциямиexpo-screen-captureдля защиты- HTTPS обязательно
- Секреты — на сервере