Lesson 10

セキュアなWEBアプリケーションの作成

Lesson 10 Chapter 1
クライアントなりすましの検証

本レッスンでは、Webアプリケーションのセキュリティについて学んでいきます。セキュアなWebアプリケーションを作るには、どのような攻撃があるのかを知り、その対策を学ぶ必要があります。今回は、「なりすまし」について扱います。

なりすまし攻撃とは

なりすまし攻撃には、さまざまな種類がありますが、その中でもセッションハイジャック攻撃について説明します。セッションハイジャック攻撃は、攻撃者が有効なセッションIDを盗み取ることで、正当なユーザーとしてサイトにログインし、不正な行動を行うことができます。

HTTPリクエストとセッションハイジャック

HTTPリクエストは、リクエストライン、ヘッダー、ボディの3つの部分で構成されています。リクエストラインは、HTTPメソッド(GET、POSTなど)とURLの組み合わせで、サーバーに何を要求するかを示します。ヘッダーは、リクエストに関する追加の情報を提供します。例えば、ブラウザの種類、言語、クッキー情報などが含まれます。セッションハイジャック攻撃では、攻撃者がリクエストヘッダーを偽装してセッションIDを盗み取り、正当なユーザーとしてサイトにアクセスします。

セッションIDとは

セッションIDとは、ウェブサイトにアクセスするユーザーごとに一意に割り当てられるIDのことです。ウェブサイトにアクセスすると、サーバーはそのユーザーに対して一意のセッションIDを生成し、そのIDをクッキーなどでクライアント側に送信します。その後、ユーザーが同じウェブサイトにアクセスするときに、そのセッションIDをもとにサーバーがそのユーザーのセッションを特定し、必要な情報を保持します。セッションIDは、ユーザーがログアウトする、ブラウザを閉じる、セッションの有効期限が切れるなど、ある一定の条件が満たされるまで有効です。

セッションハイジャック対策

セッションハイジャックを対策する方法はどのようなものがあるでしょうか。以下のような対策があげられます。

  • SSL/TLSの使用:HTTPSを使用することにより、通信が暗号化され、盗聴者が通信内容を盗み聞きすることができなくなります。これにより、セッションIDを含むHTTPリクエストヘッダーが暗号化され、セッションハイジャックを防止することができます。

  • セッションIDの暗号化:セッションIDを暗号化することにより、攻撃者がセッションIDを盗んでも、データを復号化することができず、セッションハイジャックを防止することができます。

  • セッションIDの有効期限の短縮:セッションIDの有効期限を短く設定することにより、攻撃者が有効なセッションIDを長期間使用することを防止することができます。ただし、ユーザーに不便を与える可能性があります。

以上が、セッションハイジャック攻撃とその対策についての説明です。ただし、攻撃手法は常に進化しているため、最新のセキュリティ情報にアクセスし、適切なセキュリティ対策を取ることが重要です。

Lesson 10 Chapter 2
CSRF

本チャプターでは、CSRF(Cross-Site Request Forgery)とその対策について、CORS(Cross-Origin Resource Sharing)とSpring Securityの観点から学んでいきます。

CSRFとは

CSRFは、攻撃者が悪意のあるサイトを作成し、ユーザーを誘導して別のWebサイトに不正な操作を行わせる攻撃のことを指します。このCSRF対策について学んでいきましょう。

CORSの仕組み

CSRF対策を理解する上で、異なるオリジン間でのリソース共有を可能にするCORS(Cross-Origin Resource Sharing)の仕組みを知ることが重要です。CORSは、ブラウザによって実装されており、同じオリジン以外からのリクエストをブロックします。これにより、CSRF攻撃の影響を軽減することができます。しかし、異なるオリジンからのリクエストを許可するために、CORSではオリジン間のリソース共有を明示的に許可する必要があります。

CORSは、以下のような仕組みで動作しています。

  1. ブラウザがHTTPリクエストを送信する際、リクエストヘッダーにオリジン情報を含めます。

  2. サーバー側が受け取ったリクエストに対して、Access-Control-Allow-Originなどのレスポンスヘッダーを返却します。

  3. ブラウザは、レスポンスヘッダーを確認し、アクセスが許可された場合にのみ、レスポンスを処理します。

また、CORSではリクエストをシンプルリクエストとそうでないリクエストに分類しています。シンプルリクエストは、条件を満たす場合に該当し、それ以外はプリフライトリクエストが使用されます。

プリフライトリクエストは、実際のリクエストの前に、OPTIONSメソッドでサーバーに送信されます。このリクエストを通して、サーバーがCORSに対応しているかどうかを確認し、実際のリクエストを送信することができるかどうかを判断する手順です。

これらの仕組みによって、CSRF攻撃を防ぐことができる場合がありますが、CORSだけでは十分なCSRF対策にはなりません。従って、より効果的なCSRF対策を行うためには、Spring Securityのようなセキュリティフレームワークを使用することが望ましいです。

