Как да защитя своя API, който е създаден с помощта на Google Cloud Endpoints?

API е бекенд към мобилно приложение. Нямам нужда от удостоверяване на потребителя. Просто имам нужда от начин да осигуря достъп до този API. В момента бекендът ми е разкрит.

документацията изглежда говори само за удостоверяване и упълномощаване на потребителите, което не е от какво имам нужда тук. Просто трябва да се уверя, че само моето мобилно приложение може да общува с този бекенд и никой друг.


person mixmasteralan    schedule 03.06.2013    source източник
comment
Всъщност вие говорите за удостоверяване и оторизация. Как бихте предложили да защитите крайна точка само за вашето приложение без някаква форма на удостоверяване (базирана на приложение или на краен потребител). Крайните точки са просто уеб услуги, които се придържат към конкретен публичен API.   -  person Tim Hoffman    schedule 03.06.2013
comment
Един примерен начин за това са акаунтите за услуги: developers.google.com/accounts/docs/OAuth2ServiceAccount Това обаче е само за API на Google. Документацията е само за клиента. Това, от което се нуждая, е документация за това как да осигуря функционалност на акаунта на услугата от страната на сървъра.   -  person mixmasteralan    schedule 03.06.2013
comment
Друг начин е да използвате собствената си хеш стойност между вашето приложение и крайната точка stackoverflow.com/questions/2447470/   -  person Ton    schedule 03.12.2013


Отговори (3)


Да, можете да направите това: използвайте удостоверяване, за да защитите вашите крайни точки, без да извършвате удостоверяване на потребителя.

Открих, че този начин на правене не е добре документиран и всъщност не съм го правил сам, но възнамерявам да го направя, така че обърнах внимание, когато видях, че се обсъжда в някои от видеоклиповете на IO13 (мисля, че това е мястото, където видях го):

Ето моето разбиране за това:

  • Създайте проект на Google API (въпреки че това всъщност не включва техните API, освен самото удостоверяване).
  • Създайте OATH клиентски идентификатори, които са свързани с вашето приложение чрез името на неговия пакет и пръстовия отпечатък SHA1 на сертификата, с който ще подпишете приложението.

Ще добавите тези клиентски идентификатори към списъка с приемливи идентификатори за вашите крайни точки. Ще добавите потребителския параметър към вашите крайни точки, но той ще бъде нулев, тъй като не е посочен потребител.

