Irbisa · Шпаргалка
Безопасность
Безопасное хранилище, certificate pinning, обфускация, OWASP Mobile Top 10
Какие ключевые практики безопасности нужны Flutter-приложению и как они ложатся на OWASP Mobile Top 10?
Как безопасно хранить токены и другие чувствительные данные на устройстве?
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; class TokenStore { TokenStore([this._storage = const FlutterSecureStorage()]); final FlutterSecureStorage _storage; String? _access; // memory only: gone when the process dies…Посмотрите на этот код входа: какие проблемы с безопасностью вы видите и как исправить каждую?
Future<void> onSignedIn(String accessToken, String refreshToken) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('access_token', accessToken); await prefs.setString('refresh_token', refreshToken); debugPrint('signed in, token=$accessToken'); final requestKey = Random().nextInt(1 << 32).toString(); // later used to sign requests await prefs.setString('request_key', requestKey);} Future<void> signOut() async { final prefs = await SharedPreferences.getInstance(); await prefs.remove('access_token');}Сторонний сервис выдал API-ключ и просит положить его в приложение. Может ли секрет жить в бинарнике и где ему место?
// right: Publishable key (compiled in, restricted server-side, safe to ship)class Config { static const mapsKey = String.fromEnvironment('MAPS_KEY'); static const apiBase = String.fromEnvironment( 'API_BASE', defaultValue: 'https://api.example.com', );…Что такое App Transport Security и Network Security Config и когда их приходится трогать во Flutter-приложении?
<!-- iOS: ios/Runner/Info.plist - plaintext for one legacy host only --><key>NSAppTransportSecurity</key><dict> <key>NSExceptionDomains</key> <dict> <key>legacy-sensor.example.com</key>…Как работает code obfuscation во Flutter и как символизировать краши после его включения?
# 1) Build a release with obfuscation + split debug infoflutter build apk --release \ --obfuscate \ --split-debug-info=build/symbols/v1_2_3 flutter build ipa --release \…Как избежать утечки чувствительных данных через логи, скриншоты и буфер обмена?
import 'package:flutter/material.dart'; // App switcher: cover the UI while inactive, since iOS has no FLAG_SECURE.// Use as MaterialApp(builder: (_, child) => PrivacyCurtain(child: child!)).// Android: window.setFlags(FLAG_SECURE, FLAG_SECURE) in MainActivity instead.class PrivacyCurtain extends StatefulWidget {…Приложение декодирует JWT, видит в claim
roleзначениеadminи показывает админскую вкладку, а сервер роль не перепроверяет. Почему пентестер записывает это в находки?// wrong: The tab is hidden; the endpoint is wide openfinal claims = JwtDecoder.decode(accessToken);if (claims['role'] == 'admin') { tabs.add(const AdminTab()); // repackaged build: force this branch, call the API}…Поле поиска отдаёт текст напрямую в
sqfliterawQuery, а загрузка сохраняет файл под именем, которое вернул API; что чините первым?import 'package:path/path.dart' as p;import 'package:sqflite/sqflite.dart'; // wrong: One apostrophe changes the statement; a crafted string changes the appFuture<List<Map<String, Object?>>> searchBad(Database db, String q, String sort) => db.rawQuery("SELECT * FROM notes WHERE title LIKE '%$q%' ORDER BY $sort");…local_authвернулtrueпосле промпта Face ID. Что это на самом деле доказало и как превратить это в реальную защиту?Какие подводные камни безопасности у использования WebView во Flutter-приложении?
import 'package:webview_flutter/webview_flutter.dart'; // right: Constrained WebView (explicit allowlist, no JS bridge unless needed)class HelpCenterPage extends StatefulWidget { const HelpCenterPage({super.key}); @override State<HelpCenterPage> createState() => _HelpCenterPageState();…Почему deep link считается недоверенным вводом и что мешает другому приложению перехватить ваши ссылки?
// go_router: one guarded entry point for every incoming link// session: a ChangeNotifier that knows whether the user is signed infinal router = GoRouter( refreshListenable: session, redirect: (context, state) { final loggedIn = session.isSignedIn;…Найдите баг: этот интерцептор должен добавлять токен только к нашему API, но токен всплывает в логах чужого сервера.
const apiBase = 'https://api.example.com'; class AuthInterceptor extends Interceptor { AuthInterceptor(this.tokens); final TokenStore tokens; @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { // only our own API gets the token if (options.uri.toString().startsWith(apiBase)) { options.headers['Authorization'] = 'Bearer ${tokens.access}'; } handler.next(options); }}Коллега хочет добавить pub-пакет с 30 лайками для работы с платежами. Как принять решение и как потом удержать дерево зависимостей безопасным?
Пользователи разлогиниваются после переезда на новый телефон, а на Android пара запусков падает с ошибкой расшифровки токена. Что произошло?
import 'package:flutter/services.dart';import 'package:flutter_secure_storage/flutter_secure_storage.dart';import 'package:shared_preferences/shared_preferences.dart'; const _storage = FlutterSecureStorage( iOptions: IOSOptions(…Что такое SSL/certificate pinning и как реализовать его во Flutter?
import 'dart:io';import 'package:dio/dio.dart';import 'package:dio/io.dart';import 'package:flutter/services.dart'; /// Trusts ONLY the CA bundled with the app: any other chain, including a…Этот клиент должен пиннить сертификат API. Остановит ли он прокси, CA которого доверен на устройстве, и что бы вы изменили?
HttpClient pinnedClient() { final client = HttpClient(); client.badCertificateCallback = (cert, host, port) { final pin = base64Encode(sha256.convert(cert.der).bytes); return pin == kApiCertPin; }; return client;}Как работает OAuth 2.0 с PKCE для мобильных приложений и почему он обязателен?
import 'dart:convert';import 'dart:math';import 'package:crypto/crypto.dart';import 'package:flutter_appauth/flutter_appauth.dart'; // What PKCE adds, by hand: a random verifier and its S256 challenge…Как спроектировать ротацию access- и refresh-токенов и как обнаружение повторного использования ловит украденный refresh-токен?
Найдите баг: после часа в фоне приложение при открытии часто разлогинивает пользователя, хотя refresh-токен ещё действителен.
void addAuth(Dio dio, TokenStore tokens) { dio.interceptors.add(InterceptorsWrapper(onError: (err, handler) async { final req = err.requestOptions; if (err.response?.statusCode != 401 || req.extra['retried'] == true) { return handler.next(err); } try { final pair = await tokens.refresh(); // POST /auth/refresh, the server rotates the pair req..extra['retried'] = true..headers['Authorization'] = 'Bearer ${pair.access}'; handler.resolve(await dio.fetch(req)); } on DioException catch (e) { handler.reject(e); } }));}Как определить рутованное или джейлбрейкнутое устройство и почему тестировщик с Frida всё равно обходит вашу проверку?
Коллега шифрует офлайн-кеш документов через AES ключом, выведенным из email пользователя, и константным IV из туториала. Что здесь сломано?
import 'dart:convert';import 'dart:math';import 'dart:typed_data';import 'package:cryptography/cryptography.dart';import 'package:flutter_secure_storage/flutter_secure_storage.dart';…Хранилище токенов зашифровано, но криминалисты достали вложения из чатов и кеш картинок с заблокированного тестового телефона. Как данные оказались доступны?
import 'dart:io';import 'package:flutter_cache_manager/flutter_cache_manager.dart';import 'package:path_provider/path_provider.dart';import 'package:webview_flutter/webview_flutter.dart'; /// Derived data belongs in Caches/tmp: not backed up, and the OS may reclaim it.…Модифицированная сборка вашего приложения открывает премиум без оплаты, хотя код покупок выглядит правильным. Где неверно проведена граница доверия?
import 'package:dio/dio.dart';import 'package:in_app_purchase/in_app_purchase.dart'; class Purchases { Purchases(this._iap, this._api, this._entitlements); final InAppPurchase _iap;…