Güvenli Jeton için CORS'u HTTPOnly Cookie ile Nasıl Etkinleştirebilirim?

Yayınlanan: 2021-12-29

Bu yazıda, erişim belirteçlerimizi güvence altına almak için CORS'u (Çapraz Kaynaklı Kaynak Paylaşımı) HTTPOnly çerezi ile nasıl etkinleştireceğimizi görüyoruz.

Günümüzde, arka uç sunucuları ve ön uç istemcileri farklı etki alanlarına dağıtılmaktadır. Bu nedenle, istemcilerin tarayıcılarda sunucuyla iletişim kurmasına izin vermek için sunucunun CORS'u etkinleştirmesi gerekir.

Ayrıca sunucular, daha iyi ölçeklenebilirlik için durumsuz kimlik doğrulaması uygular. Belirteçler istemci tarafında saklanır ve korunur, ancak oturum gibi sunucu tarafında değil. Güvenlik için, belirteçleri HTTPOnly çerezlerinde saklamak daha iyidir.

Kaynaklar Arası istekler neden engelleniyor?

Ön uç uygulamamızın https://app.geekflare.com konuşlandırıldığını varsayalım. https://app.geekflare.com //app.geekflare.com'a yüklenen bir komut dosyası yalnızca aynı kaynaklı kaynakları talep edebilir.

Başka bir etki alanına https://api.geekflare.com veya başka bir bağlantı noktasına https://app.geekflare.com:3000 veya başka bir şema http://app.geekflare.com bir çapraz kaynak isteği göndermeye çalıştığımızda, çapraz kaynak isteği tarayıcı tarafından engellenir.

Ama neden tarayıcı tarafından engellenen aynı istek herhangi bir arka uç sunucusundan curl isteği kullanılarak veya postacı gibi araçlar kullanılarak CORS sorunu olmadan gönderiliyor. Aslında, kullanıcıları CSRF (Siteler Arası İstek Sahteciliği) gibi saldırılardan korumak güvenlik içindir.

Bir örnek alalım, herhangi bir kullanıcının tarayıcısında kendi PayPal hesabına giriş yaptığını varsayalım. Başka bir malicious.com paypal.com yüklenen bir komut dosyasından paypal.com herhangi bir CORS hatası/engelleme olmadan aynı kaynak isteği gönderir gibi çapraz kaynaklı bir istek gönderebiliriz.

Saldırganlar, gerçek URL'yi gizlemek için kötü amaçlı sayfalarını https://malicious.com/transfer-money-to-attacker-account-from-user-paypal-account kısa URL'ye dönüştürerek kolayca gönderebilir. Kullanıcı kötü amaçlı bağlantı tıkladığında, komut etki yüklü malicious.com PayPal hesabı idam alacak saldırgana kullanıcı miktarını aktarmak için PayPal'a bir çapraz menşe isteği gönderir. PayPal hesaplarına giriş yapan ve bu kötü amaçlı bağlantıya tıklayan tüm kullanıcılar paralarını kaybedeceklerdir. PayPal hesabı kullanıcı bilgisi olmadan herkes kolayca para çalabilir.

Yukarıdaki nedenle, tarayıcılar tüm çapraz kaynaklı istekleri engeller.

CORS (Çapraz Kaynaklı Kaynak Paylaşımı) nedir?

CORS, sunucu tarafından tarayıcıya güvenilir etki alanlarından bir çapraz kaynak isteği göndermesini söylemek için kullanılan üstbilgi tabanlı bir güvenlik mekanizmasıdır.
Kaynaklar arası isteklerin tarayıcılar tarafından engellenmesini önlemek için kullanılan CORS başlıklarıyla etkinleştirilen sunucu.

CORS nasıl çalışır?

Sunucu, güvenilen etki alanını CORS yapılandırmasında zaten tanımladığı için. Sunucuya bir istek gönderdiğimizde, yanıt tarayıcıya istenen etki alanının güvenilir olup olmadığını başlığında bildirecektir.

İki tür CORS isteği vardır:

  • Basit istek
  • Ön Kontrol İsteği

