Lesson 9

セキュアなアプリケーションの実装

Lesson 9 Chapter 1
helmetモジュール

突然ですが、皆さんの作ったWebアプリケーションが公開されたときのことを考えてみましょう。公開後、アプリケーションにはたちまち多くのアクセスが発生します。ほとんどは一般の利用者からのアクセスですが、中には悪意ある攻撃者からのアクセスもあります。

実際に、あるWebアプリケーションが攻撃を受け、ウイルスを埋め込まれてしまった事案も起きています。利用者が安心してWebアプリケーションを使うためには、ウイルスの感染を防いだり、情報が外部に漏れないようにしたりといったセキュリティ対策が必要です。

そこでLesson9では、Node.jsを使用したアプリケーションのセキュリティ対策を学びます。本項を参考に、セキュリティに対する正しい知識と認識を身につけてください。

Express(Node.js)おけるセキュリティ対策

開発環境から実稼働環境(本番環境)へのデプロイは、ただでさえ緊張を伴う作業です。単に同一のセットアップをすれば良いというわけではなく、パフォーマンスや信頼性を損なわないための配慮が求められます。

特にセキュリティーに関しては確認すべき項目が多く、より一層の注意を払わばければなりません。

ただし、すべての項目を網羅しようとすると、説明が膨大になってしまいます。そのため、本章は特に重要な2つのトピックを取り上げます。

  • helmetモジュール
  • CORS対策

まずは「helmetモジュール」から解説していきましょう。

その他の対策項目

その他のセキュリティ対策として、以下の3点を挙げておきます。

  • 非推奨のバージョンのExpressを使用しない
  • 依存関係にあるパッケージの脆弱性を確認する
  • 認証に対して回数制限ポリシーを実装する

上記はほんの一例です。ぜひ皆さん自身でもキャッチアップを図ってください。

helmetモジュールとは

helmetモジュールは、Expressを使ってWebアプリケーションを作成する際、セキュリティ対策として使われるモジュールです。具体的には、複数の種類のHTTPヘッダーを設定してセキュリティを堅牢化します。

helmetの使用方法は非常に簡単です。npm install helmetを実行してインストールし、次のように記述してください。

app.js
const express = require("express");
const helmet = require("helmet");
const app = express();

app.use(helmet());

app.use(helmet())と記述しただけで、本当にセキュリティ対策になるのか疑問に思えるかもしれません。前述した通り、helmetモジュールは複数のHTTPヘッダーを設定してセキュリティを確保します。具体的に対応できる脆弱性は以下の通りです。

  • XSS(クロスサイトスクリプティング)攻撃
  • 情報漏洩
  • 不正なSSL証明
  • クリックジャッキング
  • フィッシング
  • 中間者攻撃
  • MIMEスニッフィング

煩雑さを避けるため、ここではXSS(クロスサイトスクリプティング)攻撃に絞って解説します。

XSS(クロスサイトスクリプティング)

XSSとは、Webアプリケーションサーバーの脆弱性を突き、悪質なサイトへ誘導するスクリプトを埋め込む攻撃です。

具体的には、XSSによって次の被害を受ける可能性があります。

  • 悪意のあるスクリプトにより、利用者が偽ページへ誘導される
  • 悪意のあるスクリプトにより、利用者の入力情報が攻撃者のもとへ送信される

XSSは数ある攻撃の中でも報告数が突出して多く、適切な対策を行わなければなりません。

helmetモジュールにより設定されるHTTPヘッダー(Content-Security-Policy: CSP)は、このXSS攻撃を緩和します。

具体的には、ドメインのホワイトリストを参照し、リストに載っているドメインのスクリプトのみ実行します。ホワイトリストに載っていないスクリプトは実行されないため、攻撃者に付け入る隙を与えません。

helmetモジュールを使うことで、上で説明した以外のHTTPヘッダーの設定も可能です。設定可能なHTTPヘッダーは、GitHubのページから確認できます。

helmetモジュールは、すべての脅威からWebアプリケーションを守る特効薬ではありません。あくまでセキュリティ向上の手助けをするツールである、ということを認識して活用してください。

Lesson 9 Chapter 2
CORS対応

Chapter1に引き続き、Node.js(Express)におけるセキュリティ対策を見ていきましょう。

今回は「CORS」の解説です。

CORSとは

CORSとは「オリジン間リソース共有(Cross-Origin Resource Sharing )」の略であり、異なるオリジンへのアクセスを許可する仕組みです。

前提として、現在のWebブラウザには「同一オリジンポリシー(Same-Origin-Policy)」が実装されており、異なるオリジンへのアクセスを制限しています。

