2012-11-06 22 views
5

Ho due app su Google App Engine, entrambe in esecuzione con lo stesso account e una che richiama servizi forniti dall'altro su HTTPS. Qual è il modo consigliato per garantire che solo la prima app possa richiamare la seconda?Google App Engine: comunicazione inter-app sicura

In alternativa, esiste un modo per specificare che un dato endpoint può essere richiamato solo da un'app in esecuzione con lo stesso account GAE?

risposta

3

Chiedi all'applicazione di verificare l'intestazione "X-Appengine-Inbound-Appid" e accertarsi che l'ID dell'app sia corretto. Questa intestazione esiste solo se la richiesta è stata presentata da un'altra app di Google App Engine e non può essere modificata dagli utenti.

Se si sta utilizzando Python, si potrebbe procedere come segue:

import webapp2 

AUTHORIZED_APPS = ('my-first-app', 'my-other-app') 

class MyHandler(webapp2.RequestHandler): 
    def dispatch(self): 
     app_id = self.request.headers.get('X-Appengine-Inbound-Appid', None) 

     if app_id in AUTHORIZED_APPS: 
      super(MyHandler, self).dispatch() 
     else: 
      self.abort(403) 

che aumenterà a 403 per qualsiasi richiesta che non ha un X-AppEngine-Inbound-AppID nella sua intestazione.

Inoltre, quando si effettuano richieste da un'applicazione a un'altra utilizzando urlfetch, assicurarsi di impostare follow_redirects = False o l'intestazione non viene aggiunta.

+0

Non è possibile che qualcuno che conosce gli ID app delle app autorizzate li impersonino aggiungendo l'intestazione manualmente? –

+0

No. "Questa intestazione non può essere falsificata da altre app o servizi esterni: è sterilizzata dal sistema App Engine" http://stackoverflow.com/a/7961397/789417 –

+0

Il codice sopra riportato non funzionerà in realtà se il L'intestazione X-Appengine-Inbound-Appid non è presente. Se l'intestazione non è presente, app_id sarà None. Controllando se None è in una tupla, verrà generato un errore TypeError. Ciò che in realtà desideri è che AUTHORIZED_APPS sia un elenco. Controllare se None è in una lista è una cosa valida da fare in Python. – timbo

1

Il modo più semplice (e abbastanza buono in questo caso) sarebbe quello di includere un segreto in un'intestazione HTTP e controllarlo sul lato di servizio.

+0

Grazie, che in realtà è quello che sto facendo in questo momento. Mi chiedo se ci sia un modo specifico di gestione di App Engine che sfrutti il ​​fatto che le app vengano eseguite con lo stesso account. –

2

È possibile controllare il servizio Application Identity di App Engine.

+0

Grazie; questo è probabilmente più complesso di quello che mi serve, ma è buono a sapersi. –

2

Come altri hanno fatto notare, facendo leva sulla intestazione X-AppEngine-Inbound-AppID viene compilato è la soluzione più semplice. Recentemente ho avuto un problema simile, ma non ho potuto utilizzare X-Appengine-Inbound-Appid perché URLFetch non era disponibile (Node.js GAE). Ecco come risolvere il problema utilizzando gli account di servizio autenticati tramite OAuth.

Sul lato mittente si desidera impostare un account di servizio: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#creatinganaccount

allora in quel app che sarà necessario per ottenere le credenziali dell'account di servizio: https://developers.google.com/identity/protocols/application-default-credentials

Quindi è possibile utilizzare le credenziali per fare un authClient, che è possibile utilizzare per inviare la richiesta. Dovrai aggiungere un ambito OAuth al tuo authClient. Il più logico da usare è https://www.googleapis.com/auth/userinfo.email. Ciò consentirà al destinatario di ottenere l'indirizzo email dell'account di servizio del mittente. https://developers.google.com/identity/protocols/googlescopes

Qui è il codice per farlo funzionare in (mittente) Node.JS:

process.env.GOOGLE_APPLICATION_CREDENTIALS = <PATH TO CREDENTIALS FILE> 

google.auth.getApplicationDefault((err, authClient) => { 
    if (err) { 
    console.log("Failed to get default credentials: ", String(err)); 
    return; 
    } 

    if (authClient.createScopedRequired && authClient.createScopedRequired()) { 
    authClient = authClient.createScoped([ 
     'https://www.googleapis.com/auth/userinfo.email' 
    ]); 
    } 

    auth_client.request({ 
    url: <RECEIVER URL>, 
    method: "GET" 
    }, (error, result, response) => { 
    // Process response 
    }); 
}); 

Poi sul lato del ricevitore è necessario verificare l'indirizzo e-mail corrisponde l'indirizzo e-mail del account di servizio del mittente. Quando il motore dell'app viene richiamato localmente, la richiesta OAuth non viene autenticata correttamente, quindi se vuoi testare localmente dovrai eseguire un recupero URL per verificare tu stesso la richiesta.

Ricevitore Python:

scope = "https://www.googleapis.com/auth/userinfo.email" 
allowed_users = set([ 
    "<SENDER SERVICE ACCOUNT EMAIL>" 
]) 
IS_DEV = os.environ["SERVER_SOFTWARE"][:3] == "Dev" 

class MyHandler(webapp2.RequestHandler): 
    def get(self, clientId): 
     user = self.get_current_user() 
     if user in allowed_users: 
      # Do whatever you wanted 

    def get_current_user(self): 
     if IS_DEV: 
      token = self.request.headers.get("Authorization")[len("Bearer "):] 
      response = urlfetch.fetch(
       "https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=%s" % token 
      ) 
      return json.loads(response.content)["email"] 
     else: 
      return oauth.get_current_user(scope)