Basit İstek:

CORS-basit istek akışı, bir çapraz kaynak isteği gönderdiğini ancak yanıt aldığında söyler. Başlıkları kontrol eder.

  • Tarayıcı, isteği Origin(https://app.geekflare.com) ile bir çapraz kaynaklı etki alanına gönderir .
  • Sunucu, izin verilen yöntemlerle ve izin verilen kaynakla ilgili yanıtı geri gönderir .
  • Talebi aldıktan sonra, tarayıcı gönderilen kaynak başlık değeri ( https://app.geekflare.com ) ve alınan erişim-kontrol-allow-origin değerinin ( https://app.geekflare.com ) aynı olup olmadığını kontrol eder veya joker karakter(*). Aksi takdirde CORS hatası verecektir.

Ön Kontrol İsteği:

CORS-Preflight Request Image which show the flow of cross-origin request with OPTIONS preflight request before sending actual request for verifying headers.

  • Yöntemler (PUT, DELETE) veya özel başlıklar veya farklı içerik türü vb. gibi çapraz kaynaklı istekten gelen özel istek parametresine bağlı olarak. Tarayıcı, gerçek isteğin gönderilmesinin güvenli olup olmadığını kontrol etmek için bir ön kontrol SEÇENEKLERİ isteği göndermeye karar verir. ya da değil.
  • Yanıtı aldıktan sonra (durum kodu: 204, yani içerik yok), tarayıcı gerçek istek için erişim-kontrol-izin ver parametrelerini kontrol edecektir. İstek parametrelerine sunucu tarafından izin veriliyorsa. Gönderilen ve alınan asıl çapraz kaynak isteği

access-control-allow-origin: * , tüm kaynaklar için yanıta izin verilir. Ancak ihtiyacınız olmadıkça güvenli değildir.

CORS nasıl etkinleştirilir?

Herhangi bir etki alanı için CORS'u etkinleştirmek için, kaynak, yöntemler, özel üstbilgiler, kimlik bilgileri vb.'ye izin vermek için CORS üstbilgilerini etkinleştirin.

Tarayıcı, sunucudan CORS başlığını okur ve yalnızca istek parametrelerini doğruladıktan sonra istemciden gelen gerçek isteklere izin verir.

  • Access-Control-Allow-Origin: Tam etki alanlarını belirtmek için(https://app.geekflate.com, https://lab.geekflare.com) veya joker karakter(*)
  • Erişim Kontrolü-İzin Verme Yöntemleri: Yalnızca ihtiyacımız olan HTTP yöntemlerine (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS) izin vermek için.
  • Access-Control-Allow-Headers: Yalnızca belirli Başlıklara izin vermek için (Yetkilendirme, csrf-token)
  • Access-Control-Allow-Credentials: Kaynaklar arası kimlik bilgilerine (çerezler, yetkilendirme başlığı) izin vermek için kullanılan Boole değeri.
  • Access-Control-Max-Age: Tarayıcıya ön kontrol yanıtını bir süre önbelleğe almasını söyler.
  • Access-Control-Expose-Headers: İstemci tarafı komut dosyası tarafından erişilebilen üstbilgileri belirtin.

Apache ve Nginx web sunucusunda CORS'u etkinleştirmek için bu öğreticiyi izleyin.

ExpressJS'de CORS'u Etkinleştirme

CORS içermeyen örnek bir ExpressJS uygulamasını ele alalım:

 const express = require('express'); const app = express() app.get('/users', function (req, res, next) { res.json({msg: 'user get'}) }); app.post('/users', function (req, res, next) { res.json({msg: 'user create'}) }); app.put('/users', function (req, res, next) { res.json({msg: 'User update'}) }); app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') })

Yukarıdaki örnekte, POST, PUT, GET yöntemleri için kullanıcı API uç noktasını etkinleştirdik, ancak DELETE yöntemini etkinleştirmedik.

CORS'u ExpressJS uygulamasında kolayca etkinleştirmek için, cors'ları yükleyebilirsiniz.

 npm install cors

Erişim-Kontrol-İzin Ver-Origin

Tüm etki alanı için CORS'u etkinleştirme

 app.use(cors({ origin: '*' }));

Tek bir alan için CORS'u etkinleştirme

 app.use(cors({ origin: 'https://app.geekflare.com' }));

CORS'a kaynak için izin vermek istiyorsanız https://app.geekflare.com ve https://lab.geekflare.com

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ] }));

