Node.js arka uç giriş mantığı

Oct 12 2020

Bu yüzden Node.js'de kullanıcının kimliğini doğrulayan ve JWT'yi işleyen oldukça ilkel bir oturum açma mantığı yazdım. Güvenlik, verimlilik, bina, zaman uyumsuz / eşitleme, günlük kaydı açısından herhangi bir faydası var mı? GÜVENLİK temel endişemdir. Daha güzel bir formatta soru şu olur:

  • GÜVENLİK : Web sitem herhangi bir şekilde, şekilde veya biçimde güvenli mi? Tarafından sağlanan yöntemlere yerleşik olanlar dışında herhangi bir güvenlik önlemi uygulayabilir miyim merak ediyorum Node.js. Ayrıca, şifrelerin açıkça tahmin edilebileceğini biliyorum, ancak farklı kullanıcılar çalışırken oturum açmayı sağlamak için böyledirler.
  • VERİMLİLİK : Kullanıcı adlarını ve şifreyi nasıl kontrol ediyorum? Bunu yapmanın daha iyi bir yolu var mı?
  • YAPI : Web sitemi nasıl yükledim kabul edilebilir mi? Bir dosyadan okuduktan sonra yanıtı bitirme?
  • ASYNC / SYNC : Aynı anda hem önceden biçimlendirip asynchem de aradığımı biliyorum sync. Bunda bir sorun var mı?
  • GÜNLÜKLEME : Sunucuya yapılan tüm bağlantıları ve tüm oturum açma girişimlerini günlüğe kaydediyorum . Bu iyi bir uygulama mı yoksa günlüğe kaydetmenin başarması gereken şeyi abartıyor muyum? (Kaynak: Node.js ile Oturum Açma Sunucusu )

Benim kodum:

// main login logic
app.post('/api/login', apiLimiter, async function (req, res) {
  // TODO: implement cookie check whether a still valid token is set and if so respond with cookie already set
  // TODO: add roles into jwt and add roles checking into other code
  // TODO: if wrong password send a response telling there's a wrong password/username
  try  {
    const pw = req.body.password;
    const submittedUser = req.body.email;
    
    User.findOne({eMail: req.body.email}, async function (err, user) {
      if (err) throw err;
      console.log(user);
      const match = await bcrypt.compare(pw, user.password);
      console.log(match);
      if (match === true && user.eMail == submittedUser) {
        jwt2.sign({user}, 'secrettokenhere', { expiresIn: '15min'}, (err, token) =>{
          res.cookie(
            'access_token', 'Bearer '+ token, {
              //domain: 'localhost',
              path: '/',
              expires: new Date(Date.now() + 900000), // cookie will be removed after 15 mins
              httpOnly: true // in production also add secure: true
            })
            .json(
              user
            );
        });
      }
      else {
        res.status(200).send("Bad login");
      }
    });    
  } catch (err) {
    res.status(500).send();
    console.log(err);
  }
});

Not: Ön uç mantığıyla ayrı bir soru takip edecek.

Yanıtlar

6 CertainPerformance Oct 12 2020 at 17:59

Zaman uyumsuz giriş hatatry / catchetrafında User.findOneçünkü hiçbir şey yapamadığı findOneeşzamansızdır. Ne zaman findOnebaşarısız, bu geri arama için bir hata geçmek olacak, ancak geri arama yaptığında throw erryanıt kullanıcıya gönderilecek böylece, hiçbir şey, asenkron hatayı yakalamak için vardır. Başka bir sorun da, var olup olmadığını kontrol etmemenizdir user- yoksa , passwordözelliğine erişmeye çalıştığınızda bir hata atılacaktır . (Ayrıca .signbir hataya yol açıp açmadığını da kontrol etmiyorsunuz)

Geri aramalar yerine Promises kullanmayı da düşünebilirsiniz - findOnezaten bir Promise döndürür ve Promises genellikle tercih edilir çünkü daha kolay zincirlenebilirler ve hata yönetimi daha temiz olabilir.

