REST kimlik doğrulaması ve API anahtarını açığa çıkarma


96

REST hakkında bir şeyler okudum ve SO hakkında ve diğer birçok site ve blog hakkında pek çok soru var. Bu sorunun sorulduğunu hiç görmemiş olsam da ... bazı nedenlerden dolayı, aklımı bu konsepte ayıramıyorum ...

Bir RESTful API oluşturuyorsam ve güvenliğini sağlamak istersem, gördüğüm yöntemlerden biri güvenlik belirteci kullanmaktır. Diğer API'leri kullandığımda, bir belirteç oldu ve paylaşılan bir sır ... mantıklı. Anlamadığım şey, bir dinlenme hizmeti işleminin javascript (XHR / Ajax) üzerinden yapılıyor olması, birinin bunu FireBug gibi basit bir şeyle (veya tarayıcıdaki "kaynağı görüntüle") koklamasını engelleyen şey ve API anahtarını kopyalamak ve ardından anahtarı ve sırrı kullanarak bu kişinin kimliğine bürünmek?


Gördüğüm yöntemlerden biri bir güvenlik belirteci kullanmak , orada gerçekten pek çok yöntem var. Somut bir örnek var mı? "REST" ile "yalnızca kayıtlı kullanıcılar için bir javascript API'si sunma" (ör. Google maps) ile kafanızın karıştığını düşünebilirim.
PeterMmm

1
Neredeyse 2 yıl önce sorduğunuzdan beri: Sonunda kendinizi ne kullandınız?
Arjan

Aslında hiçbir şey kullanmadım, daha çok kavramları oluştururken kafamı sarmaya çalışıyordum. PeterMmm'nin yukarıdaki yorumu muhtemelen doğrudur ... hala bunların hiçbirini uygulamaya ihtiyacım olmadı, ancak kendimi daha iyi hale getirmek istedim ... takip ettiğiniz için teşekkürler.
2013

Yanıtlar:


22

api sırrı açıkça iletilmez, gizli, geçerli isteğin bir işaretini oluşturmak için kullanılır , sunucu tarafında, sunucu aynı işlemi izleyerek işareti oluşturur, eğer iki işaret eşleşirse, istek başarıyla doğrulanır - yani yalnızca işareti sır değil, istek üzerinden geçirilir.


10
Öyleyse, sadece geçilen işaretse ... hala javascript'te gösterilmiyor ... yani web sayfama API'leri (javascript tarafından çağrılır) aracılığıyla titreyen bir fotoğraf koyarsam ve sayfamı ziyaret ederseniz, aren ' API anahtarımı sayfamı ziyaret eden herhangi birine ifşa ediyorum?
tjans

6
Sorumu doğru sorduğumu sanmıyorum ... muhtemelen aradığımı bulamamamın nedenlerinden biri. ajax çağrımı yaptığımda, diyelim ki jquery kullanarak, api anahtarını ajax çağrısına yerleştirmem gerekir, böylece sunucuya iletilir ... bu noktada birisi API anahtarını görebilir. Bunu yanlış anlıyorsam, istemci komut dosyasına gömülü değilse API anahtarı istekle birlikte nasıl gönderilir?
tjans

4
Sonuç olarak: insanlara openapi / restapi kullanmadan önce bir apikey + apisecret çifti atanacak, sunucunun isteği kimin yaptığını bildiğinden emin olmak için apikey + işareti sunucu tarafına aktarılacak, apisecret güvenlik için asla sunucu tarafına aktarılmayacak .
James.Xu

8
Dolayısıyla @ James.Xu'nun 'sır, mevcut isteğin bir işaretini oluşturmak için kullanılır' ifadesi YANLIŞ! İstemci sırrı bilmediğinden, ona göndermek güvenli olmayacağından (ve bunu başka nasıl bilebilirdi?) Teknik olarak 'özel anahtar' olan 'sır' YALNIZCA SUNUCU TARAFINDAN kullanılır (çünkü müşterinin işareti ile karşılaştırılacak bir işaret oluşturmak için bunu kimse bilmiyor. Öyleyse soru: İstemci ve sunucu dışında kimsenin bilmediği 'api anahtarı' ile ne tür veriler birleştiriliyor? İşaret = api_key + ne?
AC'ler