Erişim-Kontrol-İzin Ver-Yöntemleri

Tüm yöntemler için CORS'u etkinleştirmek için, ExpressJS'deki CORS modülünde bu seçeneği atlayın. Ancak belirli yöntemleri etkinleştirmek için (GET, POST, PUT).

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'] }));

Erişim-Kontrol-İzin-Üstbilgileri

Varsayılanlar dışındaki üstbilgilerin gerçek isteklerle gönderilmesine izin vermek için kullanılır.

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'] }));

Erişim-Kontrol-İzin Ver-Kimlik Bilgileri

Tarayıcıya, withCredentials true olarak ayarlanmış olsa bile istek üzerine kimlik bilgilerine izin vermesini söylemek istemiyorsanız bunu atlayın .

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true }));

Erişim-Kontrol-Max-Yaş

Tarayıcının ön kontrol yanıt bilgilerini belirli bir saniye boyunca önbellekte önbelleğe almasını sağlamak. Yanıtı önbelleğe almak istemiyorsanız bunu atlayın.

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600 }));

Önbelleğe alınmış ön kontrol yanıtı, tarayıcıda 10 dakika boyunca kullanılabilir olacaktır.

Erişim-Kontrol-Gösterme-Üstbilgileri

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['Content-Range', 'X-Content-Range'] }));

Joker karakteri (*) maruz bırakılan Başlıklara koyarsak, Yetkilendirme başlığını göstermez. Bu yüzden aşağıdaki gibi açıkça göstermeliyiz

 app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['*', 'Authorization', ] }));

Yukarıdakiler, tüm başlıkları ve Yetkilendirme başlığını da ortaya çıkaracaktır.

HTTP çerezi nedir?

Çerez, sunucunun istemci tarayıcısına göndereceği küçük bir veri parçasıdır. Daha sonraki isteklerde, tarayıcı her istekte aynı etki alanıyla ilgili tüm çerezleri gönderir.

Çerezin, bir çerezin ihtiyaç duyduğumuz şekilde farklı çalışmasını sağlamak için tanımlanabilen bir özelliği vardır.

  • İsim Çerezin adı.
  • değer: çerez adı ile ilgili çerez verileri
  • Etki alanı: çerezler yalnızca tanımlanan etki alanına gönderilir
  • Yol: yalnızca tanımlanan URL öneki yolundan sonra gönderilen çerezler. Çerez yolumuzu path='admin/' gibi tanımladığımızı varsayalım. https://geekflare.com/expire/ URL'si için gönderilmeyen, ancak URL öneki https://geekflare.com/admin/ ile gönderilen çerezler
  • Max-Age/Expires(saniyedeki sayı): Çerezin süresi ne zaman dolacak. Tanımlama bilgisinin ömrü, tanımlama bilgisini belirtilen süreden sonra geçersiz kılar.
  • HTTPOnly(Boolean): Arka uç sunucusu bu HTTPOnly tanımlama bilgisine erişebilir, ancak true olduğunda istemci tarafı komut dosyasına erişemez.
  • Secure(Boolean): Tanımlama bilgileri, yalnızca doğru olduğunda bir SSL/TLS alanı üzerinden gönderilir.
  • sameSite(string [Strict, Lax, None]): Siteler arası isteklerde gönderilen çerezleri etkinleştirmek/kısıtlamak için kullanılır. sameSite çerezleri hakkında daha fazla ayrıntı sameSite için MDN'ye bakın. Strict, Lax, None olmak üzere üç seçeneği kabul eder. Çerez güvenli değeri, tanımlama bilgisi yapılandırması için true olarak ayarlandı sameSite=Yok.

Belirteçler için neden HTTPOnly çerezi?

