Gatsby(React)とXSS

Mar 28, 2021 17:40 · 1717 words · 4 minute read React Gatsby Security

最近「React & GatsbyJS開発入門」を見ながら、ReactとGatsbyを触っています。

React & GatsbyJS開発入門 - mottox2(つのぶえ出版) - BOOTH

Gatsbyでは、「外部サイトのRSS」や「HeadlessCMS」などからデータを取得し、取得したデータを組み込んだ静的サイトをビルドすることができます。
外部から受け取ったデータを使ってコンテンツを組み立てるとなると、試してみたくなるのがXSS(Cross-site Scripting)です。
ということで、GatsbyでXSSが発生するのか検証してみました。

目次

XSS(Cross-site Scripting)とは?

「外部から入力されたデータを使ってコンテンツを作成する」機能を悪用する攻撃手法です。
外部から「悪意のあるデータ」を入力された場合、Webアプリ上でスクリプトが実行されたり、表示内容が改ざんされてしまう可能性があります。

安全なウェブサイトの作り方 - 1.5 クロスサイト・スクリプティング

XSSについてもっと知りたい方は、安定の徳丸本がおすすめです。

検証内容

今回はGolangのHugoで生成されたRSSを、Gatsbyの「gatsby-source-rss-feed」というプラグインで読み込みます。
「外部から受け取ったスクリプトを使って作成したコンテンツ」でXSSが発生するか確認します。

全体像

環境構築

早速検証環境を作っていきます。

Hugo

まずはHugoの環境を作ります。

今回はanankeのThemeを使ってみました。

$ hugo new site hugo-malicious-site
$ cd hugo-malicious-site/
$ git init
$ git submodule add https://github.com/budparr/gohugo-theme-ananke.git themes/ananke
$ echo 'theme = "ananke"' >> config.toml

下記のコンテンツを作成しました。

$ hugo new sample-post.md

$ hugo new sample-post2.md

出来上がったindex.xmlです。

Gatsby

続いてGatsbyの環境を作ります。

$ npm init gatsby
$ npm run develop

Hello Gatsby!

HugoのRSSを読み込む処理を追加します。

プラグインを追加

$ npm install gatsby-source-rss-feed

gatsby-config.jsにプラグインの情報を追記

module.exports = {
  siteMetadata: {
    title: "Gatsby-XSS-Sample",
  },
  plugins: [
    {
      resolve: `gatsby-source-rss-feed`,
      options: {
        url: `http://localhost:1313/index.xml`,
        name: `MaliciousSite`,
      },
    },
  ],
};

データが読み込めているか、GraphiQLで確認します。

$ npm run develop

Hugo側で設定したスクリプトを読み込めていることが確認できました。

読み込んだスクリプトを画面上に表示する処理を実装します。
今回は「/src/pages/index.js」で下記のように読み込みました。

import * as React from "react";
import { graphql } from "gatsby";

const LinkItem = (props) => {
  const { title, link } = props.post;
  return (
    <li>
      <a href={link}>{title}</a>
    </li>
  );
};

const IndexPage = (props) => {
  return (
    <ul>
      {props.data.allFeedMaliciousSite.nodes.map((post) => {
        return <LinkItem post={post} key={post.link} />;
      })}
    </ul>
  );
};

export const query = graphql `
query MyQuery {
  allFeedMaliciousSite {
    nodes {
      title
      link
    }
  }
}
`;

export default IndexPage;

検証環境情報

検証実施時点の環境情報はこちら。

  "dependencies": {
    "gatsby": "^3.1.2",
    "gatsby-source-rss-feed": "^1.2.2",
    "react": "^17.0.1",
    "react-dom": "^17.0.1"
  }

検証結果

それではいよいよ検証です。

$ npm run develop

で開発サーバを起動し、ブラウザから表示内容を確認します。

確認した結果がこちら。
Hugoから読み込んだスクリプトは実行されませんでした。

Gatsbyの公式ドキュメントによると、JSXに含まれるHTMLタグは自動でエスケープ処理されます。

Security in Gatsby

ただし、外部から受け取ったデータを扱う場合には気をつけないといけないパターンもあるようです。

  • aタグのhref属性に「javascript:」から始まる文字列でスクリプトを渡した場合
  • dangerouslySetInnerHTMLを使った場合

どうしてもこのような処理をする必要がある場合、公式ドキュメントでは「sanitize-html」や「DOMPurify」を使うことを薦めています。

sanitize-html

DOMPurify

まとめ

Gatsbyを利用する場合、「href属性」と「dangerouslySetInnerHTML」に気をつければXSSが発生する可能性は低そう。
(まだ発見されていない攻撃パターンが存在する可能性もあるので、”低そう”としています。)

参照情報

Security in Gatsby

Reactで発生しうるXSS脆弱性

最近のJavaScriptフレームワークでのXSS

React における XSS

tweet Share