# 🔒 راهنمای امنیتی اپلیکیشن VPN Panel Android

## نگاه کلی

این مستند شامل تمام اقدامات امنیتی پیاده‌سازی شده برای جلوگیری از هک، کرک، مود و دسترسی غیرمجاز به اپلیکیشن است.

---

## 🛡️ لایه‌های امنیتی پیاده‌سازی شده

### 1. Root Detection (تشخیص Root)
جلوگیری از اجرای اپ روی دستگاه‌های Root شده

### 2. SSL Pinning
اطمینان از ارتباط امن با سرور و جلوگیری از Man-in-the-Middle Attack

### 3. Code Obfuscation (ProGuard)
مخفی‌سازی کدهای Java/Kotlin برای جلوگیری از Reverse Engineering

### 4. Anti-Tampering
تشخیص تغییرات در APK و جلوگیری از اجرای نسخه‌های مود شده

### 5. SafetyNet/Play Integrity API
تایید یکپارچگی دستگاه و اپلیکیشن از طریق Google

### 6. Token-based Authentication
استفاده از JWT Token با Refresh Token برای احراز هویت امن

### 7. Encrypted SharedPreferences
رمزنگاری داده‌های حساس ذخیره شده در دستگاه

### 8. Network Security Config
محدودیت اتصالات شبکه و اجبار استفاده از HTTPS

### 9. Screenshot & Screen Recording Prevention
جلوگیری از Screenshot و ضبط صفحه

### 10. App Signing
امضای دیجیتال APK با کلید خصوصی

---

## 📦 فایل‌های امنیتی

### SecurityManager.java
کلاس اصلی مدیریت امنیت

```java
package com.vpnpanel.security;

import android.content.Context;
import android.os.Build;
import java.io.*;
import java.security.*;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;

public class SecurityManager {
    
    private static SecurityManager instance;
    private Context context;
    
    private SecurityManager(Context context) {
        this.context = context.getApplicationContext();
    }
    
    public static synchronized SecurityManager getInstance(Context context) {
        if (instance == null) {
            instance = new SecurityManager(context);
        }
        return instance;
    }
    
    /**
     * بررسی امنیت کامل اپلیکیشن
     * @return true اگر اپ امن باشد
     */
    public boolean isAppSecure() {
        // 1. بررسی Root
        if (isDeviceRooted()) {
            return false;
        }
        
        // 2. بررسی Tampering
        if (isAppTampered()) {
            return false;
        }
        
        // 3. بررسی Emulator
        if (isRunningOnEmulator()) {
            return false;
        }
        
        // 4. بررسی Debugger
        if (isDebuggerConnected()) {
            return false;
        }
        
        return true;
    }
    
    /**
     * تشخیص Root
     */
    public boolean isDeviceRooted() {
        return checkRootMethod1() || checkRootMethod2() || checkRootMethod3();
    }
    
    private boolean checkRootMethod1() {
        String[] paths = {
            "/system/app/Superuser.apk",
            "/sbin/su",
            "/system/bin/su",
            "/system/xbin/su",
            "/data/local/xbin/su",
            "/data/local/bin/su",
            "/system/sd/xbin/su",
            "/system/bin/failsafe/su",
            "/data/local/su",
            "/su/bin/su"
        };
        
        for (String path : paths) {
            if (new File(path).exists()) {
                return true;
            }
        }
        return false;
    }
    
    private boolean checkRootMethod2() {
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(new String[]{"which", "su"});
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            return in.readLine() != null;
        } catch (Throwable t) {
            return false;
        } finally {
            if (process != null) process.destroy();
        }
    }
    
    private boolean checkRootMethod3() {
        String[] packages = {
            "com.noshufou.android.su",
            "com.noshufou.android.su.elite",
            "eu.chainfire.supersu",
            "com.koushikdutta.superuser",
            "com.thirdparty.superuser",
            "com.yellowes.su",
            "com.topjohnwu.magisk"
        };
        
        PackageManager pm = context.getPackageManager();
        for (String packageName : packages) {
            try {
                pm.getPackageInfo(packageName, 0);
                return true;
            } catch (PackageManager.NameNotFoundException e) {
                // Package not found
            }
        }
        return false;
    }
    
    /**
     * تشخیص تغییرات در APK (Tampering)
     */
    public boolean isAppTampered() {
        try {
            PackageInfo packageInfo = context.getPackageManager()
                .getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
            
            Signature[] signatures = packageInfo.signatures;
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            
            for (Signature signature : signatures) {
                md.update(signature.toByteArray());
            }
            
            String currentSignature = bytesToHex(md.digest());
            
            // TODO: جایگزین کنید با Hash امضای واقعی APK خود
            String expectedSignature = "YOUR_ACTUAL_APK_SIGNATURE_HASH";
            
            return !currentSignature.equals(expectedSignature);
        } catch (Exception e) {
            return true; // در صورت خطا، فرض می‌کنیم Tamper شده
        }
    }
    
    /**
     * تشخیص Emulator
     */
    public boolean isRunningOnEmulator() {
        return (Build.FINGERPRINT.startsWith("generic")
            || Build.FINGERPRINT.startsWith("unknown")
            || Build.MODEL.contains("google_sdk")
            || Build.MODEL.contains("Emulator")
            || Build.MODEL.contains("Android SDK built for x86")
            || Build.MANUFACTURER.contains("Genymotion")
            || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
            || "google_sdk".equals(Build.PRODUCT));
    }
    
    /**
     * تشخیص Debugger
     */
    public boolean isDebuggerConnected() {
        return android.os.Debug.isDebuggerConnected();
    }
    
    /**
     * تبدیل Byte به Hex
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02X", b));
        }
        return result.toString();
    }
}
```

