NodeおよびExpress 4での基本的なHTTP認証


107

Express v3で基本的なHTTP認証を実装するのは簡単でした。

app.use(express.basicAuth('username', 'password'));

バージョン4(私は4.2を使用しています)はbasicAuthミドルウェアを削除したため、少し行き詰まっています。私は次のコードを持っていますが、ブラウザーがユーザーに資格情報を要求することはありません。これは私が望んでいることです(そして私が古い方法で行ったことを想像しています)。

app.use(function(req, res, next) {
    var user = auth(req);

    if (user === undefined || user['name'] !== 'username' || user['pass'] !== 'password') {
        res.writeHead(401, 'Access invalid for user', {'Content-Type' : 'text/plain'});
        res.end('Invalid credentials');
    } else {
        next();
    }
});

2
恥知らずなプラグイン:私はそれを簡単にし、必要な最も標準的な機能を備えたかなり人気のあるモジュールを維持しています:express-basic-auth
LionC

私は会社のプロジェクトのための時間の超短スパンで(コンテキストアウェアな認可を可能にする)、それに適応しなければならなかったので、私は最近、@LionCのパッケージをフォーク:npmjs.com/package/spresso-authy
castarco

回答:


108

バニラJavaScript (ES6)によるシンプルな基本認証

app.use((req, res, next) => {

  // -----------------------------------------------------------------------
  // authentication middleware

  const auth = {login: 'yourlogin', password: 'yourpassword'} // change this

  // parse login and password from headers
  const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
  const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':')

  // Verify login and password are set and correct
  if (login && password && login === auth.login && password === auth.password) {
    // Access granted...
    return next()
  }

  // Access denied...
  res.set('WWW-Authenticate', 'Basic realm="401"') // change this
  res.status(401).send('Authentication required.') // custom message

  // -----------------------------------------------------------------------

})

注:この「ミドルウェア」は、どのハンドラーでも使用できます。next()ロジックを削除して逆にします。以下の1ステートメントの例、またはこの回答の編集履歴を参照してください。

どうして?

  • req.headers.authorization値「Basic <base64 string>」が含まれていますが、空にすることもでき、失敗しないようにするため、奇妙な組み合わせ|| ''
  • ノードは分かっていないatob()btoa()、したがって、Buffer

ES6-> ES5

constたださvar...のソートは
(x, y) => {...}ちょうどされfunction(x, y) {...}
const [login, password] = ...split()、わずか2であるvar1で割り当て

インスピレーションの源(パッケージを使用)


上記は、非常に短く、すばやくプレイグラウンドサーバーにデプロイできるように意図された非常に単純な例です。ただし、コメントで指摘したように、パスワードにはコロン文字を含めることもできます:。これをb64authから正しく抽出するには、これを使用できます。

  // parse login and password from headers
  const b64auth = (req.headers.authorization || '').split(' ')[1] || ''
  const strauth = Buffer.from(b64auth, 'base64').toString()
  const splitIndex = strauth.indexOf(':')
  const login = strauth.substring(0, splitIndex)
  const password = strauth.substring(splitIndex + 1)

  // using shorter regex by @adabru
  // const [_, login, password] = strauth.match(/(.*?):(.*)/) || []

1つのステートメントでの基本認証

...一方、使用するログインが1つまたは非常に少ない場合、これは最低限必要なものです(資格情報を解析する必要すらありません)。

function (req, res) {
//btoa('yourlogin:yourpassword') -> "eW91cmxvZ2luOnlvdXJwYXNzd29yZA=="
//btoa('otherlogin:otherpassword') -> "b3RoZXJsb2dpbjpvdGhlcnBhc3N3b3Jk"

  // Verify credentials
  if (  req.headers.authorization !== 'Basic eW91cmxvZ2luOnlvdXJwYXNzd29yZA=='
     && req.headers.authorization !== 'Basic b3RoZXJsb2dpbjpvdGhlcnBhc3N3b3Jk')        
    return res.status(401).send('Authentication required.') // Access denied.   

  // Access granted...
  res.send('hello world')
  // or call next() if you use it as middleware (as snippet #1)
}

PS:「安全な」パスと「パブリック」パスの両方が必要ですか?express.router代わりに使用することを検討してください。

var securedRoutes = require('express').Router()