2
Haklısın @AC'ler. Her iki sunucu (web sitesi ve 3. taraf API) aynı sırrı bilse bile, web sitesi sunucusunda bir miktar imza hesaplanamaz ve ardından bu sonucu HTML / JavaScript'e koyup tarayıcının bunu API'ye geçirmesini sağlayamazsınız . Bunu yaparak, başka herhangi bir sunucu ilk web sunucusundan bu HTML'yi isteyebilir, yanıttan imzayı alabilir ve bunu kendi web sitelerinde HTML'de kullanabilir. (Yukarıdaki gönderinin , HTML'deki genel bir API anahtarının nasıl güvenli olabileceği sorusuna gerçekten cevap vermediğini düşünüyorum .)
Arjan

63

İş ortaklarının yalnızca bize kaydettirdikleri alanlarda kullanabilecekleri bir API sunuyoruz. İçeriği kısmen herkese açıktır (ancak tercihen yalnızca bildiğimiz alanlarda gösterilebilir), ancak çoğunlukla kullanıcılarımıza özeldir. Yani:

  • Neyin gösterildiğini belirlemek için , kullanıcımızın bizimle oturum açması gerekir, ancak bu ayrı olarak ele alınır.

  • Verilerin nerede gösterildiğini belirlemek için, bildiğimiz alanlara erişimi sınırlamak ve her şeyden önce özel kullanıcı verilerinin CSRF'ye karşı savunmasız olmasını sağlamak için genel bir API anahtarı kullanılır .