### EncryptedStorage.java
رمزنگاری داده‌های حساس

```java
package com.vpnpanel.security;

import android.content.Context;
import android.content.SharedPreferences;
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKey;
import java.io.IOException;
import java.security.GeneralSecurityException;

public class EncryptedStorage {
    
    private static EncryptedStorage instance;
    private SharedPreferences sharedPreferences;
    
    private EncryptedStorage(Context context) {
        try {
            MasterKey masterKey = new MasterKey.Builder(context)
                .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
                .build();
            
            sharedPreferences = EncryptedSharedPreferences.create(
                context,
                "vpn_panel_secure_prefs",
                masterKey,
                EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
            );
        } catch (GeneralSecurityException | IOException e) {
            throw new RuntimeException("Failed to create encrypted storage", e);
        }
    }
    
    public static synchronized EncryptedStorage getInstance(Context context) {
        if (instance == null) {
            instance = new EncryptedStorage(context);
        }
        return instance;
    }
    
    public void saveToken(String token) {
        sharedPreferences.edit().putString("auth_token", token).apply();
    }
    
    public String getToken() {
        return sharedPreferences.getString("auth_token", null);
    }
    
    public void saveRefreshToken(String refreshToken) {
        sharedPreferences.edit().putString("refresh_token", refreshToken).apply();
    }
    
    public String getRefreshToken() {
        return sharedPreferences.getString("refresh_token", null);
    }
    
    public void clearTokens() {
        sharedPreferences.edit()
            .remove("auth_token")
            .remove("refresh_token")
            .apply();
    }
}
```

### SSLPinningInterceptor.java
SSL Pinning برای امنیت شبکه

```java
package com.vpnpanel.security;

import okhttp3.CertificatePinner;
import okhttp3.OkHttpClient;

public class SSLPinningInterceptor {
    
    public static OkHttpClient createSecureClient() {
        // TODO: جایگزین کنید با SHA-256 Hash گواهی SSL سرور خود
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
            .add("your-api-domain.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
            .add("your-api-domain.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")
            .build();
        
        return new OkHttpClient.Builder()
            .certificatePinner(certificatePinner)
            .build();
    }
}
```