Spring Securityを使用したCSRF対策

Javaにおける主要なフレームワークの一つであるSpring Frameworkには、セキュリティ強化のためのフレームワークとして、Spring Securityがあります。Spring Securityでは、CSRF対策が行われています。

Spring Securityはフォーム認証を使用する場合、セッションに対してCSRFトークンを生成し、トークンをHTMLフォームに含めます。リクエストのCSRFトークンが正しいかどうかを検証し、一致しない場合はリクエストを拒否します。

また、Spring Securityの挙動では、CSRFトークンはCookieとしても送信されます。これにより、ブラウザが自動的にCSRFトークンを送信し、セキュリティを確保できます。

このように、Javaにおける開発はCSRF対策を行っています。

Lesson 10 Chapter 3
XSS対策

本章では、XSS(Cross-site Scripting)とその対策について学びます。

XSSとは

XSSは、攻撃者が悪意のあるスクリプトを入力フォームなどに投稿することで、Webサイトにそのスクリプトが埋め込まれる攻撃手法です。

XSS攻撃には、一般的に以下の3つの種類があります。

  • Reflected XSS: ユーザーが特定のURLをクリックした際、攻撃者が用意したスクリプトを含んだリンクをクリックすることで起こります。リンクをクリックすることで、そのスクリプトが実行され、情報が盗まれたり、セッションを乗っ取られたりすることがあります。

  • Stored XSS: 攻撃者がウェブサイトにスクリプトを埋め込み、他のユーザーがそのサイトを訪問することで起こります。ウェブサイトには通常、コメント欄などの入力フォームがあるため、攻撃者はこれを利用してスクリプトを送信することができます。

  • DOM-based XSS: 動的なWebサイトでよく起こる脆弱性で、攻撃者がクライアントサイドのJavaScriptを操作して、サイトの機能を改ざんすることができます。

XSSを防ぐためには、以下のような対策があります。

レスポンスヘッダーの文字コード指定

HTTPレスポンスは、リクエストの結果として送信されるメタデータの集合であり、Webブラウザがページを表示する前に読み取られます。Content-Typeヘッダーは、HTTPレスポンスの一部であり、コンテンツの種類やデータのサイズ、キャッシュ期間、認証トークンなどが含まれます。Content-Typeヘッダーのcharset属性に、文字コードを指定することで、攻撃者が悪意のあるスクリプトを含んだコンテンツを送信しても、ブラウザが適切な文字コードで解釈することができます。

Servlet.java
response.setContentType("text/html;charset=UTF-8");

このように設定することで、ブラウザは明示的にUTF-8という文字コードで受信したことを認識し、正しく表示することができます。

HTML要素のエスケープ処理を行う

HTML要素を動的に生成する場合は、必ずエスケープ処理を行うようにします。Javaでは、org.springframework.web.util.HtmlUtilsクラスのhtmlEscapeメソッドを使用して、以下のようにHTMLエスケープを行うことができます。

Servlet.java
String escaped = HtmlUtils.htmlEscape(input);

サーバーサイドでの検証

フォームやAPIの入力値の検証は、主にサーバーサイドで行う必要があります。クライアント側での検証は簡単に回避できるため、サーバーサイドでの検証が必要です。検証には、入力値の検証ライブラリを使用したり、バリデーション処理を自前で実装することが一般的です。Javaの場合は、Spring Frameworkに標準で含まれるバリデーションライブラリ「Hibernate Validator」がよく使われます。

以上のようにXSS対策は、様々な観点から行うことができます。

Lesson 10 Chapter 4
DBの保護

本チャプターではDBの保護について扱っていきます。レッスン8で少し扱った、SQLインジェクションからDBを保護するための対策です。

SQLインジェクション

SQLインジェクションとは、悪意のあるユーザーがWebアプリケーションに対して、SQL文を注入することで、データベースに対する不正なアクセスやデータ漏洩を引き起こす攻撃手法です。

例えば、Webアプリケーション側で入力された値を直接SQL文に結合してしまう場合、攻撃者は意図しないSQL文を注入することができます。レッスン8で扱った例をもう一度見てみましょう。ログインフォームで入力されたユーザー名とパスワードを以下のようにSQL文に結合する場合、

Sample.java
String query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";

攻撃者は、以下のように入力することで、認証を回避することができます。

  • username: ' OR '1'='1

  • password: anything

これにより、以下のようなSQL文が生成され、認証が通ってしまいます。

SQL
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '' OR '1'='1'

このSQL文は、データベース上全てのユーザー情報を取得してしまいます。このような攻撃をSQLインジェクションといいます。

SQLインジェクションの対策としては、静的プレースホルダとエスケープ処理が挙げられます。

プリペアードステートメントと静的プレースホルダ