Bu API anahtarı gerçekten herkes tarafından görülebilir, ortağımızın kimliğini başka hiçbir şekilde doğrulamıyoruz ve REFERER'a ihtiyacımız yok . Yine de güvenlidir:

  1. Bizim get-csrf-token.js?apiKey=abc123talep edildiğinde:

    1. abc123Veritabanında anahtarı arayın ve bu anahtar için geçerli etki alanlarının bir listesini alın.

    2. CSRF doğrulama çerezini arayın. Mevcut değilse, güvenli bir rastgele değer oluşturun ve bunu yalnızca HTTP oturum tanımlama bilgisine koyun . Tanımlama bilgisi mevcutsa, mevcut rastgele değeri alın.

    3. API anahtarından ve çerezden rastgele değerden bir CSRF jetonu oluşturun ve imzalayın . (Sunucuda bir jeton listesi tutmak yerine, değerleri imzalıyoruz. Her iki değer de imzalı jetonda okunabilir, sorun değil.)

    4. Yanıtı önbelleğe alınmayacak şekilde ayarlayın, çerezi ekleyin ve aşağıdaki gibi bir komut dosyası döndür:

      var apiConfig = apiConfig || {};
      if(document.domain === 'expected-domain.com' 
            || document.domain === 'www.expected-domain.com') {
      
          apiConfig.csrfToken = 'API key, random value, signature';
      
          // Invoke a callback if the partner wants us to
          if(typeof apiConfig.fnInit !== 'undefined') {
              apiConfig.fnInit();
          }
      } else {
          alert('This site is not authorised for this API key.');
      }
      

    Notlar:

    • Yukarıda bir istek taklit gelen bir sunucu tarafı komut dosyası engellemez, ancak yalnızca etki alanı maçları sağlar eğer bir tarayıcı tarafından istedi.

    • JavaScript için aynı kökenli ilke Bir tarayıcı yüklemek ve daha sonra JavaScript kaynağını incelemek için XHR (Ajax) kullanamazsınız sağlar. Bunun yerine, normal bir tarayıcı onu yalnızca <script src="https://our-api.com/get-csrf-token.js?apiKey=abc123">(veya dinamik bir eşdeğeri) kullanarak yükleyebilir ve ardından kodu çalıştırır. Tabii ki, sunucu gerekir değil desteklemek Çapraz Kökenli Kaynak Paylaşımı oluşturulan JavaScript ne de JSONP.

    • Bir tarayıcı komut dosyası document.domain, yukarıdaki komut dosyasını yüklemeden önce değerini değiştirebilir . Ancak aynı kökenli ilke yalnızca tarafından domain kısaltılması sağlar çıkarmadan yeniden gibi, önekleri subdomain.example.comsadece etmek example.comya myblog.wordpress.comkadar wordpress.com, hatta bazı tarayıcılarda bbc.co.ukiçin co.uk.

    • JavaScript dosyası bazı sunucu tarafı komut dosyaları kullanılarak getirilirse, sunucu da çerezi alır. Ancak, üçüncü taraf bir sunucu, bir kullanıcının tarayıcısının bu çerezi bizim etki alanımızla ilişkilendirmesini sağlayamaz. Bu nedenle, bir sunucu tarafı komut dosyası kullanılarak getirilen bir CSRF belirteci ve doğrulama çerezi, bir tarayıcıda değil, yalnızca sonraki sunucu tarafı çağrıları tarafından kullanılabilir. Ancak, bu tür sunucu tarafı aramaları hiçbir zaman kullanıcı çerezini içermez ve bu nedenle yalnızca herkese açık verileri getirebilir. Bu, bir sunucu tarafı komut dosyasının doğrudan ortağın web sitesinden alabileceği verilerle aynıdır.

  2. Bir kullanıcı oturum açtığında, istediğiniz şekilde bazı kullanıcı çerezleri ayarlayın. (Kullanıcı, JavaScript talep edilmeden önce zaten oturum açmış olabilir.)

  3. Sunucuya yapılan sonraki tüm API istekleri (GET ve JSONP istekleri dahil), CSRF belirtecini, CSRF doğrulama çerezini ve (oturum açılmışsa) kullanıcı çerezini içermelidir. Sunucu artık isteğin güvenilir olup olmadığını belirleyebilir:

    1. JavaScript, beklenen etki alanından yüklendi teminat altın jeton geçerli CSRF'e varlığı halinde bir tarayıcı tarafından yüklendi.

    2. Doğrulama çerezi olmadan CSRF belirtecinin varlığı sahteciliği gösterir.

    3. Hem CSRF belirtecinin hem de CSRF doğrulama çerezinin varlığı hiçbir şeyi garanti etmez: bu sahte bir sunucu tarafı isteği veya bir tarayıcıdan gelen geçerli bir istek olabilir. (Desteklenmeyen bir etki alanından yapılan bir tarayıcıdan istek olamaz.)

    4. Kullanıcı çerezinin varlığı, kullanıcının oturum açmasını sağlar, ancak kullanıcının söz konusu ortağın bir üyesi olmasını veya kullanıcının doğru web sitesini görüntülediğini garanti etmez.

    5. CSRF doğrulama çerezi olmadan kullanıcı çerezinin varlığı sahteciliği gösterir.

    6. Kullanıcı tanımlama bilgisinin varlığı, mevcut isteğin bir tarayıcı aracılığıyla yapılmasını sağlar. (Bir kullanıcının kimlik bilgilerini bilinmeyen bir web sitesine girmeyeceğini varsayarsak ve bazı sunucu tarafı taleplerde bulunmak için kendi kimlik bilgilerini kullanmalarının umurumuzda olmadığını varsayarsak.) Ayrıca CSRF doğrulama tanımlama bilgisine sahipsek, o CSRF doğrulama tanımlama bilgisi ayrıca bir tarayıcı kullanılarak alındı. Sonra, eğer aynı zamanda geçerli bir imzaya sahip belirteci bir CSRF var, veCSRF doğrulama tanımlama bilgisindeki rastgele sayı, o CSRF belirtecindeki rasgele sayı ile eşleşir, ardından bu belirteç için JavaScript, CSRF tanımlama bilgisinin ayarlandığı aynı önceki istek sırasında, dolayısıyla bir tarayıcı kullanılarak da alındı. Bu daha sonra, yukarıdaki JavaScript kodunun belirteç ayarlanmadan önce yürütüldüğünü ve o sırada etki alanının verilen API anahtarı için geçerli olduğunu gösterir.

      Yani: sunucu artık API anahtarını imzalı jetondan güvenle kullanabilir.

    7. Herhangi bir noktada sunucu isteğe güvenmezse, 403 Yasak döndürülür. Widget, kullanıcıya bir uyarı göstererek buna yanıt verebilir.

İmzalı CSRF belirteciyle karşılaştırdığımız için CSRF doğrulama tanımlama bilgisini imzalamanıza gerek yoktur. Tanımlama bilgisini imzalamamak, her HTTP isteğini kısaltır ve sunucu doğrulamasını biraz daha hızlı yapar.

