Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8832

node.js (Express) でのオープンリダイレクト脆弱性対策

$
0
0

この記事は、WESEEK Tips Wikiに投稿された記事「/Tips/JavaScript/Express/オープンリダイレクト対策」の転載です。

オープンリダイレクトとは?

Express に於けるリダイレクト

Express 4.x API#res.redirect

  • URL パラメータ等、ユーザーが指定可能な文字列をそのままリダイレクトさせるコード書くとセキュリティホールとなる
  • res.redirect('//evil.example.com/path/to/attack')で再現可能
    • -> http://evil.example.com/path/to/attackにリダイレクトされる

過去実際にあった脆弱性

対策

方針

  1. URL をパースし、不正な形式(//evil.example.com等)であればリダイレクトしない
  2. URL が、リクエストのホストと不一致ならリダイレクトしない
  3. URL が、ホワイトリストに入っていなければリダイレクトしない

コード

下記コードは、要求された URL へリダイレクトしない代わりに、サイトトップ(/)へのリダイレクトに振り替えている。

最小の対策(方針1,2まで)

middleware/safe-redirect.js
/**
 * Redirect with prevention from Open Redirect
 *
 * Usage: app.use(require('middleware/safe-redirect')())
 */constlogger=request('path/to/logger');module.exports=()=>{returnfunction(req,res,next){// extend res objectres.safeRedirect=function(redirectTo){if(redirectTo==null){returnres.redirect('/');}try{// check inner redirectconstredirectUrl=newURL(redirectTo,`${req.protocol}://${req.get('host')}`);if(redirectUrl.hostname===req.hostname){logger.debug(`Requested redirect URL (${redirectTo}) is local.`);returnres.redirect(redirectUrl.href);}logger.debug(`Requested redirect URL (${redirectTo}) is NOT local.`);}catch(err){logger.warn(`Requested redirect URL (${redirectTo}) is invalid.`,err);}logger.warn(`Requested redirect URL (${redirectTo}) is UNSAFE, redirecting to root page.`);returnres.redirect('/');};next();};};

利用方法

app.use(require('middleware/safe-redirect')());

ホワイトリスト指定機能付き(方針3まで)

middleware/safe-redirect.js
/**
 * Redirect with prevention from Open Redirect
 *
 * Usage: app.use(require('middleware/safe-redirect')(['example.com', 'some.example.com:8080']))
 */constlogger=request('path/to/logger')('middleware:safe-redirect');/**
 * Check whether the redirect url host is in specified whitelist
 * @param {Array<string>} whitelistOfHosts
 * @param {string} redirectToFqdn
 */functionisInWhitelist(whitelistOfHosts,redirectToFqdn){if(whitelistOfHosts==null||whitelistOfHosts.length===0){returnfalse;}constredirectUrl=newURL(redirectToFqdn);returnwhitelistOfHosts.includes(redirectUrl.hostname)||whitelistOfHosts.includes(redirectUrl.host);}module.exports=(whitelistOfHosts)=>{returnfunction(req,res,next){// extend res objectres.safeRedirect=function(redirectTo){if(redirectTo==null){returnres.redirect('/');}try{// check inner redirectconstredirectUrl=newURL(redirectTo,`${req.protocol}://${req.get('host')}`);if(redirectUrl.hostname===req.hostname){logger.debug(`Requested redirect URL (${redirectTo}) is local.`);returnres.redirect(redirectUrl.href);}logger.debug(`Requested redirect URL (${redirectTo}) is NOT local.`);// check whitelisted redirectconstisWhitelisted=isInWhitelist(whitelistOfHosts,redirectTo);if(isWhitelisted){logger.debug(`Requested redirect URL (${redirectTo}) is in whitelist.`,`whitelist=${whitelistOfHosts}`);returnres.redirect(redirectTo);}logger.debug(`Requested redirect URL (${redirectTo}) is NOT in whitelist.`,`whitelist=${whitelistOfHosts}`);}catch(err){logger.warn(`Requested redirect URL (${redirectTo}) is invalid.`,err);}logger.warn(`Requested redirect URL (${redirectTo}) is UNSAFE, redirecting to root page.`);returnres.redirect('/');};next();};};

Viewing all articles
Browse latest Browse all 8832

Trending Articles