اگر فقط قصد دارید درخواستهای استاندارد API را که برای اکثر توسعهدهندگان مناسب است، ایجاد کنید، میتوانید به احکام یکپارچگی رد شوید. این صفحه درخواستهای کلاسیک API را برای احکام یکپارچگی، که در Android نسخه 4.4 (سطح API 19) یا بالاتر پشتیبانی میشوند، شرح میدهد.
ملاحظات
درخواست های استاندارد و کلاسیک را مقایسه کنید
بسته به امنیت برنامه و نیازهای ضد سوء استفاده، میتوانید درخواستهای استاندارد، درخواستهای کلاسیک یا ترکیبی از این دو را انجام دهید. درخواستهای استاندارد برای همه برنامهها و بازیها مناسب هستند و میتوان از آنها برای بررسی واقعی بودن هر اقدام یا تماس سرور استفاده کرد و در عین حال محافظت در برابر قابلیت پخش مجدد و نفوذ را به Google Play واگذار کرد. درخواست های کلاسیک هزینه بیشتری دارند و شما مسئول اجرای صحیح آنها برای محافظت در برابر نفوذ و انواع خاصی از حملات هستید. درخواستهای کلاسیک باید کمتر از درخواستهای استاندارد انجام شوند، بهعنوان مثال بهعنوان یک بار گاه به گاه برای بررسی واقعی بودن یک اقدام بسیار ارزشمند یا حساس.
جدول زیر تفاوت های کلیدی بین این دو نوع درخواست را نشان می دهد:
درخواست استاندارد API | درخواست کلاسیک API | |
---|---|---|
پیش نیازها | ||
حداقل نسخه Android SDK مورد نیاز است | Android 5.0 (سطح API 21) یا بالاتر | Android 4.4 (سطح API 19) یا بالاتر |
الزامات Google Play | گوگل پلی استور و خدمات گوگل پلی | گوگل پلی استور و خدمات گوگل پلی |
جزئیات ادغام | ||
گرم کردن API مورد نیاز است | ✔️ (چند ثانیه) | ❌ |
تأخیر درخواست معمولی | چند صد میلی ثانیه | چند ثانیه |
فرکانس درخواست بالقوه | مکرر (بررسی درخواستی برای هر اقدام یا درخواست) | به ندرت (بررسی یکباره برای اقدامات بالاترین ارزش یا حساس ترین درخواست ها) |
تایم اوت ها | بیشتر گرم کردن ها زیر 10 ثانیه هستند، اما شامل تماس با سرور هستند، بنابراین مدت زمان طولانی توصیه می شود (مثلاً 1 دقیقه). درخواست های حکم در سمت مشتری اتفاق می افتد | بیشتر درخواستها کمتر از 10 ثانیه هستند، اما شامل تماس با سرور هستند، بنابراین زمان طولانی توصیه میشود (مثلاً 1 دقیقه) |
نشانه حکم صداقت | ||
شامل جزئیات دستگاه، برنامه و حساب است | ✔️ | ✔️ |
ذخیره رمز | ذخیره پنهان روی دستگاه توسط Google Play محافظت شده است | توصیه نمی شود |
رمزگشایی و تأیید رمز از طریق سرور Google Play | ✔️ | ✔️ |
تأخیر درخواست رمزگشایی معمولی از سرور به سرور | 10 ثانیه میلی ثانیه با سه و نه در دسترس بودن | 10 ثانیه میلی ثانیه با سه و نه در دسترس بودن |
رمزگشایی و تأیید رمز به صورت محلی در یک محیط سرور امن | ❌ | ✔️ |
رمزگشایی و تأیید رمز سمت مشتری | ❌ | ❌ |
صداقت حکم طراوت | برخی از حافظه پنهان و بازخوانی خودکار توسط Google Play | همه احکام در مورد هر درخواست دوباره محاسبه می شوند |
محدودیت ها | ||
درخواست برای هر برنامه در روز | 10000 به طور پیش فرض (می توان افزایش درخواست کرد) | 10000 به طور پیش فرض (می توان افزایش درخواست کرد) |
درخواست برای هر نمونه برنامه در دقیقه | گرم کردن: 5 گرم در دقیقه توکن های یکپارچگی: بدون محدودیت عمومی* | توکن های یکپارچگی: 5 عدد در دقیقه |
حفاظت | ||
در برابر دستکاری و حملات مشابه کاهش دهید | از فیلد requestHash استفاده کنید | از فیلد nonce با اتصال محتوا بر اساس داده های درخواست استفاده کنید |
در برابر تکرار و حملات مشابه کاهش دهید | کاهش خودکار توسط Google Play | از فیلد nonce با منطق سمت سرور استفاده کنید |
* همه درخواستها، از جمله موارد بدون محدودیت عمومی، مشمول محدودیتهای دفاعی غیرعمومی در مقادیر بالا هستند.
درخواست های کلاسیک را به ندرت مطرح کنید
تولید یک توکن یکپارچگی از زمان، داده و باتری استفاده میکند و هر برنامه حداکثر تعداد درخواستهای کلاسیکی دارد که میتواند در روز انجام دهد. بنابراین، تنها زمانی باید درخواستهای کلاسیک را برای بررسی بالاترین ارزش ارسال کنید یا اینکه حساسترین اقدامات واقعی هستند، زمانی که میخواهید یک تضمین اضافی برای یک درخواست استاندارد داشته باشید. شما نباید درخواست های کلاسیک برای اقدامات با فرکانس بالا یا کم ارزش ارائه دهید. هر بار که برنامه به پیشزمینه میرود و یا هر چند دقیقه یک بار در پسزمینه درخواستهای کلاسیک انجام ندهید و از تماس همزمان با تعداد زیادی دستگاه خودداری کنید. برنامهای که درخواستهای کلاسیک بیش از حد تماس برقرار میکند ممکن است برای محافظت از کاربران در برابر اجرای نادرست خنثی شود.
از ذخیره کردن احکام خودداری کنید
ذخیره یک حکم خطر حملاتی مانند exfiltration و replay را افزایش می دهد، جایی که یک حکم خوب از یک محیط غیرقابل اعتماد مجددا استفاده می شود. اگر در نظر دارید یک درخواست کلاسیک ایجاد کنید و سپس آن را برای استفاده در حافظه پنهان ذخیره کنید، به جای آن توصیه میشود یک درخواست استاندارد در صورت تقاضا انجام دهید. درخواستهای استاندارد شامل مقداری حافظه پنهان در دستگاه است، اما Google Play از تکنیکهای حفاظتی اضافی برای کاهش خطر حملات مجدد و نفوذ استفاده میکند.
از فیلد nonce برای محافظت از درخواست های کلاسیک استفاده کنید
Play Integrity API زمینهای به نام nonce
را ارائه میکند که میتواند برای محافظت بیشتر از برنامه شما در برابر حملات خاص، مانند پخش مجدد و حملات دستکاری، استفاده شود. Play Integrity API مقداری را که در این قسمت تنظیم کردهاید، در داخل پاسخ یکپارچگی امضا شده برمیگرداند. دستورالعمل نحوه تولید nonces را برای محافظت از برنامه خود در برابر حملات به دقت دنبال کنید.
درخواست های کلاسیک را با عقب نشینی نمایی دوباره امتحان کنید
شرایط محیطی، مانند اتصال ناپایدار اینترنت یا بارگذاری بیش از حد دستگاه، می تواند باعث شود که بررسی یکپارچگی دستگاه با شکست مواجه شود. این می تواند منجر به ایجاد هیچ برچسبی برای دستگاهی شود که در غیر این صورت قابل اعتماد است. برای کاهش این سناریوها، یک گزینه امتحان مجدد با عقب نشینی نمایی اضافه کنید.
نمای کلی
هنگامی که کاربر اقدامی با ارزش بالا در برنامه شما انجام می دهد که می خواهید با بررسی یکپارچگی از آن محافظت کنید، مراحل زیر را کامل کنید:
- باطن سمت سرور برنامه شما یک مقدار منحصر به فرد را به منطق سمت سرویس گیرنده تولید و ارسال می کند. مراحل باقی مانده به این منطق به عنوان "برنامه" شما اشاره می کند.
- برنامه شما از ارزش منحصربفرد و محتوای کنش با ارزش شما،
nonce
را ایجاد می کند. سپس Play Integrity API را فراخوانی میکند و درnonce
ارسال میکند. - برنامه شما یک حکم امضا شده و رمزگذاری شده از Play Integrity API دریافت می کند.
- برنامه شما حکم امضا شده و رمزگذاری شده را به باطن برنامه شما منتقل می کند.
- پشتیبان برنامه شما حکم را به یک سرور Google Play ارسال می کند. سرور Google Play حکم را رمزگشایی و تأیید می کند و نتایج را به باطن برنامه شما برمی گرداند.
- باطن برنامه شما بر اساس سیگنالهای موجود در بار توکن تصمیم میگیرد که چگونه ادامه دهد.
- پشتیبان برنامه شما نتایج تصمیم را به برنامه شما ارسال می کند.
یک nonce تولید کنید
هنگامی که از یک عملکرد در برنامه خود با Play Integrity API محافظت می کنید، می توانید از فیلد nonce
برای کاهش انواع خاصی از حملات، مانند حملات دستکاری شخص در وسط (PITM) و حملات مجدد استفاده کنید. Play Integrity API مقداری را که در این قسمت در پاسخ یکپارچگی امضا شده تنظیم کردهاید برمیگرداند.
مقدار تنظیم شده در فیلد nonce
باید به درستی قالب بندی شود:
-
String
- آدرس اینترنتی امن
- کدگذاری شده به صورت Base64 و بدون بسته بندی
- حداقل 16 کاراکتر
- حداکثر 500 کاراکتر
در زیر چند روش رایج برای استفاده از فیلد nonce
در Play Integrity API آورده شده است. برای به دست آوردن قوی ترین محافظت در برابر nonce
، می توانید روش های زیر را ترکیب کنید.
برای محافظت در برابر دستکاری یک هش درخواست اضافه کنید
میتوانید از پارامتر nonce
در یک درخواست API کلاسیک مشابه پارامتر requestHash
در یک درخواست استاندارد API برای محافظت از محتوای یک درخواست در برابر دستکاری استفاده کنید.
هنگامی که شما درخواست حکم یکپارچگی می کنید:
- خلاصه ای از تمام پارامترهای درخواست حیاتی (مثلاً SHA256 یک سریال سازی درخواست پایدار) را از اقدام کاربر یا درخواست سرور که در حال انجام است، محاسبه کنید.
- از
setNonce
برای تنظیم فیلدnonce
روی مقدار خلاصه محاسبه شده استفاده کنید.
هنگامی که یک حکم صداقت دریافت می کنید:
- رمز یکپارچگی را رمزگشایی و تأیید کنید و خلاصه را از فیلد
nonce
بدست آورید. - خلاصه درخواست را به همان روشی که در برنامه وجود دارد محاسبه کنید (مثلاً SHA256 یک سریال درخواست پایدار).
- خلاصه های سمت برنامه و سمت سرور را مقایسه کنید. اگر مطابقت نداشته باشند، درخواست قابل اعتماد نیست.
شامل مقادیر منحصر به فرد برای محافظت در برابر حملات تکراری
به منظور جلوگیری از استفاده مجدد کاربران مخرب از پاسخهای قبلی Play Integrity API، میتوانید از فیلد nonce
برای شناسایی منحصربهفرد هر پیام استفاده کنید.
هنگامی که شما درخواست حکم یکپارچگی می کنید:
- یک مقدار منحصر به فرد جهانی را به گونه ای بدست آورید که کاربران مخرب نتوانند پیش بینی کنند. به عنوان مثال، یک عدد تصادفی امن رمزنگاری شده تولید شده در سمت سرور می تواند چنین مقداری یا یک شناسه از قبل موجود باشد، مانند یک جلسه یا شناسه تراکنش. یک نوع ساده تر و کمتر ایمن، تولید یک عدد تصادفی در دستگاه است. توصیه می کنیم مقادیر 128 بیت یا بزرگتر ایجاد کنید.
- برای تنظیم فیلد
nonce
به مقدار یکتا از مرحله 1،setNonce()
فراخوانی کنید.
هنگامی که یک حکم صداقت دریافت می کنید:
- رمز یکپارچگی را رمزگشایی و تأیید کنید و مقدار منحصر به فرد را از فیلد
nonce
بدست آورید. - اگر مقدار مرحله 1 در سرور ایجاد شده است، بررسی کنید که مقدار یکتای دریافتی یکی از مقادیر تولید شده است و برای اولین بار استفاده می شود (سرور شما باید یک رکورد از مقادیر تولید شده را برای مدت زمان مناسب نگه دارد. ). اگر مقدار منحصر به فرد دریافتی قبلاً استفاده شده است یا در رکورد ظاهر نمی شود، درخواست را رد کنید
- در غیر این صورت، اگر مقدار منحصربهفرد روی دستگاه ایجاد شده است، بررسی کنید که مقدار دریافتی برای اولین بار استفاده میشود (سرور شما باید مقادیری را که قبلاً دیدهاید را برای مدت زمان مناسب ثبت کند). اگر مقدار منحصر به فرد دریافتی قبلاً استفاده شده است، درخواست را رد کنید.
هر دو محافظت در برابر حملات دستکاری و پخش مجدد را ترکیب کنید (توصیه می شود)
امکان استفاده از فیلد nonce
برای محافظت در برابر حملات دستکاری و پخش مجدد به طور همزمان وجود دارد. برای انجام این کار، مقدار منحصر به فرد را همانطور که در بالا توضیح داده شد، ایجاد کنید و آن را به عنوان بخشی از درخواست خود بگنجانید. سپس هش درخواست را محاسبه کنید و مطمئن شوید که مقدار منحصر به فرد را به عنوان بخشی از هش لحاظ کنید. پیاده سازی که هر دو رویکرد را ترکیب می کند به شرح زیر است:
هنگامی که شما درخواست حکم یکپارچگی می کنید:
- کاربر اقدام با ارزش بالا را آغاز می کند.
- یک مقدار منحصر به فرد برای این عمل همانطور که در بخش شامل مقادیر منحصر به فرد برای محافظت در برابر حملات تکراری توضیح داده شده است، بدست آورید.
- پیامی را که می خواهید محافظت کنید آماده کنید. مقدار منحصر به فرد مرحله 2 را در پیام وارد کنید.
- برنامه شما خلاصهای از پیامی را که میخواهد از آن محافظت کند، محاسبه میکند، همانطور که در بخش شامل هش درخواست برای محافظت در برابر دستکاری توضیح داده شده است. از آنجایی که پیام حاوی مقدار یکتا است، مقدار یکتا بخشی از هش است.
- از
setNonce()
برای تنظیم فیلدnonce
به خلاصه محاسبه شده از مرحله قبل استفاده کنید.
هنگامی که یک حکم صداقت دریافت می کنید:
- مقدار منحصر به فرد را از درخواست دریافت کنید
- رمز یکپارچگی را رمزگشایی و تأیید کنید و خلاصه را از فیلد
nonce
بدست آورید. - همانطور که در بخش شامل یک هش درخواست برای محافظت در برابر دستکاری توضیح داده شد، خلاصه را دوباره در سمت سرور محاسبه کنید و بررسی کنید که با خلاصه به دست آمده از توکن یکپارچگی مطابقت داشته باشد.
- همانطور که در بخش شامل مقادیر منحصر به فرد برای محافظت در برابر حملات تکراری توضیح داده شده است، اعتبار مقدار منحصر به فرد را بررسی کنید.
نمودار توالی زیر این مراحل را با یک nonce
سمت سرور نشان می دهد:
درخواست حکم صداقت
پس از ایجاد یک nonce
، میتوانید یک حکم یکپارچگی را از Google Play درخواست کنید. برای این کار مراحل زیر را انجام دهید:
- همانطور که در مثال های زیر نشان داده شده است، یک
IntegrityManager
ایجاد کنید. - یک
IntegrityTokenRequest
بسازید وnonce
از طریق متدsetNonce()
در سازنده مربوطه تامین کنید. برنامههایی که منحصراً خارج از Google Play و SDK توزیع میشوند نیز باید شماره پروژه Google Cloud خود را از طریق روشsetCloudProjectNumber()
مشخص کنند. برنامهها در Google Play به یک پروژه Cloud در Play Console مرتبط هستند و نیازی به تنظیم شماره پروژه Cloud در درخواست ندارند. از مدیر برای فراخوانی
requestIntegrityToken()
استفاده کنید کهIntegrityTokenRequest
را ارائه می کند.
کاتلین
// Receive the nonce from the secure server. val nonce: String = ... // Create an instance of a manager. val integrityManager = IntegrityManagerFactory.create(applicationContext) // Request the integrity token by providing a nonce. val integrityTokenResponse: Task<IntegrityTokenResponse> = integrityManager.requestIntegrityToken( IntegrityTokenRequest.builder() .setNonce(nonce) .build())
جاوا
import com.google.android.gms.tasks.Task; ... // Receive the nonce from the secure server. String nonce = ... // Create an instance of a manager. IntegrityManager integrityManager = IntegrityManagerFactory.create(getApplicationContext()); // Request the integrity token by providing a nonce. Task<IntegrityTokenResponse> integrityTokenResponse = integrityManager .requestIntegrityToken( IntegrityTokenRequest.builder().setNonce(nonce).build());
وحدت
IEnumerator RequestIntegrityTokenCoroutine() { // Receive the nonce from the secure server. var nonce = ... // Create an instance of a manager. var integrityManager = new IntegrityManager(); // Request the integrity token by providing a nonce. var tokenRequest = new IntegrityTokenRequest(nonce); var requestIntegrityTokenOperation = integrityManager.RequestIntegrityToken(tokenRequest); // Wait for PlayAsyncOperation to complete. yield return requestIntegrityTokenOperation; // Check the resulting error code. if (requestIntegrityTokenOperation.Error != IntegrityErrorCode.NoError) { AppendStatusLog("IntegrityAsyncOperation failed with error: " + requestIntegrityTokenOperation.Error); yield break; } // Get the response. var tokenResponse = requestIntegrityTokenOperation.GetResult(); }
بومی
/// Create an IntegrityTokenRequest opaque object. const char* nonce = RequestNonceFromServer(); IntegrityTokenRequest* request; IntegrityTokenRequest_create(&request); IntegrityTokenRequest_setNonce(request, nonce); /// Prepare an IntegrityTokenResponse opaque type pointer and call /// IntegerityManager_requestIntegrityToken(). IntegrityTokenResponse* response; IntegrityErrorCode error_code = IntegrityManager_requestIntegrityToken(request, &response); /// ... /// Proceed to polling iff error_code == INTEGRITY_NO_ERROR if (error_code != INTEGRITY_NO_ERROR) { /// Remember to call the *_destroy() functions. return; } /// ... /// Use polling to wait for the async operation to complete. /// Note, the polling shouldn't block the thread where the IntegrityManager /// is running. IntegrityResponseStatus response_status; /// Check for error codes. IntegrityErrorCode error_code = IntegrityTokenResponse_getStatus(response, &response_status); if (error_code == INTEGRITY_NO_ERROR && response_status == INTEGRITY_RESPONSE_COMPLETED) { const char* integrity_token = IntegrityTokenResponse_getToken(response); SendTokenToServer(integrity_token); } /// ... /// Remember to free up resources. IntegrityTokenRequest_destroy(request); IntegrityTokenResponse_destroy(response); IntegrityManager_destroy();
رای یکپارچگی را رمزگشایی و تأیید کنید
وقتی یک حکم یکپارچگی درخواست میکنید، Play Integrity API یک نشانه پاسخ امضا شده ارائه میکند. nonce
که در درخواست خود وارد می کنید بخشی از نشانه پاسخ می شود.
فرمت توکن
این توکن یک توکن وب JSON (JWT) است، که رمزگذاری وب JSON (JWE) از JSON Web Signature (JWS) است. اجزای JWE و JWS با استفاده از سریال سازی فشرده نمایش داده می شوند.
الگوریتمهای رمزگذاری/امضا در پیادهسازیهای مختلف JWT به خوبی پشتیبانی میشوند:
رمزگشایی و تأیید در سرورهای Google (توصیه می شود)
Play Integrity API به شما این امکان را میدهد که حکم یکپارچگی را در سرورهای Google رمزگشایی و تأیید کنید، که امنیت برنامه شما را افزایش میدهد. برای انجام این کار، این مراحل را کامل کنید:
- یک حساب سرویس در پروژه Google Cloud ایجاد کنید که به برنامه شما مرتبط است.
در سرور برنامه خود، رمز دسترسی را از اعتبار حساب سرویس خود با استفاده از محدوده
playintegrity
دریافت کنید و درخواست زیر را انجام دهید:playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \ '{ "integrity_token": "INTEGRITY_TOKEN" }'
پاسخ JSON را بخوانید.
رمزگشایی و تأیید به صورت محلی
اگر تصمیم دارید کلیدهای رمزگذاری پاسخ خود را مدیریت و دانلود کنید، میتوانید رمز بازگشتی را در محیط سرور امن خود رمزگشایی و تأیید کنید. با استفاده از روش IntegrityTokenResponse#token()
می توانید توکن برگشتی را بدست آورید.
مثال زیر نحوه رمزگشایی کلید AES و کلید EC عمومی رمزگذاری شده با DER را برای تأیید امضا از کنسول Play به کلیدهای خاص زبان (در مورد ما زبان برنامه نویسی جاوا) در پشتیبان برنامه نشان می دهد. توجه داشته باشید که کلیدها با استفاده از پرچمهای پیشفرض با پایه 64 کدگذاری میشوند.
کاتلین
// base64OfEncodedDecryptionKey is provided through Play Console. var decryptionKeyBytes: ByteArray = Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT) // Deserialized encryption (symmetric) key. var decryptionKey: SecretKey = SecretKeySpec( decryptionKeyBytes, /* offset= */ 0, AES_KEY_SIZE_BYTES, AES_KEY_TYPE ) // base64OfEncodedVerificationKey is provided through Play Console. var encodedVerificationKey: ByteArray = Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT) // Deserialized verification (public) key. var verificationKey: PublicKey = KeyFactory.getInstance(EC_KEY_TYPE) .generatePublic(X509EncodedKeySpec(encodedVerificationKey))
جاوا
// base64OfEncodedDecryptionKey is provided through Play Console. byte[] decryptionKeyBytes = Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT); // Deserialized encryption (symmetric) key. SecretKey decryptionKey = new SecretKeySpec( decryptionKeyBytes, /* offset= */ 0, AES_KEY_SIZE_BYTES, AES_KEY_TYPE); // base64OfEncodedVerificationKey is provided through Play Console. byte[] encodedVerificationKey = Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT); // Deserialized verification (public) key. PublicKey verificationKey = KeyFactory.getInstance(EC_KEY_TYPE) .generatePublic(new X509EncodedKeySpec(encodedVerificationKey));
در مرحله بعد، از این کلیدها برای رمزگشایی رمز یکپارچگی (قسمت JWE) استفاده کنید و سپس قسمت JWS تودرتو را تأیید و استخراج کنید.
کاتلین
val jwe: JsonWebEncryption = JsonWebStructure.fromCompactSerialization(integrityToken) as JsonWebEncryption jwe.setKey(decryptionKey) // This also decrypts the JWE token. val compactJws: String = jwe.getPayload() val jws: JsonWebSignature = JsonWebStructure.fromCompactSerialization(compactJws) as JsonWebSignature jws.setKey(verificationKey) // This also verifies the signature. val payload: String = jws.getPayload()
جاوا
JsonWebEncryption jwe = (JsonWebEncryption)JsonWebStructure .fromCompactSerialization(integrityToken); jwe.setKey(decryptionKey); // This also decrypts the JWE token. String compactJws = jwe.getPayload(); JsonWebSignature jws = (JsonWebSignature) JsonWebStructure.fromCompactSerialization(compactJws); jws.setKey(verificationKey); // This also verifies the signature. String payload = jws.getPayload();
محموله به دست آمده یک نشانه متن ساده است که حاوی احکام یکپارچگی است.