理由は後述しますが、XSSやCSRFなどのサイト間にまたがる攻撃を防ぐためです。

しかし、実際にWebアプリケーションを開発していると、異なるオリジン間で通信しなければならない状況に遭遇します。CORSはこの課題を解決し、異なるオリジン間でのアクセスを条件付きで許可します。

ここまでの説明で理解できなくても心配ありません。CORSを理解するためには、「オリジン」について深く知る必要があります。以下で解説していきましょう。

オリジンとは

オリジンは「スキーム(プロトコル)」「ホスト名」「ポート」を組み合わせた文字列です。以下の画像で確認しましょう。

origin.png オリジンの定義

例えば、URLがhttp://example.com:8080/dirのとき、http://example.com:8080がオリジンです。よく知られる「ドメイン」と異なり、プロトコルとポート番号が含まれる点に注意してください。

同一オリジンとクロスオリジン

次は、2つのオリジンを比べたとき、「同一オリジンであること」と「他のオリジンであること」が、どのように判定されるのか確認しましょう。

以下の表は、http://my-app.jp/my/index.htmlと比べたとき、同一オリジンであるかどうかをまとめたものです。

比較対象のオリジン 同一オリジンかどうか
http://my-app.jp/my/page.html 同一オリジン
http://my-app.jp/your/item/index.html 同一オリジン
https://my-app.jp/index.html 他のオリジン(プロトコルが異なる)
http://my-app.jp:81/my/index.html 他のオリジン(ポート番号が異なる)
http://your-app.jp/index.html 他のオリジン(ホスト名が異なる)

上記の通り、プロトコル・ホストネーム・ポート番号のうち一つでも異なれば別オリジンとみなされます。

クロスオリジン

このようにオリジンが異なることを「クロスオリジン」とも呼びます。

同一オリジンポリシーがある理由

ここで、なぜブラウザには「同一オリジンポリシー」という、別オリジンへのアクセスを制限する仕組みがあるのか説明します。

あなたが悪意のあるサイトA(http://evil.jp)を訪問してサイトを閲覧しているとします。サイトAには、悪意のあるスクリプトB(http://secret.jp)から情報を抜き出し攻撃者に送信するスクリプト)が埋め込まれているとしましょう。

このとき、ブラウザの「同一オリジンポリシー」により、http://evil.jpからhttp://secret.jpへのHTTPリクエストは発生しません。すなわち、http://secret.jpから情報を抜き出そうとする悪意のあるスクリプトBは、HTTPリクエストを送ることができないのです。

今回は悪意のあるスクリプトが実行されずに済みました。このように「同一オリジンポリシー」があることで、Webアプリケーションのセキュリティが確保されます。

CORS(オリジン間リソース共有)の必要性

安全なWebアプリケーション開発のためには、同一オリジンポリシーが欠かせません。しかし、時としてその制約がネックになる場合もあります。

たとえば、どうしてもhttps://my-app.jpからhttps://my-resource.jpへHTTPリクエストを送りたいという場面もあるでしょう。https://my-resource.jpから非同期でファイルを読み込む場合や、API接続を行う場合などが考えられます。

CORS(オリジン間リソース共有)が活躍するのは、まさにこのような場面です。CORSを設定することで「同一オリジンポリシー」が緩和され、望み通りの設計が実現します。

CORSの設定方法

ExpressでCORSを使用するためには、HTTPリクエストを受け取るサーバー側で設定が必要です。設定項目は次の3つあり、下のように記述します。

  • Access-Control-Allow-Origin: リクエストを受け取るドメインを指定
  • Access-Control-Allow-Methods: 受け取るHTTPメソッドを指定
  • Access-Control-Allow-Headers: 受け取るデータタイプを指定
app.js
const express = require("express");
const app = express();

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

// リクエストを受け取るドメインを指定
res.setHeader("Access-Control-Allow-Origin", "https://my-app.jp");

  // 受け取るHTTPメソッドを指定
  res.setHeader(
    "Access-Control-Allow-Methods",
    "GET, POST, PUT, PATCH, DELETE, OPTION"
  );

  // 受け取るデータタイプを指定
  res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
  next();
});

この設定によってサーバーが返すHTTPレスポンスにオリジンの情報が付され、クライアント側で受け取れるようになります。

CORSの基本的な実装方法は以上です。

helmetもCORSも、仕組みさえ分かってしまえば実装は難しくありません。大切なのは、それが何を目的とした機能であるのか、十分に理解した上で使用することです。

次のLessonでは、「Cookie」についての理解を深めましょう。