プリペアドステートメントとは、SQL文を事前にコンパイルしておくことで、実行時にパラメータをバインドし、繰り返し実行することができる仕組みです。これにより、パフォーマンスの向上や、SQLインジェクション攻撃の防止が期待できます。

静的プレースホルダは、プリペアドステートメントを使用してSQL文のパラメータをバインドする際に採用される方法です。具体的には、パラメータの値をSQL文内に直接記述するのではなく、?というプレースホルダを使用してSQL文内に埋め込みます。実行時にプレースホルダに対して具体的な値をバインドすることで、SQL文をパラメータ化し、SQLインジェクション攻撃を防止することができます。

以下は、静的プレースホルダを使用してパラメータをバインドするJavaのDAOの例です。

SampleDAO.java
String sql = "SELECT * FROM users WHERE id = ? AND password = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, userId);
ps.setString(2, password);
ResultSet rs = ps.executeQuery();

上記の例では、SQL文の中で?を使用してパラメータをバインドしています。そして、stmt.setInt(1, userId)のようにして、1つ目の?にuserIdの値をバインドしています。これにより、userIdにSQLインジェクション攻撃を仕掛けられても、SQL文には直接値が埋め込まれていないため、攻撃を防止することができます。

このように、SQL文を単なる文字列として扱い、文字列中に含まれる特定の文字が意味を持たないようにすることをエスケープ処理といいます。具体的には、特定の文字を特殊な文字列に変換することで、その文字がSQL文中で意味を持つことを防ぎます。 このように、エスケープ処理を適用することで、SQL文を安全に扱うことができます。

エスケープ処理

エスケープ処理には、上記に示した、静的プレースホルダを使った方法と、動的プレースホルダを使った方法があります。

動的プレースホルダを使った方法では、SQL文中にプレースホルダを設定する代わりに、文字列をエスケープしてSQL文に挿入します。エスケープ処理には、バックスラッシュ(\)やシングルクォーテーション(')などの文字を特殊な文字列に置き換える方法があります。

以下は、JavaのDAOで動的プレースホルダを使ったSQL文の例です。

SampleDAO.java
String sql = "SELECT * FROM users WHERE id = '" + escape(userId) + "' AND password = '" + escape(password) + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);

このコードでは、escape()メソッドでuserIdpasswordをエスケープしています。この関数は、引数の文字列に含まれる特殊文字やSQL文に影響を与える文字をエスケープ文字に置換することで、SQLインジェクション攻撃を防ぎます。例えば、シングルクォーテーションは'\'に置換されます。

ただし、動的プレースホルダを使った方法は、SQL文中に含まれる特殊な文字列を適切にエスケープしないと、エスケープ処理が不十分となり、SQLインジェクション攻撃を受ける可能性があります。そのため、可能であれば静的プレースホルダを使った方法を選択することが望ましいです。

このようにSQLインジェクションは、静的プレースホルダ、動的プレースホルダを用いて対策することができます。

Lesson 10 Chapter 5
アクセス制御

本チャプターではアクセス制御について学んでいきます。アクセス制御とは、Webアプリケーションにおいて、認証されたユーザーにしかアクセスを許可しないようにする機能のことです。アクセス制御を設定することで、不正なアクセスによるセキュリティリスクを軽減できます。

JSPでの直リンク対策

アクセス制御は、主に以下の2つの方法で行われます。

  • 認証

  • 認可

認証は、ユーザーが誰であるかを確認することです。一般的には、ユーザー名とパスワードの組み合わせを使って認証を行います。認証には、セッションやクッキーなどの機構を使って、ユーザーが認証済みであることを保持します。

認可は、認証されたユーザーがどのような操作を許可されるかを制御することです。例えば、管理者権限を持つユーザーには、すべての機能を使用できるようにするなどの制御が行われます。

JSPでの直リンク対策とは、URLを直接入力してページにアクセスした場合に、認証がされていない場合はログインページにリダイレクトするなどの対策を行うことを指します。具体的には、以下のような手法が用いられます。

  • フィルターの設定: フィルターを使用して、リクエストが送信された際に、ユーザーがログイン済みであるかどうかを判定します。ログインしていない場合は、ログインページにリダイレクトするように設定します。

  • ログイン済みかどうかのチェック: 各ページのアクセス時に、ログインしているかどうかをチェックする処理を実装します。ログインしていない場合は、ログインページにリダイレクトするようにします。

  • ディレクトリ構造の変更: ディレクトリの構造を変更して、直接アクセスできないようにします。例えば、jspファイルをWEB-INF以下に配置することで、直接アクセスできないようになります。

以上が、JSPでの直リンク対策です。

セキュアなWebアプリケーションを作成することは、利用者や企業やサービス提供者の損失を防ぐだけでなく、信頼性を高めるためにも非常に重要です。本レッスンで扱った対策忘れずに、アプリケーション開発を行いましょう。