Ⓜ️

Movable Typeで作るセキュアな企業サイト:静的CMSの真価

に公開

はじめに

Webサイトを構築する際、「セキュリティ」は特に企業サイトにおいて最優先されるべき要素の一つです。近年では、WordPressなどの動的CMSが主流となっていますが、それに伴いCMSに起因する脆弱性報告も見受けられるようになっています(参考:IPA 脆弱性レポート)。
本記事では、静的CMSであるMovable Typeの特性に着目し、なぜ企業サイトにおいて「静的生成」がセキュリティ面で有利なのか、実例や仕組みを交えながら解説します。

対象読者

  • 自社WebサイトやコーポレートサイトをCMSで構築・運用しているWeb担当者
  • CMS導入を検討しているシステム部門や開発者
  • WordPress等の動的CMSから静的CMSへの移行を考えている方
  • Movable Typeの採用メリットを理解したい方

静的CMSとしてのMovable Typeの特徴

1. 静的HTMLの出力により攻撃対象が限定される

Movable Typeは記事やテンプレートを保存したあと、HTMLファイルとして静的に出力します(Six Apart公式)。この方式では、Webサーバー上にはPHPやデータベースのような動的処理を行うコンポーネントが存在せず、外部から攻撃可能なインターフェースが大幅に減少します。

これは、一般的なWordPressサイトに見られる「PHPのリモートコード実行(RCE)」などの脅威を物理的に回避できる構造です。


2. 管理画面をWebサーバーと分離可能

Movable Typeでは、Web公開用サーバーと管理画面を動かすアプリケーションサーバーを分離する運用が可能です(出典)。これにより、公開環境にはMovable Type本体を設置せず、HTMLファイルのみを配置するセキュアな構成が実現できます。

この方式は、「公開環境が完全に読み取り専用」となるため、サーバーの乗っ取りリスクや情報漏洩リスクを極小化できます。


3. Web Application Firewall(WAF)を補完的に扱える構成

WAFはWebアプリケーションのセキュリティを補完する手段ですが、Movable Typeのように動的処理が原則発生しない静的サイトでは、攻撃面が限定的であるため、WAFの依存度を下げられる可能性があります
※ただし、WAFを完全に不要とするわけではありません。静的サイトであっても、CDN経由での悪意あるアクセス(たとえば大量アクセスによるDoSや、CloudFront経由のボットアクセス)をフィルタリングしたり、管理画面を公開している場合にはWAFによる保護が引き続き有効です。したがって「WAF依存度を下げられる」とは、Webアプリケーション層に対する攻撃対象が減少することでWAFによる防御コストや必要性が相対的に低くなる、という意味であり、完全なWAF不要構成を指すものではありません。


4. 静的生成のパフォーマンスと信頼性

静的ファイルはデータベース負荷やサーバー処理の影響を受けず、高速かつ安定した配信が可能です。これは、セキュリティとは直接関係ないようでいて、実際にはDDoS攻撃などに対する耐性の向上に寄与します。

さらに、Amazon CloudFrontなどのCDN(コンテンツ配信ネットワーク)と非常に高い親和性を持っており、HTML・CSS・JavaScript・画像ファイルなどの静的アセットをエッジロケーションにキャッシュすることで、世界中のユーザーに高速で安定した配信を実現できます(AWS公式ドキュメント)。


5. バージョン管理と監査性

Movable Typeには、テンプレートのバージョン履歴管理や再利用機能があります。ただし、Gitのような完全な履歴ベースのバージョン管理とは異なり、バージョン管理機能の範囲には制限があります。
実際の監査ログ(誰が・いつ・何を変更したか)を厳密に管理したい場合は、別途バージョン管理システムとの連携が必要です。


6. HTMLファイルの配信方式と運用の柔軟性