@ApiMethod(
   name = "sendInfo",
   clientIds = { Config.WEB_CLIENT_ID, Config.MY_APP_CLIENT_ID, Config.MY_DEBUG_CLIENT_ID },
   audiences = { Config.WEB_CLIENT_ID } 
   // Yes, you specify a 'web' ID even if this isn't a Web client.
)
public void sendInfo(User user, Info greeting) {

Тук има прилична документация за горното: https://developers.google.com/appengine/docs/java/endpoints/auth

Вашето клиентско приложение ще посочи тези клиентски идентификационни номера, когато формулира извикването на услугата за крайна точка. Всички подробности за OATH ще бъдат обработени зад кулисите на вашето клиентско устройство, така че вашите клиентски идентификатори да бъдат преведени в токени за удостоверяване.

HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleAccountCredential credential = GoogleAccountCredential.usingAudience( ctx, Config.WEB_CLIENT_ID );
//credential.setSelectedAccountName( user );  // not specify a user
Myendpoint.Builder builder = new Myendpoint.Builder( transport, jsonFactory, credential );  

Този клиентски код е само най-доброто ми предположение - съжалявам. Ако някой друг има справка за това как точно трябва да изглежда клиентският код, тогава и аз ще се интересувам.

person Tom    schedule 03.06.2013
comment
Не можете да не посочите потребител в credential.setSelectedAccountName(); - отнема задължителен низов аргумент... - person Micro; 17.08.2015
comment
Опитвали ли сте това? Надявам се, че сте написали този отговор, след като сте опитали. - person Bharathi; 31.03.2016
comment
Том: Това работело ли е за теб? @MicroR: Искате да кажете, че ще вземе празен низ? - person sam; 03.03.2017
comment
@sam, съжалявам, но не помня - вече не се занимавам с тази област. - person Tom; 03.03.2017
comment
@Tom Благодаря ти. - person sam; 04.03.2017

Съжалявам, че трябва да кажа, че Google не предлага решение за вашия проблем (което е и мой проблем). Можете да използвате техния механизъм за API ключ (вижте https://developers.google.com/console/help/new/#usingkeys), но има огромна дупка в тази стратегия, предоставена от собствения API Explorer на Google (вижте https://developers.google.com/apis-explorer/#p/), което е страхотен инструмент за разработка за тестване на API, но разкрива всички API на крайни точки на облак , а не само API на услугите на Google. Това означава, че всеки с името на вашия проект може да преглежда и извиква вашия API в свободното си време, тъй като API Explorer заобикаля сигурността на API ключа. Намерих заобиколно решение (въз основа на страхотния отговор на bossylobster на тази публикация: Simple Access API (Developer Key) with Google Cloud Endpoint (Python) ), който трябва да подаде поле за заявка, което не е част от дефиницията на заявка за съобщение в API на вашия клиент, и след това да го прочете във вашия API сървър. Ако не намерите недокументираното поле, вие предизвиквате неразрешено изключение. Това ще запуши дупката, създадена от API Explorer. В iOS (което използвам за приложението си) добавяте свойство към всеки клас на заявка (тези, създадени от инструмента за генериране на API на Google) по следния начин:

@property (copy) NSString *hiddenProperty;

и задайте стойността му на ключ, който изберете. Във вашия сървърен код (python в моя случай) проверявате за неговото съществуване и barf, ако не го виждате или не е зададен на стойността, за която вашият сървър и клиент ще се съгласят:

mykey,keytype = request.get_unrecognized_field_info('hiddenProperty')
        if mykey != 'my_supersecret_key':
            raise endpoints.UnauthorizedException('No, you dont!')

Надяваме се, че това ви поставя на прав път

person Carlos Guzman    schedule 01.10.2014
comment
Но ако някой декомпилира вашия Android APK, не може ли просто да види полето за заявка за обикновен текст и след това да създаде друго приложение за Android и да играе с вашия API? В javascript би било още по-лесно, тъй като можете просто да видите източника. - person Micro; 17.08.2015
comment
Не съм запознат с APK или колко лесно е да се декомпилира. В моя случай, приложение за iOS, е доста трудно да се опитате да стигнете до кода, който прави това. - person Carlos Guzman; 19.08.2015
comment
@MicroR От страна на клиента може да се създаде квази сложен метод, който накрая генерира някаква, например проста дълга стойност, изпратена до сървъра. След като бъде обфусциран, дори декомпилиран, той ще изглежда като безсмислен набор от аритметични действия. И от страна на сървъра проверете дали получената стойност след предварително дефиниран набор от други действия е разделена на, да речем, 17. - person yurin; 15.03.2016
comment
@MicroR с удоволствие: функция c(){var a,b=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],f =0,d=0,g=0,e=0;b[0]=6;b[1]=0;b[2]=1;b[3]=1;a=4;за(e =16;a‹e-1;)b[a++]=Math.floor(10*Math.random())%10;g=(e+1)%2;for(a=0;a‹e- 1;a++)(a+g)%2?(d=2*b[a],9‹d&&(d-=9),f+=d):f+=b[a];b[e-1] =(10-f%10)%10;d=b.join();return d=d.substr(0,e)}; - person yurin; 15.03.2016

Документацията е само за клиента. Това, от което се нуждая, е документация за това как да осигуря функционалност на акаунта на услугата от страната на сървъра.

Това може да означава няколко различни неща, но бих искал да обърна внимание на това, което според мен задава въпросът. Ако искате само вашият акаунт за услуга да има достъп до вашата услуга, тогава можете просто да добавите clientId на акаунта за услуга към вашите @Api/@ApiMethod анотации, да създадете GoogleCredential и да извикате услугата си, както обикновено. Конкретно...

В конзолата за разработчици на Google създайте нов акаунт за услуга. Това ще създаде .p12 файл, който се изтегля автоматично. Това се използва от клиента в документацията, към която сте се свързали. Ако не можете да защитите .p12, тогава това не е много по-сигурно от парола. Предполагам, че това е причината това да не е изрично посочено в документацията на Cloud Endpoints.

Добавяте ИД на КЛИЕНТ, показан в конзолата за разработчици на google, към клиентските идентификатори във вашата анотация @Api или @ApiMethod

import com.google.appengine.api.users.User

@ApiMethod(name = "doIt", scopes = { Constants.EMAIL_SCOPE }, 
     clientIds = { "12345678901-12acg1ez8lf51spfl06lznd1dsasdfj.apps.googleusercontent.com" })
public void doIt(User user){ //by convention, add User parameter to existing params
    // if no client id is passed or the oauth2 token doesn't 
    // match your clientId then user will be null and the dev server 
    // will print a warning message like this:
    // WARNING: getCurrentUser: clientId 1234654321.apps.googleusercontent.com not allowed
    //..
}

Създавате клиент по същия начин, както бихте направили с незащитената версия, като единствената разлика е, че създавате обект на GoogleCredential, който да прехвърлите към MyService.Builder на вашата услуга.

HttpTransport httpTransport = new NetHttpTransport(); // or build AndroidHttpClient on Android however you wish
JsonFactory jsonFactory = new JacksonFactory();

// assuming you put the .p12 for your service acccount 
// (from the developer's console) on the classpath; 
// when you deploy you'll have to figure out where you are really
// going to put this and load it in the appropriate manner 
URL url = getClass().class.getResource("/YOURAPP-b12345677654.p12");
File p12file = new File(url.toURI());

GoogleCredential.Builder credentialBuilder = new GoogleCredential.Builder();
credentialBuilder.setTransport(httpTransport);
credentialBuilder.setJsonFactory(jsonFactory);
//NOTE: use service account EMAIL (not client id)
credentialBuilder.setServiceAccountId("12345678901-12acg1ez8lf51spfl06lznd1dsasdfj@developer.gserviceaccount.com");    credentialBuilder.setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/userinfo.email"));
credentialBuilder.setServiceAccountPrivateKeyFromP12File(p12file);
GoogleCredential credential = credentialBuilder.build();

Сега извикайте генерирания си клиент по същия начин, по който бихте направили незащитената версия, с изключение на това, че създателят взема нашите идентификационни данни на Google от по-горе като последен аргумент

MyService.Builder builder = new MyService.Builder(httpTransport, jsonFactory, credential);
builder.setApplicationName("APP NAME");
builder.setRootUrl("http://localhost:8080/_ah/api");

final MyService service = builder.build();
// invoke service same as unsecured version
person Tom    schedule 01.11.2014
comment
Том, опитвам това на dev, но подаваният потребител винаги е null, въпреки че упълномощаването се извършва с помощта на моя имейл идентификатор, който се използва в проекта GAE. Клиентският идентификатор, който предоставих в @Apimethod, не трябва да бъде грешно, тъй като успешно го използвам за достъп до облачно хранилище на Google. Работи ли ви това? - person maya; 02.06.2015
comment
това не работи Опитах за уеб клиент. Но мога да се обадя от всеки клиент. Може би трябва да блокира клиенти, различни от моя клиентски идентификатор. Но не става. - person Bharathi; 31.03.2016