Sunucudan gönderilen erişim belirtecini yerel depolama , dizine alınmış DB ve tanımlama bilgisi (HTTPOnly değil true olarak ayarlanmıştır) gibi istemci tarafı depolamasında saklamak XSS saldırısına karşı daha savunmasızdır. Sayfalarınızdan birinin XSS saldırısına karşı zayıf olduğunu varsayalım. Saldırganlar, tarayıcıda depolanan kullanıcı belirteçlerini kötüye kullanabilir.

HTTPOnly tanımlama bilgileri yalnızca sunucu/arka uç tarafından ayarlanır/alınır, ancak istemci tarafında değil.

İstemci tarafı komut dosyası, bu HTTPonly tanımlama bilgisine erişimle sınırlandırılmıştır. Bu nedenle HTTPOnly çerezleri, XSS Saldırılarına karşı savunmasız değildir ve daha güvenlidir. Çünkü sadece sunucu tarafından erişilebilir.

CORS'un etkin olduğu arka uçta HTTPOnly çerezini etkinleştir

CORS'ta Çerezin etkinleştirilmesi, uygulamada/sunucuda aşağıdaki yapılandırmaya ihtiyaç duyar.

  • Access-Control-Allow-Credentials üstbilgisini true olarak ayarlayın.
  • Access-Control-Allow-Origin ve Access-Control-Allow-Headers bir joker karakter(*) olmamalıdır.
  • Çerez sameSite özniteliği Yok olmalıdır.
  • sameSite değerini yok olarak etkinleştirmek için güvenli değeri true olarak ayarlayın: Alan adında çalışması için SSL/TLS sertifikasıyla arka ucu etkinleştirin.

Oturum açma kimlik bilgilerini kontrol ettikten sonra HTTPOnly çerezinde bir erişim belirteci ayarlayan bir örnek kod görelim.

 const express = require('express'); const app = express(); const cors = require('cors'); app.use(cors({ origin: [ 'https://app.geekflare.com', 'https://lab.geekflare.com' ], methods: ['GET', 'PUT', 'POST'], allowedHeaders: ['Content-Type', 'Authorization', 'x-csrf-token'], credentials: true, maxAge: 600, exposedHeaders: ['*', 'Authorization' ] })); app.post('/login', function (req, res, next) { res.cookie('access_token', access_token, { expires: new Date(Date.now() + (3600 * 1000 * 24 * 180 * 1)), //second min hour days year secure: true, // set to true if your using https or samesite is none httpOnly: true, // backend only sameSite: 'none' // set to none for cross-request }); res.json({ msg: 'Login Successfully', access_token }); }); app.listen(80, function () { console.log('CORS-enabled web server listening on port 80') });

Yukarıdaki dört adımı arka uç dilinizde ve web sunucunuzda uygulayarak CORS ve HTTPOnly tanımlama bilgilerini yapılandırabilirsiniz.

Yukarıdaki adımları izleyerek CORS'u etkinleştirmek için apache ve Nginx için bu öğreticiyi takip edebilirsiniz.

Cross-Origin isteği için withCredentials

Varsayılan olarak aynı kaynak isteğiyle gönderilen kimlik bilgileri (Çerez, Yetkilendirme). Cross-Origin için withCredentials değerini true olarak belirtmeliyiz.

XMLHttpRequest API'si:

 var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://api.geekflare.com/user', true); xhr.withCredentials = true; xhr.send(null);

API'yi getir:

 fetch('http://api.geekflare.com/user', { credentials: 'include' });

JQuery Ajax'ı:

 $.ajax({ url: 'http://api.geekflare.com/user', xhrFields: { withCredentials: true } });

aksiyolar:

 axios.defaults.withCredentials = true

Çözüm

Umarım yukarıdaki makale, CORS'un nasıl çalıştığını anlamanıza ve sunucuda çapraz kaynaklı istekler için CORS'u etkinleştirmenize yardımcı olur. Tanımlama bilgilerini HTTPOnly'de depolamak neden güvenlidir ve withCredentials'ın istemcilerde kaynaklar arası istekler için nasıl kullanıldığı.