---

## 🔐 پیکربندی ProGuard

فایل `proguard-rules.pro`:

```proguard
# کلی
-keepattributes *Annotation*
-keepattributes Signature
-keepattributes Exceptions

# مخفی‌سازی نام کلاس‌ها و متدها
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
-optimizationpasses 5

# مخفی‌سازی کامل (Obfuscation)
-obfuscationdictionary dictionary.txt
-classobfuscationdictionary dictionary.txt
-packageobfuscationdictionary dictionary.txt

# حفظ کلاس‌های Model
-keep class com.vpnpanel.models.** { *; }

# حفظ کلاس‌های API
-keep class com.vpnpanel.api.** { *; }

# Retrofit
-keepattributes Signature
-keepattributes Exceptions
-keep class retrofit2.** { *; }
-keepclassmembers,allowobfuscation interface * {
    @retrofit2.http.* <methods>;
}

# OkHttp
-dontwarn okhttp3.**
-keep class okhttp3.** { *; }

# Gson
-keep class com.google.gson.** { *; }
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Security Classes - حفظ کلاس‌های امنیتی
-keep class com.vpnpanel.security.** { *; }

# Native Methods
-keepclasseswithmembernames class * {
    native <methods>;
}
```

---

## 🌐 Network Security Config

فایل `network_security_config.xml` در `res/xml/`:

```xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <!-- فقط HTTPS مجاز است -->
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
    
    <!-- دامنه‌های مجاز -->
    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">your-api-domain.com</domain>
        <pin-set expiration="2025-12-31">
            <pin digest="SHA-256">YOUR_SSL_CERTIFICATE_HASH_1</pin>
            <pin digest="SHA-256">YOUR_SSL_CERTIFICATE_HASH_2</pin>
        </pin-set>
    </domain-config>
</network-security-config>
```

در `AndroidManifest.xml`:

```xml
<application
    android:networkSecurityConfig="@xml/network_security_config"
    ...>
</application>
```

---

## 📱 جلوگیری از Screenshot

در `MainActivity.java`:

```java
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    
    // جلوگیری از Screenshot
    getWindow().setFlags(
        WindowManager.LayoutParams.FLAG_SECURE,
        WindowManager.LayoutParams.FLAG_SECURE
    );
    
    setContentView(R.layout.activity_main);
    
    // بررسی امنیت
    SecurityManager securityManager = SecurityManager.getInstance(this);
    if (!securityManager.isAppSecure()) {
        showSecurityWarningAndExit();
        return;
    }
    
    // ادامه ...
}

private void showSecurityWarningAndExit() {
    new AlertDialog.Builder(this)
        .setTitle("هشدار امنیتی")
        .setMessage("این اپلیکیشن روی دستگاه‌های Root شده یا Emulator قابل اجرا نیست.")
        .setCancelable(false)
        .setPositiveButton("خروج", (dialog, which) -> finish())
        .show();
}
```

---

## 🔑 JWT Authentication

### LoginActivity.java

```java
private void login(String email, String password) {
    ApiService apiService = ApiClient.getClient().create(ApiService.class);
    
    LoginRequest request = new LoginRequest(email, password);
    
    apiService.login(request).enqueue(new Callback<LoginResponse>() {
        @Override
        public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
            if (response.isSuccessful() && response.body() != null) {
                String token = response.body().getToken();
                String refreshToken = response.body().getRefreshToken();
                
                // ذخیره امن Token
                EncryptedStorage.getInstance(LoginActivity.this).saveToken(token);
                EncryptedStorage.getInstance(LoginActivity.this).saveRefreshToken(refreshToken);
                
                // انتقال به صفحه اصلی
                startActivity(new Intent(LoginActivity.this, MainActivity.class));
                finish();
            } else {
                Toast.makeText(LoginActivity.this, "ایمیل یا رمز عبور اشتباه است", Toast.LENGTH_SHORT).show();
            }
        }
        
        @Override
        public void onFailure(Call<LoginResponse> call, Throwable t) {
            Toast.makeText(LoginActivity.this, "خطا در اتصال", Toast.LENGTH_SHORT).show();
        }
    });
}
```

