2013-03-12 14 views
12

Un caso di utilizzo comune per WebAPI è il rendering delle viste della shell da parte dei controller MVC, che contengono javascript che quindi raggiunge l'API per accedere ai dati.Come rendere le azioni WebAPI accessibili solo dalla mia app?

Ma supponiamo che tu abbia alcune costose operazioni API e non vuoi che le persone accedano da remoto a questi endpoint: desideri solo che le tue visualizzazioni MVC, fornite dalla tua applicazione, accedano a esse. Come puoi andare a proteggerli?

In questo caso, Request.IsLocal non funziona, perché javascript lo sta invocando dal browser del client sul proprio computer. Anche se funzionasse, è necessario scavare per ottenere il vero HttpContext per trovare questa proprietà - e quella soluzione non funzionerebbe in WebAPI self-hosted.

Per gli endpoint API che richiedono uno IPrincipal valido, è possibile proteggerli con l'attributo [Authorize]. Ma per quanto riguarda gli endpoint API a cui vuoi che l'app sia in grado di accedere per gli utenti anonimi?

Ho provato una soluzione e la pubblicherò separatamente come risposta, perché non sono sicuro che sia l'approccio migliore (o addirittura buono).

risposta

2

Se il sito MVC utilizza l'autenticazione, è possibile abilitare l'autenticazione dei moduli per i metodi dell'API Web. È possibile scrivere un attributo [Authorize] personalizzato che verificherà la presenza di un cookie di autenticazione moduli che verrà inviato dalla chiamata AJAX e, se presente, costruisce l'entità.

Un'altra possibile soluzione è proteggere l'API con tokens che è uno stile più RESTful. L'idea è che quando un utente si autentica sul tuo sito MVC puoi generare e passare un token alla vista che verrà usata quando invierai la richiesta AJAX all'API Web che a sua volta verificherà la validità del token e la sua firma.

Se invece il tuo sito non utilizza l'autenticazione, le cose si complicheranno molto perché non hai modo di sapere se la richiesta proviene da un client fidato poiché stai usando javascript per chiamare i tuoi metodi API.

+0

E se volessi che l'azione fosse accessibile agli utenti anonimi? – danludwig

+0

@danludwig - limitare l'accesso a una risorsa costosa e fornire un accesso illimitato ad esso sono requisiti in conflitto. Hai bisogno di prendere una decisione. –

+0

@danludwig, quindi le cose stanno andando fuori controllo. Semplicemente non puoi farlo. Con la definizione stessa di un utente anonimo, significa che può chiamare qualsiasi endpoint. E anche se chiami l'API Web solo dai tuoi controller e non da javascript, l'utente anonimo potrebbe comunque chiamare indirettamente il controller. –

2

Prima di andare a parlare di "cosa hai provato", ecco cosa ho provato. Funziona. Non so se c'è un modo migliore.

  1. Creare un filtro azione MVC e aggiungerlo come un filtro globale durante Application_Start.

  2. Creare un filtro di azione Http (WebAPI) e utilizzarlo per azioni che dovrebbero rifiutare le richieste remote.

Il filtro globale MVC fa questo:

  1. cerca un cookie specifico nella richiesta. Se il cookie è lì, il suo valore è decifrato. Il valore decrittografato dovrebbe essere una rappresentazione in formato stringa di DateTime, quindi utilizzare DateTime.TryParse per estrarlo. Se il valore viene analizzato correttamente su un DateTime e che DateTime è meno di un giorno, ARRESTARE QUI e non fare nient'altro.

  2. Se il cookie non è presente, o non può essere decodificato/analizzato, o è più vecchio di un giorno, scrivere un nuovo cookie nel browser. Utilizzare l'attuale DateTime.UtcNow.ToString() come valore, crittografarlo e scriverlo con HttpOnly = false.

Il filtro WebAPI fa questo:

  1. cerca un cookie specifico nella richiesta. Se il cookie è presente, decodificarne il valore e provare a analizzarlo come DateTime.

  2. Se il valore è un valore valido per DateTime ed è inferiore a 2 giorni, ARRESTARE QUI e non fare nient'altro.

  3. In caso contrario, lanciare un'eccezione Proibita 403.

Un paio di note sulla mia attuale implementazione di questo. Prima di tutto, io uso la crittografia AES con un segreto condiviso e un sale. Il segreto condiviso viene memorizzato come appSetting in web.config. Per il sale, ho abilitato l'identificazione anonima e ho usato Request.AnonymousID come sale. Non mi piace molto il sale perché è più difficile da ottenere in un controller WebAPI, ma non impossibile a condizione che non sia auto-ospitato.

+2

Ma con questo approccio, nulla impedisce al client di afferrare il cookie e chiamare qualsiasi metodo sulla propria API che desidera. Non esiste un metodo affidabile per impedire l'accesso alla tua API Web da parte di utenti anonimi e, allo stesso tempo, consentire loro di utilizzare il tuo sito Web e il tuo sito Web indirettamente chiamando l'API Web. –

+0

@DarinDimitrov, questo è vero. Nota che questo non è per proteggere l'intera API, solo alcuni endpoint che non richiedono un 'IPrincipal'. Inoltre, l'utente dovrebbe prendere 2 cookie, incluso il .ASPXANONYMOUS, poiché viene utilizzato come sale di crittografia. Ma questo rende più difficile. Leggerò il tuo link dei token, grazie per quello. – danludwig

+1

Solo per riferimento, questo è chiamato un attacco di riproduzione, ad esempio l'utente malintenzionato è in grado di inviare nuovamente la richiesta. La metodologia di @ danludwig protegge un po 'rendendo la validità della richiesta "scaduta" dopo un giorno. Tuttavia, un giorno è ancora molto tempo; 15 minuti sono più tipici. In genere si vorrà anche crittografare il corpo della richiesta (cioè i campi pubblicati). Ciò impedisce la modifica dei dati, rendendo il tipo di riproduzione inutile per la maggior parte delle funzioni. –

Problemi correlati