securedRoutes.use(/* auth-middleware from above */)
securedRoutes.get('path1', /* ... */) 

app.use('/secure', securedRoutes)
app.get('public', /* ... */)

// example.com/public       // no-auth
// example.com/secure/path1 // requires auth

2
ベスト
オブザ

2
.split(':')少なくとも1つのコロンを含むパスワードが詰まるため、使用しないでください。このようなパスワードはRFC 2617に従って有効です。
Distortum

1
const [_, login, password] = strauth.match(/(.*?):(.*)/) || []コロン部分にRegExp を使用することもできます。
アダブル2018

3
を使用!==してパスワードを比較すると、タイミング攻撃に対して脆弱になります。en.wikipedia.org/wiki/Timing_attackは、一定時間の文字列比較を使用していることを確認してください。
hraban

1
Buffer.from() // for stringsまたはBuffer.alloc() // for numbersを使用するBuffer()と、セキュリティ上の問題により非推奨になりました。
エイリアン氏

71

TL; DR:

☒がexpress.basicAuthなくなって
basic-auth-connect廃止されました
basic-auth任意のロジックはありません
http-auth過剰です
express-basic-authあなたが何をしたいです

より詳しい情報:

Expressを使用しているため、express-basic-authミドルウェアを使用できます。

ドキュメントを参照してください:

例:

const app = require('express')();
const basicAuth = require('express-basic-auth');
 
app.use(basicAuth({
    users: { admin: 'supersecret123' },
    challenge: true // <--- needed to actually show the login dialog!
}));

17
challenge: trueオプションについて理解するのに少し時間が
かかり

1
@VitaliiZurian良い点-私はそれを答えに追加しました。指摘してくれてありがとう。
rsp

4
@rspこれを特定のルートにのみ適用する方法を知っていますか?
ホルヘLヘルナンデス

他の依存関係を追加したくない場合は、基本的な認証を1行で手書きするのは非常に簡単です...
Qwerty

クライアントのURLはどのように見えますか?
GGEv

57

ミドルウェアの多くはv4でExpressコアから取り出され、個別のモジュールに配置されました。基本的な認証モジュールはこちらです:https : //github.com/expressjs/basic-auth-connect

あなたの例はこれに変更する必要があるだけです:

var basicAuth = require('basic-auth-connect');
app.use(basicAuth('username', 'password'));

19
このモジュールは非推奨であると主張しています(ただし、提案された代替案では満足できないようです)
Arnout Engelen 14

3
^^密に文書化されていないように、絶対に満足できない。ミドルウェアとしての使用例はゼロですが、おそらく良い例ですが、呼び出しは使用できません。彼らが与える例は、一般性には優れていますが、使用情報には適していません。
Wylie Kulik 2016年

ええ、これは非推奨です。推奨されるものはドキュメントが少ないですが、コードは非常にシンプルですgithub.com/jshttp/basic-auth/blob/master/index.js
Loourr

1
この回答でbasic-authライブラリの使用方法を説明しました
Loourr

コード全体にパスワードを平文で入力することに基づいて、モジュール全体がどのように存在しますか?少なくとも、base64で比較することによってそれを不明瞭にすることは、わずかに良いようです。
user1944491

33

元のコードを使用しbasicAuthて答えを見つけました:

app.use(function(req, res, next) {
    var user = auth(req);

    if (user === undefined || user['name'] !== 'username' || user['pass'] !== 'password') {
        res.statusCode = 401;
        res.setHeader('WWW-Authenticate', 'Basic realm="MyRealmName"');
        res.end('Unauthorized');
    } else {
        next();
    }
});

10
このモジュールは非推奨と見なされています。代わりにjshttp / basic-authを使用してください(同じAPIなので回答が引き続き適用されます)
Michael

32

Express 4.0でhttp-authによる基本認証を変更しました。コードは次のとおりです。

var auth = require('http-auth');

var basic = auth.basic({
        realm: "Web."
    }, function (username, password, callback) { // Custom authentication method.
        callback(username === "userName" && password === "password");
    }
);

app.get('/the_url', auth.connect(basic), routes.theRoute);

1
これは文字通りプラグアンドプレイです。素晴らしい。
sidonaldson 2017

20

これを行うには複数のモジュールがあるようですが、いくつかは非推奨です。