### AuthInterceptor.java

```java
package com.vpnpanel.api;

import com.vpnpanel.security.EncryptedStorage;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;

public class AuthInterceptor implements Interceptor {
    
    private Context context;
    
    public AuthInterceptor(Context context) {
        this.context = context;
    }
    
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        
        String token = EncryptedStorage.getInstance(context).getToken();
        
        if (token == null) {
            return chain.proceed(originalRequest);
        }
        
        Request authenticatedRequest = originalRequest.newBuilder()
            .header("Authorization", "Bearer " + token)
            .build();
        
        Response response = chain.proceed(authenticatedRequest);
        
        // اگر 401 دریافت کردیم، Token منقضی شده
        if (response.code() == 401) {
            response.close();
            
            // تلاش برای Refresh Token
            if (refreshToken()) {
                // درخواست را دوباره ارسال کن
                token = EncryptedStorage.getInstance(context).getToken();
                Request newRequest = originalRequest.newBuilder()
                    .header("Authorization", "Bearer " + token)
                    .build();
                return chain.proceed(newRequest);
            } else {
                // Refresh Token هم منقضی شده - برگرد به Login
                Intent intent = new Intent(context, LoginActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                context.startActivity(intent);
            }
        }
        
        return response;
    }
    
    private boolean refreshToken() {
        try {
            String refreshToken = EncryptedStorage.getInstance(context).getRefreshToken();
            if (refreshToken == null) {
                return false;
            }
            
            // فراخوانی API برای Refresh Token
            ApiService apiService = ApiClient.getClient().create(ApiService.class);
            Response<LoginResponse> response = apiService.refreshToken(refreshToken).execute();
            
            if (response.isSuccessful() && response.body() != null) {
                String newToken = response.body().getToken();
                String newRefreshToken = response.body().getRefreshToken();
                
                EncryptedStorage.getInstance(context).saveToken(newToken);
                EncryptedStorage.getInstance(context).saveRefreshToken(newRefreshToken);
                
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return false;
    }
}
```

---

## 📋 Checklist امنیتی قبل از Release

- [ ] تغییر `expectedSignature` در `SecurityManager` با Hash امضای واقعی
- [ ] تغییر SSL Certificate Hashes در `SSLPinningInterceptor`
- [ ] تغییر دامنه API در `network_security_config.xml`
- [ ] فعال‌سازی ProGuard در `build.gradle`
- [ ] غیرفعال‌سازی Logging در Production
- [ ] امضای APK با کلید خصوصی قوی
- [ ] تست روی دستگاه واقعی (نه Emulator)
- [ ] فعال‌سازی Play App Signing
- [ ] فعال‌سازی SafetyNet/Play Integrity API
- [ ] بررسی عدم نشت اطلاعات در Log

---

## 🚀 Build و Release

### ساخت APK Release

```bash
./gradlew assembleRelease
```

### امضای APK

```bash
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 \
  -keystore your-keystore.jks \
  app-release-unsigned.apk \
  your-key-alias
```

### Zipalign

```bash
zipalign -v 4 app-release-unsigned.apk app-release.apk
```

---

## ⚠️ نکات مهم

1. **هرگز** Token یا API Key را به صورت Hardcode در کد قرار ندهید
2. **همیشه** از HTTPS استفاده کنید
3. **هیچگاه** اطلاعات حساس را در Log ذخیره نکنید
4. **حتما** کلید امضای خصوصی را در جای امن نگه دارید
5. **قطعا** ProGuard را فعال کنید
6. **لزوما** از EncryptedSharedPreferences استفاده کنید

---

## 📞 پشتیبانی

در صورت مشاهده مشکل امنیتی، لطفاً فوراً گزارش دهید.