Movable Typeは、HTMLファイルを生成してから任意のWebサーバーへ手動または自動でアップロードする仕組みを採用しています([Six Apart公式](https://www.sixapart.jp/movabletype/solutions/staging-pack.html)。

主な配信方法は以下の通りです:

  • FTP/SFTPによる配信
  • rsyncやGitHub ActionsなどによるCI/CD連携
  • Amazon S3 + CloudFrontを使ったクラウド連携

これらにより、公開用サーバーと管理環境の完全な分離が可能となり、セキュリティ・運用効率ともに高い水準を実現できます。


6-1. CloudFront + S3 を活用したMovable Typeの活用例

Movable Typeで生成した静的HTMLファイルを、Amazon S3(Simple Storage Service)にアップロードし、Amazon CloudFrontを通じて配信する構成は、企業サイトにおける高セキュリティかつ高パフォーマンスな運用例として非常に有効です。

運用イメージ

  1. Movable Typeで記事やページを静的HTMLとして生成
  2. 生成したHTMLや画像、CSS、JavaScriptなどの静的ファイルをS3バケットへアップロード
  3. CloudFrontを設定してS3バケットをオリジンに指定し、世界中のエッジロケーションから高速配信を実現
  4. 管理画面は別サーバーで運用し、S3公開バケットには管理機能を持つシステムは一切置かない

MT S3 Transferプラグインとの連携
株式会社12Gridとカーリーブラケット株式会社が開発したMT S3 Transferプラグインを利用すれば、管理画面から直接S3バケットへファイルをアップロード可能です[1][2][3][5]。GUI操作で配信設定を完結できるため、運用の効率化に寄与します。

セキュリティ面のメリット

  • Webサーバーへの直接アクセスをなくし、攻撃対象を大幅に減少
    S3は静的ファイルのホスティングに特化し、動的処理は一切行わないため、従来のWebサーバーで起こり得る脆弱性(例:PHPのリモートコード実行など)リスクが排除されます。

  • CloudFrontのWAF連携やSSL/TLS終端により通信を安全に保護
    CloudFrontはAWS WAFと連携でき、悪意あるリクエストをフィルタリング可能です。また、HTTPS通信を強制することでデータの改ざんや盗聴を防ぎます。

  • S3バケットポリシーとIAM(Identity and Access Management)を使った厳格なアクセス制御
    公開用のS3バケットは読み取り専用に設定し、管理画面サーバーのみがファイルアップロードの権限を持つように細かく権限設計できます。

  • 攻撃経路の明確化
    S3バケットへの直接アクセスをブロックし、CloudFront経由のみを許可する設計を徹底することで、公開領域のセキュリティをさらに強化できます。

パフォーマンス面のメリット

  • CloudFrontのエッジキャッシュによりユーザーに近い場所から高速配信
  • DDoS攻撃に対する耐性強化(AWS Shieldとの併用も可能)
  • 大規模トラフィックにも柔軟に対応可能でスケーラビリティが高い
  • パフォーマンスチューニング
    Cache-Controlヘッダ(例:max-age=86400)を適切に設定することで、キャッシュ効率とオリジン負荷の最適化が可能です。

運用自動化のポイント

  • CI/CDツールやスクリプト(例:GitHub Actions、CircleCI、AWS CodePipeline)を使って、Movable Typeで生成されたファイルを自動でS3に同期・デプロイ
  • バージョニング機能をS3で有効にして、公開コンテンツの履歴管理やロールバックを容易にする

6-2. AWS CDKを使ったCloudFront + S3(OAC対応)構成の具体例(TypeScript)

AWS CDKを利用することで、CloudFrontのOAC(Origin Access Control)対応を含めたS3バケットとCloudFrontディストリビューションの構成をコードとして管理可能です。
例えば以下のようなTypeScriptコードでセキュアかつ最新の構成を宣言的に記述できます。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as iam from 'aws-cdk-lib/aws-iam';
import { RemovalPolicy } from 'aws-cdk-lib';

export class MovableTypeStaticSiteStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // S3バケット(静的ホスティング用)
    const siteBucket = new s3.Bucket(this, 'SiteBucket', {
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      versioned: true,
      removalPolicy: RemovalPolicy.RETAIN,
      encryption: s3.BucketEncryption.S3_MANAGED,
    });

    // CloudFront OAC設定
    const oac = new cloudfront.CfnOriginAccessControl(this, 'OAC', {
      originAccessControlConfig: {
        name: 'MovableTypeOAC',
        originAccessControlOriginType: 's3',
        signingBehavior: 'always',
        signingProtocol: 'sigv4',
      },
    });

    // CloudFrontディストリビューション
    // L1(CfnDistribution)でOACを指定
    const distribution = new cloudfront.CfnDistribution(this, 'Distribution', {
      distributionConfig: {
        enabled: true,
        defaultRootObject: 'index.html',
        defaultCacheBehavior: {
          targetOriginId: 'S3Origin',
          viewerProtocolPolicy: 'redirect-to-https',
          allowedMethods: ['GET', 'HEAD'],
          cachedMethods: ['GET', 'HEAD'],
          compress: true,
          // AWS公式マネージドポリシーリスト
          // https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html
          cachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', // CachingOptimized
          originRequestPolicyId: '88a5eaf4-2fd4-4709-b370-b4c650ea3fcf', // AllViewerExceptHostHeader
        },
        origins: [{
          id: 'S3Origin',
          domainName: siteBucket.bucketRegionalDomainName,
          originAccessControlId: oac.attrId,
          s3OriginConfig: {},
        }],
        viewerCertificate: {
          cloudFrontDefaultCertificate: true,
        },
        priceClass: 'PriceClass_100',
      },
    });

    // OAC使用時に必須のS3バケットポリシーを明示的に追加
    siteBucket.addToResourcePolicy(new iam.PolicyStatement({
      effect: iam.Effect.ALLOW,
      principals: [new iam.ServicePrincipal('cloudfront.amazonaws.com')],
      actions: ['s3:GetObject'],
      resources: [siteBucket.arnForObjects('*')],
      conditions: {
        'StringEquals': {
          'AWS:SourceArn': `arn:aws:cloudfront::${cdk.Aws.ACCOUNT_ID}:distribution/${distribution.ref}`
        }
      }
    }));

    new cdk.CfnOutput(this, 'CloudFrontURL', { value: `https://${distribution.ref}` });
    new cdk.CfnOutput(this, 'S3BucketName', { value: siteBucket.bucketName });
  }
}

