Nuxt.jsでcsrfTokenを埋め込みセキュアな通信を行う

背景:マイクロサービスコンテナでNuxtアプリケーションを作成し、本番運用上手くいったので嬉しくてついry

自社サービスでNuxtアプリケーションを実運用できたので、前回のコンテナ化を元に、バックエンドのAPIで、Nuxt + Expressの構成でcsrf対策と、実現します。

@nuxt/axios-modulesで、バック・フロント兼用のaxiosの設定を行う

バックエンドをnode.jsで統合する場合、axios-moduleを使ったほうが面倒事が少なくて済みます。

npm i --save @nuxt/axios

でインストールを行います。これでnuxt.config.jsのmodulesに'@nuxtjs/axios'を追加すると、axiosキーでイベントや、パラメータを設定できます。詳しくは、上述の公式リンクを参照ください。

nuxt.config.js
  modules: [ 
   '@nuxtjs/axios',
    ...
  ],
  axios: {
    browserBaseURL: process.env.BASE_APP_URL || '/',
    requestInterceptor (config, { store }) {
      if (store.state.csrfToken) {
        config.headers.common['x-csrf-token'] = store.state.csrfToken
      }
      return config
    }
  }

以上のようにrequestInterceptorは client中で、リクエスト発生前にvuxStoreに、csrfTokenが存在すれば、これをヘッダーに設定する事ができるので、セットします。

csurfによって、セキュリティトークンの発行ができるようにする

次に、バックエンドで、tokenを受け渡す方法ですが、expressの場合、cookieParserを、使うと自動で、レスポンスにCookieを受け渡すことが出来るので、利用します。また、csurfというライブラリを利用すると、cookieによってuuidを保持することが可能になり、セキュリティトークンの照合が可能になります。

server/index.js
import express from 'express'
import { Nuxt, Builder } from 'nuxt'
import csrf from 'csurf'
import cookieParser from 'cookie-parser'
import bodyParser from 'body-parser'

const app = express()
const host = process.env.HOST || '127.0.0.1'
const port = process.env.PORT || 3000

// Import and Set Nuxt.js options
let config = require('../nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')
// Init Nuxt.js
const nuxt = new Nuxt(config)

// Build only in dev mode
if (config.dev && process.env.NOBUILD !== 'on') {
  const builder = new Builder(nuxt)
  builder.build()
}

app.set('port', port)
app.use(bodyParser.urlencoded({
  extended: true
}))
app.use(bodyParser.json())
app.use(cookieParser())
app.use(csrf({ cookie: true }))
// Import API Routes
const baseUrl = process.env.BASE_APP_URL || '/'
//… some middleware injection 
app.use(nuxt.render)
// Listen the server
app.listen(port, host)
console.log('Server listening on ' + host + ':' + port) // 

nuxtSeverInitによってcsrfTokenをstateにセットする

また、actions内のnuxtSeverInitはnuxt内の特別なイベントで 、サーバサイドで実行される、イベントになります。csurfをmiddlewareに挟むことによって、requestから、csrfTokenが取得できるようになるため、mutation経由でcsrfTokenをstateに仕込むことができます。

store/index.js
var mutations = {
 SET_CSRF_TOKEN (state, csrfToken) {
   state.csrfToken = csrfToken
 },
 ...
}

const actions = {
  nuxtServerInit ({commit}, { req }) {
    if (req.cookies) {
      commit('SET_CSRF_TOKEN', req.csrfToken())
    }
  },
 ...
}

以上で、axios-modules経由でcsrfTokenを仕込むことが出来たので、セキュアなAPI通信ができるようになりました。また、今回の事例は、requestInterceptor (config, { store })でトークン取得を行え、送信時にTokenが仕込めればよいので、バックエンドがexpressでなくても応用できます。実はこのあたりのrequestのパラメータのやり取りの融通が効く所が、フロントだけマイクロサービスにしても、完全にプロダクト運用に踏み切れると判断したところだったりします。

余談

最近Firabaseホスティングで、Cloud Functions経由で、バックエンドほとんどいらないアプリケーションをNuxt(Next)で作れるらしいという噂があり、Kubernetesで苦労(参考1,参考2)しなくても全然良かった(参考3)のではないかと思いました。

ただ、サーバレスだとセッションアフィニティーを保持することが出来ない気がしている(出来たらすみません。)ので、断続的な接続とかどうするんだろうとか思いましたが、認証とかgauth使えばいいし、必要であれば、PubSubで十分対応可能な範囲なのかなと思ったりするので、やっぱり、もっと早く知りたかったですね。ただ、ホスティング使わなくても、Nuxt.jsもう全然本番運用イケるじゃん!って思った時の僕の感動を少しでも感じていただければと思いますし、皆さんは是非、firebase使って楽していただければと思います。

nodeは実装楽だしフロントのビューはWEBなら絶対に書くし、速度重視のサービスでなければそこそこのレスポンススピードが出てSSRだとFirstMeaningfulViewまで早いし、小規模マイクロサービスに向いてるなぁと個人的には思うこの頃。。。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.