はじめに
はじめまして。みのといいます。
今回バックエンド側で送信元IPアドレスによって変化するWebアプリを作成しました。
その際に詰まったところが多かったため、備忘録を残していきます。
やりたいこと
バックエンド側のNestJSでクライアントの送信元のIPアドレスを取得し、アドレスによって挙動を変えたい。
解決策
HTTPリクエストを受け付けるように設定する。
まずはHTTPリクエストを扱えるように設定します。
コントローラークラスにてReqオブジェクトをcommonパッケージからimportします。また、expressパッケージからHTTPリクエストを扱えるRequestオブジェクトをimportします。
@Get()でGetリクエストが来たときにgetFunctionを呼び出すことができます。
その際に、@ReqでHTTPリクエストを引数としてもたせることで、サービスクラスにあるgetFunctionにHTTPリクエストによって挙動を変更させることができます。
import { Controller, Get, Req } from '@nestjs/common';
import { AppService } from './app.service';
import { Request } from 'express';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
async getFunction(@Req() req:Request){
return this.appService.getFunction(req,res);
}
}
送信元IPアドレスを取得する
前項でHTTPリクエストを扱えるようにしたので、HTTPリクエストに格納されているIPアドレス情報を取得します。
サービスクラスにて、同様にRequestオブジェクトをexpressパッケージからimportします。
送信元IPアドレスを扱うメソッドにて、HTTPリクエストで格納されたクライアントのIPアドレスを取得します。IPアドレスによって挙動を変化させることができます。
import { Request } from 'express'
async getFunction(req:Request){
const ip = req.ip;
// (以下省略)
}
これでIPアドレスを取得し、そのIPアドレスによって挙動を変化するメソッドを作れるようになりました。
注意点
しかし、デプロイする環境次第では、HTTPリクエストを送信したクライアントの送信元IPアドレスではないアドレスが取得される可能性があります。
そのような環境では、プロキシやロードバランサーを挟んでいたり、外部インターネットからの直接のアクセスを遮断したりなど通信間でコンポーネントが挟まれることが想定されます。
この場合送信元IPアドレスはクライアントではなく、プロキシやロードバランサーのIPアドレス、プライベートアドレスやリンクローカルアドレス等が取得されてしまいます。
このような環境があると踏まえて、クライアントの送信元IPアドレスを取得できるようにHTTPリクエストのヘッダーの一つであるX-Forwarded-Forヘッダーから情報を取得します。
X-Forwarded-For (XFF) ヘッダーは、 HTTP プロキシーサーバーを通過してウェブサーバーへ接続したクライアントの、送信元 IP アドレスを特定するために事実上の標準となっているヘッダーです。
引用元:https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/X-Forwarded-For
このヘッダーを用いることでプロキシなどを経由していてもクライアントの送信元IPアドレスを取得できます。※できないケースもあります。
送信元IPアドレスをX-Forwarded-Forヘッダーから取得する。
mainクラスにてplatform-expressパッケージからNestExpressApplicationモジュールをimportします。
(WebサーバーフレームワークのExpress.jsの機能をそのまま使えることができます。)
そして、app.setを用いてtrust proxyをtrueにします。こうすることで上記のサービスクラスに記載していたconst ip = req.ip
の部分で送信元IPアドレスを取得することができます。
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
// NestExpressApplicationをimportする。
import { NestExpressApplication } from '@nestjs/platform-express';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// trust proxyをonにする。
app.set('trust proxy', true);
const port = process.env.PORT || 3000;
await app.listen(port);
}
bootstrap();
なお、参考程度にExpress.jsの公式ドキュメントにて、req.ipについてtrust proxyがfalseでなかったら、X-Forwarded-ForヘッダーからIPアドレスを抽出するよ、という記載があります。
req.ip
Contains the remote IP address of the request.When the trust proxy setting does not evaluate to false, the value of this property is derived from the left-most entry in the X-Forwarded-For header. This header can be set by the client or by the proxy.
引用元:https://expressjs.com/en/4x/api.html#req.ip
終わりに
上記の解決策によってNestJSでクライアントの送信元IPアドレスを取得し、送信元IPアドレスによって挙動を変更させるようにできました。
私が試しに作成したアプリケーションを下記に置きます。
クライアントの送信元IPアドレスによってFaviconが変化するというものです。(現在は国旗にしています。が、今後は変更予定です。)
Webアプリ:https://favipon.azurewebsites.net
GitHub:https://github.com/minobun/FavIPon