Yine de güvenlik makul görünüyor:

  • Bağlantılara yalnızca HTTPS üzerinden izin verildiğinden emin olun. HTTP istekleri ele geçirilebilir.
  • Yukarıdaki oturum açma hatası düzeltilse bile, mevcut uygulamanız belirli bir e-postanın kayıtlı olup olmadığını herkese bildirebilir. Örneğin, e-posta adresinizi biliyorsanız, sizinle oturum açmayı deneyebilir ve parolanın yanlış olup olmadığını veya böyle bir e-postanın mevcut olup olmadığını görmek için yanıtı inceleyebilirim. Bu arzu edilmeyebilir. Düzeltmek istiyorsanız, şifre eşleşmediğinde, e-posta bulunamadığında olduğu gibi aynı bloğu girerek, onu kullanıcı için ayırt edilemez hale getirin - Bad loginher iki durumda da bir yanıt göndermek isteyebilirsiniz .
  • Benzer bir şekilde, kimlik doğrulama başarısız olduğunda, gönderilecek uygun durum kodu 200 değil 401'dir (yetkisiz).

Kullanıcı adlarını ve şifreyi nasıl kontrol ediyorum?

Bana tamamen normal görünüyor.

GÜNLÜKLEME: Sunucuya yapılan tüm bağlantıları ve tüm oturum açma girişimlerini günlüğe kaydediyorum . Bu iyi bir uygulama mı

Günlüğe kaydediliyorsa, oturum açma girişimleri günlüğe kaydedilecek en önemli şeylerden biridir. Ancak console.logbunu yapmanın doğru yolu değil, en azından yalnız değil - bazı kullanıcıların oturum açma işlemleri konusunda endişelendiğini, son oturum açma girişimlerini nasıl inceleyeceksiniz? Uygulama stdout'u aracılığıyla kontrol-F-ing, bunu yapmanın pek yönetilebilir bir yolu değildir. Bunun endüstri standardının ne olduğundan emin değilim, ancak bir günlük veritabanına kaydetmeyi düşünebilirsiniz.

E-posta girişi Yaparsınız:

const pw = req.body.password;
const submittedUser = req.body.email;

User.findOne({eMail: req.body.email}, async function (err, user) {
  if (err) throw err;
  console.log(user);
  const match = await bcrypt.compare(pw, user.password);
  console.log(match);
  if (match === true && user.eMail == submittedUser) {

İstek e-postasını adlı bir değişkene koyarsınız submittedUser, ki bu bana pek de sezgisel gelmez - bir kullanıcı değil, bir e-posta dizesi içerdiğini belirten bir değişken adı kullanmak daha iyidir email- her ikisini de aynı anda yok edebilirsiniz:

const { password, email } = req.body;

Daha sonra, req.bodytekrar geçmek yerine bu değişkenleri kullanın .

Daha sonra, .findOneyapmaya gerek yoktur user.eMail == submittedUser- bu kontrol, findOneçağrının kısıtlaması dikkate alındığında gereksiz olmalıdır .

Gövde içerir email, ancak veritabanı içerir eMail. Büyük harf kullanımı farklıdır ve bu, yazım hatalarına ve hatalara neden olabilecek garip bir tutarsızlıktır. Tek bir mülk adı kullanmanızı tavsiye ederim - büyük olasılıkla email, bu büyük harf kullanımı çok daha yaygın olduğu için.

Karşılaşmayı karşılaştırmak yerine === true, maçın doğru olup olmadığını kontrol edebilirsiniz - veya başka bir seçenek, gereksiz girintiyi azaltmak için maç doğru değilse fırlatmak ve a .catch.

Yana .signgeri arama tabanlı, ancak Promises çalışmak istiyorum, onunla Promise tabanlı hale util.promisify.

Genel olarak , kodun şöyle görünmesini umuyorum:

const jwtSignPromisified = util.promisify(jwt2.sign).bind(jwt2);
app.post('/api/login', apiLimiter, async function (req, res) {
    const failLogin = () => {
        logLoginAttempt(email, false); // or something - 2nd param indicates success
        // could also pass IP address
        res.status(401).send('Bad login');
    };
    const { password, email } = req.body;
    try {
        const user = await User.findOne({ email });
        if (!user) return failLogin();
        const match = await bcrypt.compare(password, user.password);
        if (!match) return failLogin();
        logLoginAttempt(email, true);
        const token = await jwtSignPromisified({ user }, 'secrettokenhere', { expiresIn: '15min' });
        res.cookie(
            'access_token', 'Bearer ' + token, {
            //domain: 'localhost',
            path: '/',
            expires: new Date(Date.now() + 900000), // cookie will be removed after 15 mins
            httpOnly: true // in production also add secure: true
        })
            .json(user);
    } catch (error) {
        // This should not be entered under normal circumstances:
        logServerError(error);
        res.status(500).send('Unexpected server error');
    }
});

nerede logLoginAttemptve logServerErrormuayene edilebilir giriş veritabanlarına kaydet.