6-3. GitHub Actionsでの自動デプロイ例

GitHub Actionsを用いて、Movable Typeで生成した静的ファイルをS3に自動同期し、CloudFrontのキャッシュを必要に応じてインバリデーションするワークフロー例です。

name: Deploy Movable Type Static Site to S3

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v3

    - name: Setup Node.js (必要に応じて)
      uses: actions/setup-node@v3
      with:
        node-version: '18'

    - name: Build / Generate static files
      run: |
        # Movable Typeの静的生成コマンドがあればここで実行
        echo "Assuming static files exist in ./public"

    - name: Configure AWS Credentials
      uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ap-northeast-1

    - name: Sync files to S3
      run: |
        # OAC使用時はS3バケットポリシーを更新
        aws s3api put-bucket-policy --bucket your-bucket-name --policy file://bucket-policy.json
        # ファイル同期
        aws s3 sync ./public/ s3://your-bucket-name/ --delete --cache-control max-age=86400 --exact-timestamps

    - name: CloudFront Invalidate Cache (optional)
      run: |
        aws cloudfront create-invalidation --distribution-id YOUR_DISTRIBUTION_ID --paths "/*"
  • AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYはGitHubリポジトリのSecretsに登録します。
  • your-bucket-nameYOUR_DISTRIBUTION_IDは適宜置き換えてください。
  • 必要に応じて静的ファイルの生成コマンドを記述してください。

OACを使用する場合、S3バケットポリシーにCloudFrontディストリビューションARNを明示的に許可する必要があります。例:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Service": "cloudfront.amazonaws.com" },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::your-bucket-name/*",
    "Condition": {
      "StringEquals": { 
        "AWS:SourceArn": "arn:aws:cloudfront::1234567890:distribution/EXAMPLE123" 
      }
    }
  }]
}

CloudFrontのキャッシュインバリデーションはコストがかかるため、実施頻度は考慮しましょう。


補足:動的生成への対応について

Movable Typeは「静的CMS」として知られていますが、一部の機能では動的生成にも対応しています。たとえば、コメント投稿や検索機能など、リアルタイムな処理が必要な部分は動的にページを生成することが可能です。出典:Six Apart機能説明
ただし、こうした動的機能も必要最小限の範囲で限定的に利用されるよう設計されており、攻撃対象面が大きく広がることはありません


補足:認証ロックアウト機能による不正ログイン対策

Movable Typeには、認証ロックアウト機能が搭載されています。これは、管理画面やコメント認証画面などで、一定回数以上ログインに失敗した場合にアカウントを一時的にロックし、不正アクセス(辞書攻撃や総当たり攻撃)を防止するものです(公式ドキュメント)。

  • 標準設定では、1800秒間(30分)に6回以上ログインに失敗すると、そのユーザーのアカウントがロックされます。
  • また、同一IPアドレスから1800秒間に10回以上ログインに失敗した場合も、そのIPアドレスからのアクセスが一時的に禁止されます。
  • ロックアウト条件や解除方法は環境変数でカスタマイズ可能で、ロック解除通知も自動送信されます。

この機能により、仮に管理画面のURLが漏洩した場合でも、機械的なパスワード総当たり攻撃からアカウントを守ることができます。


まとめ

企業Webサイトにとって、セキュリティは単なる技術課題ではなく信用とブランドの根幹です。
Movable Typeは、静的CMSとしての構造上の優位性により、動的CMSに比べて外部からの攻撃リスクが著しく低いサイト構築が可能です。

とりわけ以下の点が企業利用において強みとなります:

  • 静的HTML出力による攻撃対象面の縮小
  • 公開サーバと管理画面の完全分離構成
  • WAF依存度を下げられるセキュア設計
  • Amazon CloudFrontとの高い親和性と高速表示
  • バージョン管理とガバナンス強化
  • 柔軟かつ安全なHTMLファイル配信手段
  • 動的生成機能の限定的な利用によるセキュリティ担保
  • 認証ロックアウト機能による不正ログイン防止策

セキュアで持続的な運用を重視する企業にとって、Movable Typeは再評価されるべきCMSの一つといえます。


出典一覧:

株式会社グローバルネットコア 有志コミュニティ(β)

Discussion