jpskill.com
🛠️ 開発・MCP コミュニティ

mobile-security

Android端末におけるセキュアなデータ保管、ネットワーク保護、入力内容の検証、認証といった、モバイルアプリケーションの安全性を高めるためのセキュリティ対策を支援するSkill。

📜 元の英語説明(参考)

Android security patterns for secure storage, network security, input validation, and authentication.

🇯🇵 日本人クリエイター向け解説

一言でいうと

Android端末におけるセキュアなデータ保管、ネットワーク保護、入力内容の検証、認証といった、モバイルアプリケーションの安全性を高めるためのセキュリティ対策を支援するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o mobile-security.zip https://jpskill.com/download/16431.zip && unzip -o mobile-security.zip && rm mobile-security.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16431.zip -OutFile "$d\mobile-security.zip"; Expand-Archive "$d\mobile-security.zip" -DestinationPath $d -Force; ri "$d\mobile-security.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して mobile-security.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → mobile-security フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

モバイルセキュリティパターン

Android のためのセキュリティのベストプラクティスです。

安全なストレージ

EncryptedSharedPreferences

// 暗号化されたプリファレンスを作成します
private fun createSecurePrefs(context: Context): SharedPreferences {
    val masterKey = MasterKey.Builder(context)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .build()

    return EncryptedSharedPreferences.create(
        context,
        "secure_prefs",
        masterKey,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
    )
}

// 使用例
class TokenStorage(context: Context) {
    private val prefs = createSecurePrefs(context)

    var accessToken: String?
        get() = prefs.getString("access_token", null)
        set(value) = prefs.edit().putString("access_token", value).apply()

    fun clear() = prefs.edit().clear().apply()
}

Android Keystore

// Keystore にキーを生成します
fun generateSecretKey(alias: String) {
    val keyGenerator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES,
        "AndroidKeyStore"
    )

    keyGenerator.init(
        KeyGenParameterSpec.Builder(alias,
            KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
            .setUserAuthenticationRequired(true)
            .setUserAuthenticationParameters(300, KeyProperties.AUTH_BIOMETRIC_STRONG)
            .build()
    )

    keyGenerator.generateKey()
}

ネットワークセキュリティ

Network Security Config

<!-- res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>

    <!-- デバッグ時のみ -->
    <debug-overrides>
        <trust-anchors>
            <certificates src="user"/>
        </trust-anchors>
    </debug-overrides>

    <!-- 証明書ピン留め -->
    <domain-config>
        <domain includeSubdomains="true">api.example.com</domain>
        <pin-set expiration="2025-12-31">
            <pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</pin>
            <pin digest="SHA-256">BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

証明書ピン留め (Ktor)

val client = HttpClient(OkHttp) {
    engine {
        config {
            certificatePinner(
                CertificatePinner.Builder()
                    .add("api.example.com", "sha256/AAAA...")
                    .add("api.example.com", "sha256/BBBB...")  // バックアップ
                    .build()
            )
        }
    }
}

安全なログ出力

// ❌ 機密データを絶対にログ出力しないでください
Log.d("Auth", "Token: $token")

// ✅ Timber を使用したリリースセーフなログ出力
class ReleaseTree : Timber.Tree() {
    override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
        if (priority >= Log.WARN) {
            // クラッシュレポートに送信します
            Crashlytics.log(priority, tag, message)
        }
    }
}

// Application 内で
if (BuildConfig.DEBUG) {
    Timber.plant(Timber.DebugTree())
} else {
    Timber.plant(ReleaseTree())
}

入力検証

// 使用前に検証します
fun validateEmail(email: String): Result<String> {
    return when {
        email.isBlank() -> Result.failure(ValidationError.Empty)
        !Patterns.EMAIL_ADDRESS.matcher(email).matches() -> 
            Result.failure(ValidationError.InvalidFormat)
        email.length > 254 -> Result.failure(ValidationError.TooLong)
        else -> Result.success(email)
    }
}

// SQL インジェクション対策 - パラメータ化されたクエリを使用します
@Query("SELECT * FROM users WHERE id = :userId")
suspend fun getUser(userId: String): User?

生体認証

val biometricPrompt = BiometricPrompt(
    activity,
    executor,
    object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            val cipher = result.cryptoObject?.cipher
            // 暗号を使用して機密データを復号化します
        }
    }
)

