OAuth 2.0 и JWT — доминирующие паттерны для защиты мобильных API, но их правильное тестирование требует специализированных знаний о жизненном цикле токенов и граничных случаях безопасности. По данным OWASP API Security Top 10 2023, нарушенная аутентификация и авторизация остаются главными уязвимостями в API, а недостатки реализации JWT напрямую ответственны за громкие утечки данных. По данным исследования Auth0 (теперь Okta), 63% мобильных приложений имеют как минимум одну уязвимость аутентификации в коде обработки токенов. Для QA-инженеров тестирование OAuth/JWT означает проверку полного жизненного цикла токенов.
TL;DR: Тестирование OAuth/JWT для мобильных приложений должно охватывать: полный поток authorization code (PKCE для мобильных), валидацию access token (подпись, срок действия, audience), ротацию refresh token, применение scope, отзыв токена и атаки безопасности (algorithm confusion, none algorithm).
OAuth 2.0 Потоки для Мобильных
Authorization Code Flow с PKCE
PKCE (Proof Key for Code Exchange) предотвращает атаки перехвата кода авторизации, делая его необходимым для мобильных приложений.
// Используя библиотеку AppAuth
class OAuthManager(private val context: Context) {
fun startAuthFlow(activity: Activity) {
val authRequest = AuthorizationRequest.Builder(
serviceConfig,
CLIENT_ID,
ResponseTypeValues.CODE,
Uri.parse("myapp://oauth-callback")
)
.setCodeVerifier(CodeVerifierUtil.generateRandomCodeVerifier())
.setScope("openid profile email")
.build()
val authService = AuthorizationService(context)
val intent = authService.getAuthorizationRequestIntent(authRequest)
activity.startActivityForResult(intent, AUTH_REQUEST_CODE)
}
}
Тестирование OAuth Потока
@Test
fun testAuthorizationCodeExchange() = runTest {
val mockAuthServer = MockWebServer()
mockAuthServer.enqueue(MockResponse()
.setResponseCode(200)
.setBody("""
{
"access_token": "mock_access_token",
"refresh_token": "mock_refresh_token",
"expires_in": 3600
}
""".trimIndent())
)
val result = oauthManager.exchangeAuthCode("mock_auth_code")
assertNotNull(result.accessToken)
assertEquals("Bearer", result.tokenType)
}
“OAuth and JWT testing is where you earn your security credibility as a QA engineer. Getting token validation right — especially edge cases like expired tokens, revoked tokens, and algorithm confusion attacks — is non-trivial and critically important.” — Yuri Kan, Senior QA Lead
Тестирование JWT Токенов
class JWTValidator {
fun validate(token: String): Boolean {
val parts = token.split(".")
if (parts.size != 3) return false
val payload = decodePayload(parts[1])
if (payload.exp * 1000 < System.currentTimeMillis()) {
throw TokenExpiredException()
}
return verifySignature(parts[0], parts[1], parts[2])
}
}
@Test
fun testJWTValidation() {
val validToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
assertTrue(validator.validate(validToken))
}
Поток Обновления Токенов
class TokenManager(private val authService: AuthService) {
suspend fun getValidAccessToken(): String {
if (isTokenExpired()) {
refreshAccessToken()
}
return accessToken ?: throw NoTokenException()
}
private suspend fun refreshAccessToken() {
val response = authService.refreshToken(refreshToken!!)
accessToken = response.accessToken
expiresAt = System.currentTimeMillis() + (response.expiresIn * 1000)
}
}
@Test
fun testAutomaticTokenRefresh() = runTest {
tokenManager.setToken("expired_token", expiresAt = System.currentTimeMillis() - 1000)
val validToken = tokenManager.getValidAccessToken()
assertEquals("new_access_token", validToken)
}
Тестирование Биометрической Аутентификации
class BiometricAuthManager(private val context: FragmentActivity) {
fun authenticate(onSuccess: () -> Unit, onFailure: (String) -> Unit) {
val biometricPrompt = BiometricPrompt(
context,
executor,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
onSuccess()
}
override fun onAuthenticationFailed() {
onFailure("Аутентификация не удалась")
}
}
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Биометрический вход")
.setSubtitle("Войдите используя биометрические данные")
.setNegativeButtonText("Использовать пароль")
.build()
biometricPrompt.authenticate(promptInfo)
}
}
Безопасное Хранение Токенов
Безопасное хранение токенов так же критично, как и сетевая безопасность. В то время как certificate pinning защищает данные в транзите, правильное хранение предотвращает несанкционированный доступ в покое.
class SecureTokenStorage(private val context: Context) {
private val keyStore = KeyStore.getInstance("AndroidKeyStore")
fun saveToken(token: String) {
val cipher = cipher
val encryptedData = cipher.doFinal(token.toByteArray())
context.getSharedPreferences("auth", Context.MODE_PRIVATE)
.edit()
.putString("token", Base64.encodeToString(encryptedData, Base64.DEFAULT))
.apply()
}
fun getToken(): String? {
val prefs = context.getSharedPreferences("auth", Context.MODE_PRIVATE)
val encryptedData = prefs.getString("token", null) ?: return null
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val decryptedData = cipher.doFinal(Base64.decode(encryptedData, Base64.DEFAULT))
return String(decryptedData)
}
}
Лучшие Практики
- Всегда используйте PKCE для мобильного OAuth - Предотвращает перехват кода
- Храните токены безопасно - Используйте Android Keystore / iOS Keychain
- Реализуйте автоматическое обновление - Бесшовный пользовательский опыт
- Тестируйте сценарии истечения - Обрабатывайте крайние случаи
- Используйте биометрическую аутентификацию - Улучшенная безопасность + UX
Для полного обзора современных практик мобильного тестирования, включая интеграцию OAuth тестирования, изучите наше руководство Мобильное Тестирование 2025. Также исследуйте Мастерство Тестирования API для комплексных стратегий тестирования API.
Заключение
OAuth 2.0 и JWT тестирование для мобильных требует:
- Реализация и тестирование PKCE потока
- Управление жизненным циклом токенов
- Валидация безопасного хранения
- Интеграция биометрической аутентификации
- Обработка ошибок и крайних случаев
Правильное тестирование аутентификации обеспечивает безопасность при поддержании бесшовного пользовательского опыта через жизненные циклы приложения.
Смотрите также
- Тестирование Безопасности Мобильных Приложений - Комплексные стратегии безопасности для мобильных приложений
- Тестирование Безопасности API - Проверка уязвимостей в эндпоинтах аутентификации
- Тестирование Мобильных Платежей - Безопасность транзакций с OAuth
- Мастерство Тестирования API - Основы тестирования REST API
- Мобильное Тестирование 2025: iOS, Android и Далее - Современные тенденции мобильного тестирования
Официальные ресурсы
FAQ
Что такое PKCE и почему он необходим для мобильного OAuth?
PKCE (Proof Key for Code Exchange) предотвращает атаки перехвата authorization code в мобильных приложениях, где client secrets не могут безопасно храниться. Вместо фиксированного client secret PKCE использует динамически генерируемый code_verifier и его SHA-256 хэш. Каждая мобильная OAuth-реализация должна использовать PKCE.
Как правильно тестировать валидацию JWT?
Тестируй: действительный токен, просроченный токен (401), недействительная подпись (401), неправильный алгоритм (атака algorithm confusion), токен с алгоритмом none (должен всегда отклоняться), неправильный audience claim и отсутствующие обязательные claims.
Как тестировать применение scope OAuth?
Создавай токены с разными подмножествами scope. Пробуй получить доступ к ресурсам, требующим scope, отсутствующих в токене. Проверяй: ресурсы возвращают 403 (не 401) для действительных токенов с недостаточным scope, и что эскалация scope правильно блокируется.
Как тестировать ротацию refresh token?
Проверяй: refresh token можно использовать один раз для получения новой пары access/refresh token. Старый refresh token немедленно аннулируется после использования. Тестируй обнаружение повторного использования refresh token и истечение срока действия долгоживущих refresh tokens.
See Also
- Тестирование Certificate Pinning в Мобильных Приложениях: Валидация SSL/TLS, Защита от MITM и Ротация Pin’ов
- Requestly: Перехват и модификация HTTP запросов - HTTP перехват с Requestly: модификация заголовков, редирект URL, mock ответов,…
- Тестируйте certificate pinning: валидация SSL/TLS, защита MITM,…
- API Contract Testing для Мобильных Приложений: Pact, Spring Cloud Contract и Лучшие Практики - Contract testing для мобильных приложений: Pact, Spring Cloud…
- Стратегия Версионирования API для Мобильных Клиентов: Обратная Совместимость, Принудительные Обновления и A/B Тестирование - Версионирование API для мобильных клиентов: обратная…
- Appium 2.0: Новая Архитектура и Облачная Интеграция для Современного Mobile Testing - Изучите революционную архитектуру Appium 2.0, экосистему драйверов…