Oluşturulan CSRF belirteci süresiz olarak geçerlidir, ancak yalnızca doğrulama tanımlama bilgisiyle birlikte, yani tarayıcı kapatılana kadar etkilidir.

Belirtecin imzasının ömrünü sınırlayabiliriz. OWASP önerisini karşılamak için kullanıcı oturumu kapattığında CSRF doğrulama tanımlama bilgisini silebiliriz . Kullanıcı başına rastgele sayıyı birden çok ortak arasında paylaşmamak için, API anahtarı çerez adına eklenebilir. Ancak bu durumda bile, yeni bir belirteç istendiğinde CSRF doğrulama çerezi kolayca yenilenemez, çünkü kullanıcılar aynı siteye birden çok pencerede göz atıyor, tek bir çerez paylaşıyor olabilir (bu, yenilenirken tüm pencerelerde güncellenir ve ardından Diğer pencerelerdeki JavaScript simgesi artık bu tek çerezle eşleşmeyecektir).

OAuth kullananlar için JavaScript fikrini aldığım OAuth ve İstemci Tarafı Widget'larına da bakın . İçin sunucu tarafında biz alanını sınırlamak için JavaScript kodu güvenemez hangi API, kullanımı, biz gizli anahtarlar yerine genel API anahtarlarını kullanıyoruz.


1
CORS kullanırken, belki güvenli bir şekilde genişletilebilir. Yukarıdakinin yerine OPTIONS, URL'de bazı genel API anahtarlarıyla önceden uçuşa alınmış bir isteği işlerken , sunucu bir tarayıcıya hangi etki alanlarına izin verildiğini söyleyebilir (veya isteği iptal edebilir). O olsa sakının bazı talepler önceden flighted isteği gerektirmeyen veya hiç CORS'yi kullanmaz ve CORS IE8 + ihtiyacı olduğunu. IE7 için bazı Flash yedekleri kullanılıyorsa, belki biraz dinamik crossdomain.xml, bunun için aynısını elde etmeye yardımcı olabilir. Henüz CORS / Flash'ı denemedik.
Arjan

Mükemmel cevap. Ancak Durumsuz REST çağrıları için işe yaradığını düşünmüyorum. Yanlışsa beni düzeltebilirsin.
Madhur Bhaiya