val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("Authenticate")
    .setNegativeButtonText("Cancel")
    .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
    .build()

biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))

ProGuard/R8 セキュリティ

# セキュリティのための R8 ルール
-keepattributes SourceFile,LineNumberTable  # クラッシュレポートのためのみ

# 機密性の高いクラスを難読化します
-repackageclasses 'a'
-allowaccessmodification

# ログ出力を削除します
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
    public static *** i(...);
}

覚えておいてください: セキュリティはオプションではありません。最初から組み込んでください。

📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Mobile Security Patterns

Security best practices for Android.

Secure Storage

EncryptedSharedPreferences

// Create encrypted preferences
private fun createSecurePrefs(context: Context): SharedPreferences {
    val masterKey = MasterKey.Builder(context)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .build()

    return EncryptedSharedPreferences.create(
        context,
        "secure_prefs",
        masterKey,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
    )
}

// Usage
class TokenStorage(context: Context) {
    private val prefs = createSecurePrefs(context)

    var accessToken: String?
        get() = prefs.getString("access_token", null)
        set(value) = prefs.edit().putString("access_token", value).apply()

    fun clear() = prefs.edit().clear().apply()
}

Android Keystore

// Generate key in Keystore
fun generateSecretKey(alias: String) {
    val keyGenerator = KeyGenerator.getInstance(
        KeyProperties.KEY_ALGORITHM_AES,
        "AndroidKeyStore"
    )

    keyGenerator.init(
        KeyGenParameterSpec.Builder(alias,
            KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
            .setUserAuthenticationRequired(true)
            .setUserAuthenticationParameters(300, KeyProperties.AUTH_BIOMETRIC_STRONG)
            .build()
    )

    keyGenerator.generateKey()
}

Network Security

Network Security Config

<!-- res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>

    <!-- Debug only -->
    <debug-overrides>
        <trust-anchors>
            <certificates src="user"/>
        </trust-anchors>
    </debug-overrides>

    <!-- Certificate pinning -->
    <domain-config>
        <domain includeSubdomains="true">api.example.com</domain>
        <pin-set expiration="2025-12-31">
            <pin digest="SHA-256">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</pin>
            <pin digest="SHA-256">BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

Certificate Pinning (Ktor)

val client = HttpClient(OkHttp) {
    engine {
        config {
            certificatePinner(
                CertificatePinner.Builder()
                    .add("api.example.com", "sha256/AAAA...")
                    .add("api.example.com", "sha256/BBBB...")  // Backup
                    .build()
            )
        }
    }
}

Safe Logging

// ❌ NEVER log sensitive data
Log.d("Auth", "Token: $token")

// ✅ Release-safe logging with Timber
class ReleaseTree : Timber.Tree() {
    override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
        if (priority >= Log.WARN) {
            // Send to crash reporting
            Crashlytics.log(priority, tag, message)
        }
    }
}

// In Application
if (BuildConfig.DEBUG) {
    Timber.plant(Timber.DebugTree())
} else {
    Timber.plant(ReleaseTree())
}

Input Validation

// Validate before use
fun validateEmail(email: String): Result<String> {
    return when {
        email.isBlank() -> Result.failure(ValidationError.Empty)
        !Patterns.EMAIL_ADDRESS.matcher(email).matches() -> 
            Result.failure(ValidationError.InvalidFormat)
        email.length > 254 -> Result.failure(ValidationError.TooLong)
        else -> Result.success(email)
    }
}

// SQL injection prevention - use parameterized queries
@Query("SELECT * FROM users WHERE id = :userId")
suspend fun getUser(userId: String): User?

Biometric Authentication

val biometricPrompt = BiometricPrompt(
    activity,
    executor,
    object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
            val cipher = result.cryptoObject?.cipher
            // Use cipher to decrypt sensitive data
        }
    }
)

val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("Authenticate")
    .setNegativeButtonText("Cancel")
    .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG)
    .build()

biometricPrompt.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))

ProGuard/R8 Security

# R8 rules for security
-keepattributes SourceFile,LineNumberTable  # For crash reports only

# Obfuscate sensitive classes
-repackageclasses 'a'
-allowaccessmodification

# Remove logging
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
    public static *** i(...);
}

Remember: Security is not optional. Build it in from the start.