これはアクティブに見えます:https :
//github.com/jshttp/basic-auth

次に使用例を示します。

// auth.js

var auth = require('basic-auth');

var admins = {
  'art@vandelay-ind.org': { password: 'pa$$w0rd!' },
};


module.exports = function(req, res, next) {

  var user = auth(req);
  if (!user || !admins[user.name] || admins[user.name].password !== user.pass) {
    res.set('WWW-Authenticate', 'Basic realm="example"');
    return res.status(401).send();
  }
  return next();
};




// app.js

var auth = require('./auth');
var express = require('express');

var app = express();

// ... some not authenticated middlewares

app.use(auth);

// ... some authenticated middlewares

authミドルウェアを正しい場所に配置してください。それより前のミドルウェアは認証されません。


私は実際には「basic-auth-connect」を支持しています。名前は悪いですが、機能的には「basic-auth」よりも優れています。後者はすべて解析許可ヘッダーです。あなたはまだしていimplementたプロトコル自分(別名正しいヘッダを送信)
FDIM

パーフェクト!これありがとう。これはうまくいき、すべてをうまく説明しました。
Tania Rascia 2017年

私はこれを試しましたが、継続的なループを介してログインするように求め続けます。
jdog 2018

6

モジュールを必要とせずに基本認証を実装できます

//1.
var http = require('http');

//2.
var credentials = {
    userName: "vikas kohli",
    password: "vikas123"
};
var realm = 'Basic Authentication';

//3.
function authenticationStatus(resp) {
    resp.writeHead(401, { 'WWW-Authenticate': 'Basic realm="' + realm + '"' });
    resp.end('Authorization is needed');

};

//4.
var server = http.createServer(function (request, response) {
    var authentication, loginInfo;

    //5.
    if (!request.headers.authorization) {
        authenticationStatus (response);
        return;
    }

    //6.
    authentication = request.headers.authorization.replace(/^Basic/, '');

    //7.
    authentication = (new Buffer(authentication, 'base64')).toString('utf8');

    //8.
    loginInfo = authentication.split(':');

    //9.
    if (loginInfo[0] === credentials.userName && loginInfo[1] === credentials.password) {
        response.end('Great You are Authenticated...');
         // now you call url by commenting the above line and pass the next() function
    }else{

    authenticationStatus (response);

}

});
 server.listen(5050);

ソース:-http: //www.dotnetcurry.com/nodejs/1231/basic-authentication-using-nodejs


1

Expressはこの機能を削除し、basic-authライブラリの使用を推奨しています。

使用方法の例を次に示します。

var http = require('http')
var auth = require('basic-auth')

// Create server
var server = http.createServer(function (req, res) {
  var credentials = auth(req)

  if (!credentials || credentials.name !== 'aladdin' || credentials.pass !== 'opensesame') {
    res.statusCode = 401
    res.setHeader('WWW-Authenticate', 'Basic realm="example"')
    res.end('Access denied')
  } else {
    res.end('Access granted')
  }
})

// Listen
server.listen(3000)

このルートにリクエストを送信するには、基本認証用にフォーマットされたAuthorizationヘッダーを含める必要があります。

最初にcurlリクエストを送信する場合は、base64エンコーディングをname:pass使用する必要があります。この場合aladdin:opensesameYWxhZGRpbjpvcGVuc2VzYW1l

カールリクエストは次のようになります。

 curl -H "Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l" http://localhost:3000/

0
function auth (req, res, next) {
  console.log(req.headers);
  var authHeader = req.headers.authorization;
  if (!authHeader) {
      var err = new Error('You are not authenticated!');
      res.setHeader('WWW-Authenticate', 'Basic');
      err.status = 401;
      next(err);
      return;
  }
  var auth = new Buffer.from(authHeader.split(' ')[1], 'base64').toString().split(':');
  var user = auth[0];
  var pass = auth[1];
  if (user == 'admin' && pass == 'password') {
      next(); // authorized
  } else {
      var err = new Error('You are not authenticated!');
      res.setHeader('WWW-Authenticate', 'Basic');      
      err.status = 401;
      next(err);
  }
}
app.use(auth);

問題が解決することを願っていますが、コードの説明を追加して、ユーザーが本当に望んでいることをユーザーが完全に理解できるようにしてください。
ジャイミルパテル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.