1
@MadhurBhaiya bu sizin devlet tanımınıza bağlıdır. Ben imzalı belirteç CSRF ve çerez gerçekten devlet olduğunu derdim ama: doğrulama ancak geçerli bir imza dayanır ve yok değil sunucu tarafında herhangi bir devlet gerekiyor. (Ayrıca, bu yanıt 8 yaşında ve IE8 öldü. Kullanım durumunuz CORS gerektiriyorsa ve CSRF'ye ihtiyaç duymuyorsa, yukarıdaki ilk yorumumdaki CORS yaklaşımını uygulamak çok daha kolay olabilir. Ancak, devlet olmadan olamazsınız herhangi bir CORS kullanıyor mu?)
Arjan

11

Bu sorunun kabul edilmiş bir cevabı var, ancak sadece açıklığa kavuşturmak için paylaşılan gizli kimlik doğrulama şu şekilde çalışır:

  1. İstemcinin ortak anahtarı var, bu herkesle paylaşılabilir, önemli değil, böylece javascript içine yerleştirebilirsiniz. Bu, sunucudaki kullanıcıyı tanımlamak için kullanılır.
  2. Sunucunun gizli anahtarı vardır ve bu sırrın korunması GEREKİR. Bu nedenle, paylaşılan anahtar kimlik doğrulaması, gizli anahtarınızı koruyabilmenizi gerektirir. Dolayısıyla, doğrudan başka bir hizmete bağlanan genel bir javascript istemcisi mümkün değildir çünkü sırrı korumak için bir sunucu aracısına ihtiyacınız vardır.
  3. Sunucu, gizli anahtarı (gizli anahtar bir tür tuza benzer) ve tercihen bir zaman damgasını içeren bir algoritma kullanarak isteği imzalar ve ardından isteği hizmete gönderir. Zaman damgası, "yeniden oynatma" saldırılarını önlemektir. Bir talebin imzası yalnızca yaklaşık n saniye geçerlidir . İmzaya dahil edilen zaman damgasının değerini içermesi gereken zaman damgası başlığını alarak sunucuda bunu kontrol edebilirsiniz. Bu zaman damgasının süresi dolmuşsa istek başarısız olur.
  4. Hizmet, sadece imzayı değil, düz metin olarak imzalanmış tüm alanları da içeren talebi alır.
  5. Hizmet daha sonra isteği paylaşılan gizli anahtarı kullanarak aynı şekilde imzalar ve imzaları karşılaştırır.

Doğru, ama tasarımı ile cevap vermez değil API anahtarını açığa. Ancak, bazı API'lerde API anahtarı olan herkes tarafından görülebilir ve soru hakkında ne yıllardan bu: "bir dinlenme hizmet işlemine istekleri [...] javascript (XHR / Ajax) üzerinden yapılan" . (Kabul edilen cevabın bu konuda da yanlış olduğunu hissediyorum; 2. noktanız bu konuda açık, iyi.)
Arjan

3

Soruyu orijinal bağlamında cevaplamaya çalışacağım. Yani soru şu: "Gizli (API) anahtarının JavaScript'e yerleştirilmesi güvenli mi?

Benim fikrime göre, sistemler arasında kimlik doğrulama amacını ortadan kaldırdığı için çok güvensiz. Anahtar kullanıcıya ifşa olacağından, kullanıcı yetkisi olmadığı bilgileri geri alabilir. Çünkü tipik bir dinlenme iletişiminde kimlik doğrulama yalnızca API Anahtarına dayanır.

Bana göre bir çözüm, JavaScript çağrısının, isteği, bir dinlenme çağrısı yapmaktan sorumlu olan dahili bir sunucu bileşenine iletmesidir. Dahili sunucu bileşeni diyelim ki bir Servlet API anahtarını izin tabanlı dosya sistemi gibi güvenli bir kaynaktan okuyacak, HTTP başlığına ekleyecek ve harici dinlenme çağrısı yapacak.

Umarım bu yardımcı olur.


Buna katılıyorum, Javascript başka bir REST API'yi doğrudan çağırmamalıdır, başka bir REST API'sini çağırmak için arka uç olarak kendi ince katmanına sahip olmalıdır
sendon1982

1

Sanırım API anahtarı değil oturum anahtarını kastediyorsunuz. Bu sorun http protokolünden miras alınır ve Oturum ele geçirme olarak bilinir . Normal "geçici çözüm", herhangi bir web sitesinde olduğu gibi, https'ye geçmektir.

REST hizmetini güvenli bir şekilde çalıştırmak için https'yi ve muhtemelen istemci kimlik doğrulamasını etkinleştirmelisiniz. Ama sonuçta, bu REST fikrinin ötesinde. REST asla güvenlikten bahsetmez.


8
Aslında anahtarı kastettim. Doğru hatırlıyorsam, bir API kullanmak için API anahtarını ve sırrını kimlik doğrulaması için geri kalan hizmete iletiyorsunuz, doğru mu? Kablonun üzerinden geçtikten sonra SSL tarafından şifreleneceğini biliyorum, ancak gönderilmeden önce, bunu kullanan istemci kodu tarafından mükemmel bir şekilde görülebilir ...
tjans

1

Sunucu tarafında yapmak istediğiniz şey, oturum açma veya kayıt sırasında istemciye geri gönderilen, süresi dolan bir oturum kimliği oluşturmaktır. İstemci daha sonra bu oturum kimliğini sonraki istekleri imzalamak için paylaşılan bir sır olarak kullanabilir.

Oturum kimliği yalnızca bir kez aktarılır ve bu SSL üzerinden OLMALIDIR.

Buradaki örneğe bakın

Oturum çalınmasını önlemek için isteği imzalarken bir nonce ve zaman damgası kullanın.


1
Ancak bir üçüncü taraf API'nizi kullandığında nasıl herhangi bir giriş olabilir ? Eğer kullanıcı giriş gidiyor, işler kolaydır: sadece bir oturumu kullanılır? Ancak diğer web sitelerinin API'niz için kimlik doğrulaması yapması gerektiğinde, bu yardımcı olmaz. (Ayrıca, bu blogunuzu tanıtmaya çok benziyor.)
Arjan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.