webプログラミング - gnst.okayama-u.ac.jp · 1.webプログラミング概要 (8)...
TRANSCRIPT
WEBプログラミング
(抜粋資料)
OKIソフトウェア 岡山支社小野晋二
岡山大学工学部ITCソフトウェアプロフェッショナルコース
1.WEBプログラミング概要
(1) テーマ
■実習プログラミングしてみよう!動作させてみよう!データベースをさわってみよう!
■ セキュリティWEBアプリケーションならではの危険を知って、その対処方法を実装していく
■ ASP.NET(.NET Framework)とC#、SQL Server細かい文法は気にせず、新しいプログラミング環境でプログラムを作っていく。最新のプラットフォームを調査しながらコーディングする。オブジェクト指向やデザインパターンなども取り入れていければよい。
※サーバ側プログラミングを主な対象として、以下は扱わない・ページデザイン(HTML)・JavaScript、VBScript・NWのセキュリティ
1.WEBプログラミング概要
(2) 実習環境
今回の実習環境ではクライアント(ブラウザ)~Webサーバ~DBを単一のPC内に同居させる。
WindowsXPWindowsXP
.NET Framework 3.5 .NET Framework 3.5
InternetExplorer
ASP.NET
SQL Server
2005 Express
SQL Server
2005 Express
WEBアプリケーション(AP)(C#)
WEBアプリケーション(AP)(C#)
SQL ServerManagement Studio
Express
Visual WebDeveloper
2008 Express
開発ツール
(テスト用)
1.WEBプログラミング概要
(3) C#
C#から他のプログラミング言語をみると・・・
C
C++
Java
Visual Basic
1.WEBプログラミング概要
(4) WEBアプリケーションとは
Webアプリケーションは、インターネットまたはイントラネット上でWebブラウザを使用してアクセスするアプリケーション
■特長・運用管理の容易さ・Webアプリケーションフレームワーク が使用される。
■ ソフトウェアとしては・ソフト実行環境とGUIの分離。(その間にはNWが存在する)
細かなGUI制御は不得意。(リアルタイムのGUIのサーバ制御は不向き。
2重POST、戻るボタン対策などが独特)ブラウザの終了も検出できない。
(サーバ側リソース解放のタイミング)
・クライアント環境も多様。・OS/ブラウザ/設定・・・・・悪意のユーザ
1.WEBプログラミング概要
(5) WEBアプリケーションの動作と流れ
① ユーザがデータ入力してボタンクリック
② ブラウザがHTTPリクエストを生成
③ WEBサーバへHTTPリクエスト送信
④ WEBサーバ内でのプログラム実行
⑤ WEBサーバがHTTPレスポンスを生成・送信
1.WEBプログラミング概要
(6) HTTP通信の概要
■HTTP通信では通常ブラウザからの操作により、TCP上での接続が始まり、画面情報の送受信が終了するとTCP通信は切断される。
(ラウンド・トリップ)
接続
切断
リクエスト(入力情報・ブラウザ情報・・・)
レスポンス(画面情報(html)、画像など)
クライアント(ブラウザ)
WEBサーバー
1.WEBプログラミング概要
(7) 最終到達目標
こんな感じで画面(ページ)が変わっていくシステムを作ってみる。
ログイン画面
フィールド:IDパスワード
ボタン:ログイン
メニュー画面
フィールド:(なし)
ボタン:情報編集 ログアウト
情報編集画面
フィールド:ユーザ名(表示)所属部署コード(表示・入力)備考(表示・入力)
ボタン:変更、戻る
入力確認画面
フィールド:ユーザ名(表示)所属部署コード(表示)備考(表示)
ボタン:確認 戻る
情報編集
戻る
ログアウトログイン
戻る
変更
確認
1.WEBプログラミング概要
(8) 実習1-1:WebSite01 新しいサイトHello Worldの作成
①Visual Web Developer 2008 Expressの起動最初の起動は数分かかる
②新規WebSiteの作成(名前はWebSite01)ファイル - 新しいWebサイトASP.NET Web サイト ファイルシステム C# WebSite01
③画面(ページ)作成(『Hello world !!』を配置)デザインモードにして以下配置
先頭行に WebSite01 Hello world !!TextBox プロパティウィンドウでIDをTb01に変更Label×3 IDはLb01~03Button IDはBtExec
何もないところをクリックして<Document> のTitleプロパティを『Hello world!!』を設定。
1.WEBプログラミング概要
(8) 実習1-1:新しいサイトHello Worldの作成
④ Page_Load・Page_Loadメソッドにロジックを記述してみる
if (IsPostBack) {
Lb02.Text = “PostBack :“ + Tb01.Text ;}else{
Lb02.Text = “not IsPostBack“ ;}
string labelform = "現在の時刻は{0}です。";Lb01.Text = String.Format(labelform, DateTime.Now) ;
⑤ ビルド・デバッグ起動⑥ ブレークポイントの設定と動作⑦ IsPostBack属性による分岐
・Page_Loadメソッドに以下ロジックを追加記述してステップ実行してみる。
1.WEBプログラミング概要(8) 実習1-1:新しいサイトHello Worldの作成
⑧ ボタンクリックイベントの作成と記述デザインモードでボタンをダブルクリック。BtExec_Clickメソッドが生成されるので、処理を記述。
⑨ テキストチェンジイベントの削除デザインモードで不用意にTextBox等をダブルクリックすると不要なメソッドが生成される。不要なメソッドは削除する。削除するだけではビルドエラーとなるので、aspxの該当フィールドのOnTextChanged=“<イベント・ハンドラ名>”の部分も削除する。
Lb03.Text += “ボタン” ;
2.ログ取得
(1) ログ取得の目的
ログ取得の目的を明確にしておく。【前提】①主に誰がみるのか
開発者 or システム管理者(システム的な管理者 or 業務的な管理者)→ 今回の場合は開発者
② 何のために今回の場合はソフト開発の調査と障害時の解析に使用。(仮想的に)設計、デバッグ、テスト、本稼動時に使用する。
③ ログ取得項目プログラム不具合等の調査に必要な項目とし、業務上の秘密項目は取得せず、一般的に社外、部門外に持出し可能な情報のみを取得する。
④ ログファイル単位/容量今回の場合は1日1ファイルとして、削除や移動の仕組みは不要。容量も問題にしなくてよい。
2.ログ取得
(2) StreamWriterを利用したログ取得
ログ取得クラスを作成する。■ ボタンクリックの動作でログ取得する。【呼出方法】
LogWriter.WriteLine(“デバッグ”) ;
■ StreamWriterの使用例
//ログファイルへ追加書き込みStreamWriter writer = File.AppendText(“website01log.txt");
//Text in the file!という文字列を書き込みする指定writer.WriteLine("Text in the file!");
//ファイルへ書き込みwriter.Flush();
//ファイルのロックを解除writer.Close();
2.ログ取得
(3) 実習2-1:WebSite02 ログ取得
① WebSite01のコピーエクスプローラでのコピー(デフォルト)マイドキュメント¥Visual Studio 2008¥WebSitesWebSite01フォルダをフォルダごとコピーフォルダの名前をWebSite02に変更
② Web Developer の メニューファイル – WebSiteを開くWebSite02を選択してOKページ先頭行の『WebSite01 ・・・』 を 『WebSite02 ログ取得』に変更
③ Default.aspx.csにボタンクリックイベントに以下を追加
LogWriter.WriteLine(”ボタンクリック”) ;
2.ログ取得
(3) 実習2-1:ログ取得
④ Default.aspx.csでクラス宣言より前に以下のディレクティブを追加する。
using System.IO ;
⑤ Default.aspx.csに以下のようなクラスを追加する。(参考)(コメント行は自分で追加)
⑥ C:ドライブ(または適切なドライブ)にlogフォルダを作成しておく。
public class LogWriter{
public static void WriteLine(string text) {
StreamWriter writer = File.AppendText(@"c:¥log¥this.txt");writer.WriteLine(text);writer.Flush();writer.Close();
}}
・ファイル名、フォルダ名は自分の環境に合せる。・@“~”は『¥』をエスケープしない。
2.ログ取得
(4) ログ取得機能の問題点(疑問点)と改善
① C:¥logフォルダがないときやIOエラー発生時に落ちるのではないか?→ IOエラー発生時に例外が発生し、システムエラー(続行停止)となる。→ 例外発生時はログ取得しないようにする。
② 常に同じファイルでファイルは大きくなる一方で取り扱いが難しくなる。→ファイル名を日付入りにする。
③ 複数のユーザからのアクセス時、競合が発生しログ取得できない。→複数のスレッドからの依頼で競合が発生しないようにする。
④ ログファイルができるディレクトリが固定になっている。→いろいろな環境でもプログラムを変更することがないように
ログ取得ディレクトリを設定ファイル(Web.config)で設定する仕組みとする。
⑤ 画面の制御ロジックと同じソースファイルになっており、他からの利用ができない。→複数の画面から呼ばれてもよい形式にする。
⑥ 共通的な情報を出力した方がよい。→ 時刻とセッションIDを共通的にログ出力する。
2.ログ取得
(5) 改善1:別のソースファイル
・ファイル→新しいファイルテンプレート=クラス名前=LogWriter.cs言語=Visual C#
※ 複数のページで共通で使うコード(クラス)はApp_Codeフォルダに配置する。ASP.NETではApp_CodeフォルダにあるコードはWeb アプリケーションの他のすべてのコードがアクセスできる。
2.ログ取得
(6) 改善2:IOエラー発生時の対処
例外はtry ・・・catch・・・で捕捉する。(C++/Javaと同様)
try {処理A処理B処理C
}catch (Exception e) {
例外処理}処理D
この記述で処理Bで例外が発生した場合、例外が捕捉され、処理Cは実行されずcatchブロックの例外処理が実行される。catchブロックで例外を処理した後、通常は処理Dが実行される。
2.ログ取得
(6) 改善2:IOエラー発生時の対処
例外ハンドラの慣習・C++では、
IOエラーや予測できない障害に対してのみ使用する場合が多く、業務的な異常などは関数、メソッドのリターン値などで通知する場合が多い。
・Javaでは、IOエラーから業務的なエラーまで広範囲に正常系以外に使用される。
・C#では業務エラーまで含めて例外ハンドラを使用することが推奨される。
ログ出力エラー時の処理
// 例外発生時の場合はデバッグコンソールに出力catch (Exception e){
System.Diagnostics.Debug.WriteLine("LogWriter[StreamWriter] " + e.Message);
}
※予測可能な例外で処理を続行する場合は、できるだけ事前に検査し、catchブロックに入らせない。(null文字列ならばゼロに扱う場合など)
2.ログ取得
(7)改善3:ファイルの競合の防止
① Singletonパターンの利用
http://www.microsoft.com/japan/msdn/practices/type/Patterns/enterprise/impsingletonincsharp.aspx
public class Singleton {private static Singleton instance;private Singleton() { } public static Singleton Instance {
get { if (instance == null) {
instance = new Singleton(); }return instance;
} }
}
※instance == nullを判断する時に他のスレッドからの依頼でSingleton作成中だったら?
2.ログ取得
(7)改善3:ファイルの競合の防止
① Singletonパターンの利用 (instance == null 同時実行の対応策)
public class Singleton {private static Singleton instance;private static Object lockObj = new Object();private Singleton() { } public static Singleton Instance {
get { lock(lockObj) {
if (instance == null) {instance = new Singleton();
}}return instance;
} }
}
※ 毎回lockするのは処理能力上好ましくない。(lock処理は“コスト”のかかる処理)
2.ログ取得
(7)改善3:ファイルの競合の防止
① Singletonパターンの利用(ダブルチェックロッキング手法)
public class Singleton {private static volatile Singleton instance;private static Object lockObj = new Object();private Singleton() { } public static Singleton Instance {
get { if (instance == null) {
lock(lockObj) {if (instance == null) {
instance = new Singleton(); }
}}return instance;
} }
}
変数instanceの宣言にvolatile修飾がない場合、上位ブロックで判定済みのためにこのif判定は実行されない可能性がある。(コンパイラの最適化)
2.ログ取得
(7)改善3:ファイルの競合の防止
① Singletonパターンの利用(静的初期化によるインスタンス生成)
静的初期化(ダブルチェックロッキングより簡単。C#では通常この方法でよい)
※static変数の初期化は同時に複数のスレッドでは実行されない!!
ただし、Singletonにしたからといって同時に実行されないということではない!!
public class Singleton {private static readonly Singleton instance = new Singleton();private Singleton() { } public static Singleton Instance {
get { return instance;
} }
}
2.ログ取得
(7)改善3:ファイルの競合の防止
② Lockオブジェクトの利用
lock キーワードは、指定のオブジェクトに対する相互排他ロックを取得し、ステート
メントを実行し、ロックを解放するステートメント ブロックをクリティカル セクションとしてマークするもの。
ただし、lockするobjectの実体が同じインスタンスでないとlockは期待どおり動作しない。→lockするobjectはstatic objectまたはSingleton(またはそのメンバの)object
とする。(static宣言時に静的初期化された値をもつobject)
lock (object){
// この間は 同時に実行されない。}
private static object objLock = new object() ;
2.ログ取得
(8)改善4:ログファイル名とディレクトリ
通常APの設定値はweb.configの< appSettings >に格納する
<configuration><appSettings>
<add key=“LogPath" value=“C:¥log¥{0}.log" /></appSettings>
</configuration>
System.Collections.Specialized.NameValueCollection appSettings = System.Web.Configuration.WebConfigurationManager.AppSettings ;
if (appSettings != null) {fileForm = appSettings[ConstData.KeyLogFile];
}
public class ConstData {public const string KeyLogFile = "LogPath";
}
■アクセス
■文字列定数定義
Web.ConfigWeb.Config
ConstData.csConstData.cs
2.ログ取得
(9)改善5:時刻の出力
① 改行なしでの出力StreamWriterのメソッド Write と WriteLineの差異WriteLine() → 文字列出力後、改行する。 Write() → 文字列出力のみ。
② 時刻の出力通常msec単位まで出力すればよい。
DateTime nowTime = DateTime.Now;Write(nowTime.ToString("HH:mm:ss.fff ")) ;
③ 複数のユーザから同時に依頼があった場合も考慮し、SessionIDも取得する。
if (HttpContext.Current.Session != null) {
Write(HttpContext.Current.Session.SessionID +" ");}
※CurrentのSessionプロパティはタイミングによってnullのこともある。elseのとき、通常SessionIDの長さ分のSpace等を出力する。(見易さのため)
2.ログ取得
(10)実習2-2:WebSite03 ログ出力改善
① 前回のWebサイト(WebSite02)をフォルダごとコピー→フォルダ名の変更⇒WebSite03
② メニュー – ファイル – Webサイトを開く
ページ先頭行の変更 『 WebSite03 ログ出力改善』
③ 以下を改善を実装・複数のページで共通に使えるように別のソースファイルにする。・IOエラーが発生しても例外が発生しないようにする。・複数のユーザからのアクセス時、競合しないようにする。・ログファイル名を日付入りにする。・ログファイルのフォルダをweb.configで設定できるようにする。・呼ばれる度に行頭に時刻、SessionIDを出力する。
3.DBアクセス
(1) Web DeveloperからのDB接続
データベースエクスプローラでの接続
データ接続 右クリック接続の追加
接続文字列の調査
テーブル→(各テーブル)→テーブルデータの表示
OrganizationInfoPersonalLoginInfoPersonalProfile
各テーブルのテスト用データを確認
3.DBアクセス
(2) DBからの読込プログラム
SqlDataAdapterの使用(例) DB読込の簡易プログラム(エラー処理なし)
using System.Data;using System.Data.SqlClient;using System.Data.Common;using System.Configuration;
DataSet ds = new DataSet();string conn = 接続文字列SqlConnection dbconn = new SqlConnection(conn);dbconn.Open();SqlDataAdapter adapter = new SqlDataAdapter();SqlCommand cmd = new SqlCommand();cmd.Connection = dbconn;cmd.CommandText = SQL文字列(select文);cmd.CommandType = CommandType.Text;adapter.SelectCommand = cmd;int line = adapter.Fill(ds);dbconn.Close();
3.DBアクセス
(2)DBからの読込プログラム
■DataSetとはメモリ空間上に存在するDataTableの集合体DataTable:メモリ空間上に存在する行と列をもったデータの集合体
(DB内のTableをメモリ上に構築したものと考えられる)
■SqlDataAdapterの役割DB(SQL Server)とDataSetを仲介するモジュール
■GridViewでの表示DataSourceプロパティの設定=結果のDataSetの0番目のTable(Tables[0])DataBind()の実行
※ViewStateに注意する
3.DBアクセス
(3)DB接続文字列
■接続文字列の格納web.configの<connectionStrings>に格納する。(文字列が全く同じときFramework内でリソースを共用するため、同一の接続の
ときは1文字も異ならないようにするべき)※web.configの例
<configuration>・・・・・・<connectionStrings>
<add name=“接続名” connectionString=“接続文字列”/></connectionStrings>・・・・・・
</configuration>
■接続文字列の取得方法
System.Configuration.ConnectionStringSettingsCollection connStrings= System.Web.Configuration.WebConfigurationManager.ConnectionStrings;
if (connStrings[“接続名”] != null) {接続文字列 = connStrings[“接続名”].ConnectionString;
}
異なる接続名で複数セットあっても可。
3.DBアクセス(4) 実習3-1:WebSite04 DBテーブル値の参照(選択)
① 新しいWebサイト WebSite04App_Codeフォルダの必要なファイルのコピーweb.configファイルでのログファイル関連設定のコピー
② 画面コントロール
・TextBox ID:TbSql SQL文入力エリア・Label ID:LbResultCode 結果・GridView ID:GvResult 選択結果の表示エリア
(ツールボックスのデータ)・Button ID:BtExec 実行ボタン
③ ButtonをダブルクリックしてButtonExec_Clickイベントの処理として実装・入力したSQLを実行し、結果をGridViewに表示・例外には対処し、メッセージを表示する。
3.DBアクセス
(5) DBの更新(Insert/Update/Delete)
■SqlCommand. ExecuteNonQuery()・SqlDataAdapter + SqlCommandでも更新可能・SqlDataAdapterなしでも更新可能(見通しをよくする)
SqlCommandのConnection/Transaction/CommandText/CommandTypeの各プロパティを設定して、 ExecuteNonQuery()を実行
■トランザクションDBの内容を変更するときはトランザクション管理を行う(単一の更新でもなるべく使用する
CommitもRollbackもしないと接続終了時にRollbackされるトランザクションはなるべく上位層で管理する(モデル層最上位)
DbConnection dbconn = new SqlConnection(接続文字列);dbconn.Open();DbTransaction tr
= dbconn.BeginTransaction(IsolationLevel.ReadCommitted);SqlCommand.Transaction = (SqlTransaction)tr・・・・・・(更新)tr.Commit() ; //ここまでで例外が発生したら tr.Rollback() ;
トランザクション分離レベル。データの一貫性や処理能力を検討して決定する。
3.DBアクセス
(6) クラス構成の例
Page(aspx)
System.Web.UI.Page
BasePage
各業務画面ページ
ビュー層
コントローラ層
画面固有の業務ロジック
業務データ更新① 業務データ更新②
DBアクセスObject
モデル層
画面デザイン
.NET Frameworkの用意するPage基本クラス
.Pageコントローラ設計モデルの中核
各画面の共通処理、テンプレートロジック
各画面の固有の入力制御、エラー処理、画面遷移
各業務のデータ更新の制御DB接続、トランザクション制御、処理結果作成
DBアクセスの共通処理(DAOパターン)
3.DBアクセス
(7) 実習3-2:WebSite05 DBテーブル値の更新
① Webサイトのコピー WebSite04 → WebSite05 (ページ先頭行の変更)② 画面コントロール
・TextBox ID:TbSql SQL文入力エリア・Label ID:LbResultCode 結果・GridView ID:GvResult 選択結果の表示エリア
(ツールボックスのデータ)・Button ID:BtExec 実行ボタン
③ ButtonをダブルクリックしてButtonExec_Clickイベントの処理として実装・SqlCommand のExecuteNonQuery()によるデータ更新・トランザクションを管理し、例外発生時にはRollBack()する。
(正常時はCommit()する)
3.DBアクセス(8) 実習3-3:WebSite06 ログイン処理
① 新しいWebサイト – WebSite06App_Codeフォルダの必要なファイルのコピーweb.configファイルでのログファイル関連設定のコピー
② ログイン画面(Default.aspx)・TextBox TbID ログインID・TextBox TbPassword パスワード入力用(表示抑制)・Label LbError エラー表示用・Button BtnLogin ログインボタン(本実習ではツールボックスのログイン用コントロールは使用しない)※<Document>のタイトルは『ログインページ』
・IDとパスワードが一致する行が1行のみ存在した場合にメニュー画面に遷移。(ページ内のロジックでServer.Transfer(“ページ名”)を実行)
・DBアクセス時等のエラー処理は実装する。・接続文字列はweb.configから取得する。・エラー発生時はログイン画面にエラー内容を表示する。・処理階層は分離しなくてもよい(してもよい)。※SQLはログ取得する。
3.DBアクセス(8) 実習3-3:WebSite06 ログイン処理
③ メニュー画面(Menu.aspx)(新しいファイル – Webフォーム – Menu.aspx)
・Button BtnLogout Logoutボタンボタン押下でLogin画面に遷移(Sever.Transfer (”default.aspx”) )する
※<Document>のタイトルは『メニューページ』
・Webサイト – 開始オプションでスタートページを設定
3.DBアクセス
(9) ログイン処理(WebSite06)のセキュリテイ上の問題点
■WebSite06が一般向け対象であった場合、セキュリティ上の問題点が複数ある。どのような問題点があるか?(ネットワーク経路上の問題は除く)
※DB設計やこれまでに提示したサンプルから引継ぐ問題点も含む
3.DBアクセス
(9) ログイン処理(WebSite06)のセキュリテイ上の問題点
■WebSite06が一般向け対象であった場合、セキュリティ上の問題点が複数ある。どのような問題点があるか?(ネットワーク経路上の問題は除く)
① SQLインジェクション→ 5項にて説明
② ログイン管理(セッション管理)いきなりMenu.aspxを表示できる。
→ 7項にて説明
③ パスワードDBテーブル上にパスワードがそのままの形である。ログにもパスワードがそのままの形で取得されている。大文字小文字の区別をしていない。
→ 6項にて説明
④ エラーメッセージFrameworkのエラーメッセージをそのまま出力している。
→ 4項にて説明
4.エラーメッセージ
(1) エラーメッセージ
OSやFrameworkの出力するエラーメッセージは可能な限り、そのまま出力しない。特にFrameworkのエラー画面をそのまま表示しないように注意する。
■理由悪意のユーザには必要以上の情報を与えることになる。(また、通常の善意の操作者には理解しづらい情報である。)
・使用しているOSやプラットフォームの種類、バージョンを知られる。→ プラットフォームの脆弱性にたどりつきやすい
・エラーメッセージによりシステム構成、ソフト構成、プログラム内容を知る手がかりとなる。
→ 攻撃のための情報となる。
4.エラーメッセージ
(2) エラー表示の対策
■対策・画面表示メッセージはエンドユーザには必要充分なメッセージとする。
ただし、ログにはできるだけ詳細な情報を取得する。
(例) 「DB接続文字列が初期化されていません」→ 「ご迷惑をかけますが、システム障害により処理続行できません。」
・Frameworkのエラー画面は表示させないようにする。Global.asaxのApplication_Errorイベントを捕捉し、自前のエラー画面等にジャンプさせる方法など
→今回は実装しない。
5.SQLインジェクション
(1)実習5-1:SQLインジェクション
WebSite06にSQLインジェクションしてみてください
■前提・プログラム内容やDB構造は知っている・IDは知っている・パスワードは知らない
一般ユーザとしてログイン画面からSQLインジェクションによりメニュー画面へ遷移させる
5.SQLインジェクション
(2)WebSite06のSQLインジェクション
入力 ID:00001 (正しい値)パスワード:‘ OR 1=1 AND ID=‘00001
SELECT ・・・WHEREID=‘00001’ AND Password=‘‘ OR 1=1 AND ID=‘00001’;
選択結果は1行のためOK!!!
■もっとひどいことに・・・入力 ID:any
パスワード:‘ ; delete from PersonalProfile where 1=1 or ID=‘
プログラム:次のSQLの結果の選択行が1行ならOK
SELECT ・・・WHERE ID=‘入力ID’ AND Password=‘入力パスワード’ ;
5.SQLインジェクション(3) 対策:入力値のチェック
扱う文字種類、文字数を決めておきチェックする。(例)商品コード:ABC-123456
<条件>・固定長の10文字で構成される・先頭3文字は大文字のアルファベット・4文字目はハイフン「-」・残りの6文字は数字
不正な内容でシステムの動作に問題を起こす可能性があるデータのチェックはクライアント(ブラウザ)側で行ってもよいが、サーバー側では必ず行う。
(クライアント側のみのチェックでは安全性は確保されない)悪意の操作に対応するにはドロップダウンのような項目であっても必要に応じて
サーバ側のチェックも実施する。
クライアント側のチェック:JavaScript VBScriptレスポンスが速い。
サーバー側のチェック:サーバーAPでのチェック(今回はC#)安全。
5.SQLインジェクション(4) 対策:特殊記号のエスケープ
(a) シングルクォート(’)のエスケープ入力文字列中の「’」→「’’」
(b) バックスラッシュ(\ ¥)のエスケープ入力文字列中の「¥」→「¥¥」
※エスケープ時にはマルチバイト文字など注意する(ShiftJIS 「表」=0x95 0x5c 「¥」=0x5c)(使用している文字コード体系による)
(c) セミコロン(;)の拒否可能であればセミコロンは入力拒否する
(d) その他特殊文字DBエンジンやバージョンによって異なるので、必ず調査する¥0 nullバイト文字¥n 改行文字¥r キャリッジリターン文字¥z ファイル終了バイト文字| VBAステートメント実行文字
5.SQLインジェクション(5) 対策:適正なDBアクセス権限
DBに対する権限を適切なものに設定する。(例) ユーザの権限と業務画面ごとにDB接続ユーザを分ける
ただし、ほとんどの業務で参照は必要のため、情報漏洩対策としては実質的に役に立たない。データの改ざん、破壊に対する効果はある。
それでもDBオーナーや管理者権限でのアクセスは避ける。
PracticeDB01には以下のDBアカウントを登録済みWebReader参照権限のみWebWriter 参照権限及びテーブル更新権限
5.SQLインジェクション(6) 対策:バインドメカニズムの採用
パラメータクエリーとも呼ばれるパラメータを含むプリペアドステートメントとパラメータに分離してDBエンジンに渡す。
プリペアドステートメント:入力データは数値定数や文字列定数として組み込まれる。→特殊記号が含まれていた場合でも、それはただの文字として扱われる。
(例)select Name from where id= @id ; と @id = ‘00001’(SQL Serverでの書式:DB エンジンによって異なる)
※ MySQLの古いバージョンなどサポートされないRDBMSもある。Likeのパラメータに使用するときなども充分に確認する。
パラメータとして渡す文字列はエスケープ不要
5.SQLインジェクション
(6) 対策:バインドメカニズムの採用
■実装(a) プログラムからパラメータを含むSQLとパラメータを渡す。
(SqlCommandのParametersを設定する)
(b) ストアドプロシージャDBにパラメータSQLや実行構文をプリペアドステートメントとして登録しておく(SqlCommandのParametersを設定する
+CommandType ← StoredProcedure)
→プログラムとSQLの分離チェックしやすい。プログラムの階層が明確化し分業が可能。簡易なシステム以外では推奨。
5.SQLインジェクション
(7) 対策まとめ
① まずバインドメカニズムにより対策する。(④の特殊記号エスケープ対策と背反)
② SQLインジェクション対策としては補助的なものとして、入力値のチェックを行う。(MMIとしては重要)
③ 効果は大きくないがDBアクセス権限も適正なものにしておく。
④ バインドメカニズム(①)が採用できないときは、必ず特殊記号をエスケープする。
5.SQLインジェクション
(8) 入力チェックの実装
入力値のチェックは必ずサーバ側で実行する。(必要に応じてクライアントでもチェック)
①Validator - ASP.NETで用意するチェック用コントロールクライアント側:チェック行われる。サーバ側:チェック指示と結果による分岐はプログラミングが必要
(サーバ側チェックはテストが面倒なので注意)
②サーバ側チェックValidatorでチェックできないような複雑なものはサーバ側でチェックする。必要に応じてScriptによるクライアントチェック。
protected void ButtonExec_Click(object sender, EventArgs e){
this.Validate(); // チェック実行指示if (this.IsValid) {
・・・・・}
}
5.SQLインジェクション
(9) 正規表現
正規表現とは『いくつかの文字列を一つの形式で表現するための表現方法』
代表的な例a aという文字列[0-9] 数字のどれか1文字 ¥dと表記することがある[a-z] アルファベット1文字[a-z0-9] アルファベットか数字1文字.(ピリオド) 任意の1文字
a¦b aまたはb(a¦b) aまたはb
代表的な特殊文字^ 行頭 $行末* 0個以上の繰り返し + 1個以上の繰り返し ? 0個または1個{n} n個繰り返し {m,n} m個以上n個以下の繰り返し
【例】日付文字列(¥d{2} ¦ ¥d{4})/(0¦1)?¥d/[0-3]?¥d
5.SQLインジェクション
(10) 実習5-2:WebSite07 正規表現の調査
① 新規Webサイト → WebSite07(App_Codeフォルダはコピー/web.config設定)② 以下コントロールの配置
・TextBox ID=TbRegExp 正規表現用の文字列・TextBox ID=TbCheck 検査対象文字列・Label ID=LbResult 結果表示用・Button ID=BtnExec 実行ボタン
③ 名前空間
④ ボタンクリックイベント
protected void BtnExec_Click(object sender, EventArgs e) {try {
Regex regexp = new Regex(TbRegExp.Text);bool result = regexp.IsMatch(TbCheck.Text);LbResult.Text = result.ToString();
}catch(Exception excep) {
LbResult.Text = excep.Message ;}
}
using System.Text.RegularExpressions;
5.SQLインジェクション
(11) 実習5-3:WebSite08 SQLインジェクション対応
① WebSite06フォルダ の コピー及び名前の変更→WebSite08(スタートページの設定/ページ先頭行に実習名)
② 入力チェックの実装(RegularExpressionValidator→正規表現によるチェック)
・IDフィールド5~10文字の英字大文字と数字のみ
③ アクセスアカウントをWebReaderにする。
④ バインドメカニズムを使用する(実装方式(a))
⑤ エラー発生時、ログにはToString()、画面には適切なメッセージを表示する。(パスワード入力値はエラー時以外もログ出力しないようにする)
⑥ エラーメッセージは共通のクラスから取得する方式とする。
6.パスワード
(1) パスワードの格納
パスワードのHash値を格納する。(ログ等にもパスワードは出力しない。)パスワードに固定値(salt)や利用者ごとの固有値(ID等)を付加してHash値を作成することも多い。
パスワードそのものをDBテーブル内には格納しない
■短所・管理者がユーザになりかわってログインしようとしてもログインできない
→本来的に好ましくない。・パスワードを忘れたとき元のパスワードを教えられない→不要・利用者ごとの固有値を付加したHashの場合は
固有値が変更になる場合、パスワードも同時に変更または入力が必要
■理由パスワードは機密情報かつ重要情報⇒ ・参照できないようにする
・参照しただけでは判らないようにする・解析できないようする(解析を難しくする)
また、結果的にハッシュ値は固定長でDBで扱いやすく、文字数制限を解除できる。
6.パスワード
(2) パスワードのHash値の実装
【採用アルゴリズム】
今回使用するアルゴリズム SHA256(SHA-1はMD5よりは堅牢であるが、現状ではクラッシュされ気味?)(SHA-2(SHA-256,SHA-512,・・・)が望ましい)
【実装】クラス : SHA256クラス
(名前空間 : System.Security.Cryptography)
SHA256 shaProvider = SHA256.Create() ;byte[] tmpHash = shaProvider.ComputeHash(バイト配列);
LoginIDと固有値(”itc$soft”)とパスワードを連結させHashを作成する。
Hash値 = ComputeHash(ID + パスワード + ”itc$soft”)このHash値の16進文字列をDBに格納する。
6.パスワード
(3) 実習6-1:WebSite09 Hash値算出
① 新規Webサイト - WebSite09App_Codeフォルダの必要なファイルのコピーweb.configファイルでのログファイル関連設定のコピー
② 以下コントロールの配置・TextBox ID=TbString1 文字列1・TextBox ID=TbString2 文字列2・TextBox ID=TbString3 文字列3
(初期値=itc$softとしたいところだが・・・)・TextBox ID=TbResult 結果(ハッシュ値)表示用(ReadOnly)・Button ID=BtnExec 実行ボタン
③ 文字列1+文字列2+文字列3のSHA256ハッシュ値を16進文字で表示する
6.パスワード
(4) 実習 6-2:WebSite10 パスワードをHash値に変更
① WebSite08フォルダ の コピー及び名前の変更→WebSite10(スタートページの設定/ページ先頭行に実習名)
② WebSite10を開き、以下の変更を実施
・パスワードを(ID + パスワード + ”itc$soft”)のハッシュ値にする。
(WebSite09によりDBのデータ内容も変更しておく)
・パスワード入力フィールドやパスワードを含むSQL等もログ取得しない。
③【拡張1】ログインOK時にLoginTime、UpdateUser、UpdateTimeをセットする。・SQLServer関数のGETDATE()を使用する。
Update PersonalLoginInfo setLoginTime = GETDATE(), UpdateUser = Id,UpdateTime = GETDATE()where Id = @id;
・トランザクションは管理する。・権限はWebWriter
7.セッション管理
(1) HTTP通信の概要
■HTTP通信では通常ブラウザからの操作により、TCP上での接続が始まり、画面情報の送受信が終了するとTCP通信は切断される。
(ラウンド・トリップ)
接続
切断
リクエスト(入力情報・ブラウザ情報・・・)
レスポンス(画面情報(html)、画像など)
クライアント(ブラウザ)
WEBサーバー
7.セッション管理
(2) 情報の引継
URL
ログイン画面
WEBサーバー
ログイン画面
ブラウザ
ID・パスワード
メニュー画面
ボタン
ログイン画面)
メニュー画面
会員情報編集画面
ログイン画面:初期処理
ログイン画面:ボタン処理
メニュー画面:初期処理
メニュー画面:ボタン処理
会員情報編集画面:初期処理
7.セッション管理
(3) レスポンス送信~リクエスト受信までの情報保持
ユーザのIDやログイン済みの情報を保持しておく必要がある。
① セッション情報Webサーバ内にユーザ毎(ブラウザ毎)に存在する情報格納領域・ユーザ毎(ブラウザ毎)に存在する=複数存在する・一定時間で消滅する(web.configに設定、既定は20分)
Web.configの設定例(2分)
<system.web><sessionState timeout=“2" />
</system.web>
ASP.NETでは以下の特徴がある・各セッション情報とのバインドは.NET Frameworkによって行われる。・セッション情報はDBに格納することもできる。・各セッション情報を識別するキー(SessionID)はCookieまたはGETの
パラメータとしてクライアントに保持する。
Cookie エンドユーザがCookieを許可する必要がある。(ブラウザの設定)GETパラメータ URLにセッション情報が表示される。
7.セッション管理
(3) レスポンス送信~リクエスト受信までの情報保持
セッション情報をこの目的で使用する場合システム構成によっては注意を要する
LB
Webサーバ
Webサーバ
Webサーバ
DBサーバ
負荷分散装置
・単純にLB(負荷分散装置)で振り分けるとセッション情報にアクセスできない→LBの設定で回避できる場合がほとんど
・セッション情報をDB上に保有する方式も有効
7.セッション管理
(3) レスポンス送信~リクエスト受信までの情報保持
② hiddenフィールド非表示フィールドに格納することにより、クライアント側に情報を保持できる。ASP.NETではViewState(hiddenフィールドの一種)に格納できる。
ViewStateに格納された情報はデフォルトでは暗号化されずクライアント側での改ざんは容易ではないが解読はされる。したがって重要な情報は暗号化したりDB等サーバ側に格納し、そのキー情報のみをhiddenフィールドに保持する必要がある。
戻るボタン/更新ボタン対策(フィールド情報の再送信)は注意が必要。(ログアウトボタン後の戻るボタンなど)また、タイムアウトの管理もアプリケーションで実装する。
7.セッション管理
(4) ページ遷移時の情報保持
ASP.NETではページ遷移時(Transfer(”ページ.aspx”)を実行した場合)に、ページ
間での情報引継にもセッション情報を情報格納領域として利用するのが一般的。
Session[名前] = object ;(例)
Session[“DepartmentCode”] = “0001” ;Server.Transfer(“foo.aspx”) ;
string xxx = (string) Session[“DepartmentCode”] ;
【注意事項】・アプリケーション共通の情報領域やstatic変数にユーザ固有の情報をセットしない
・セッション情報はずっと残るので、不要になった項目はクリアする。クリアしないとバグの温床となる。
ページ遷移前ページ遷移前
ページ遷移後(foo.aspx)ページ遷移後(foo.aspx)
7.セッション管理
(5) 実習7-1:WebSite11セッションチェックの実装
① WebSite10のコピー → WebSite11 (スタートページ設定/実習名の設定)② 方針
ログイン済みのとき、Session[“_UserID”] にIDをセットする
ログアウト時にSession.RemoveAll()
■ログイン画面(Defalt.aspx)ログインボタンクリックのパスワードチェックOKとTranseferの間
Session.RemoveAll(); //念のためのクリアSession[ ConstData._UserID ] = DBの値 ;
■メニュー画面Page_Load()の先頭
if (Session[ ConstData._UserID ] == null) {Server.Transfer(ログイン画面) ;
}
ログアウトボタンの処理
Session.RemoveAll();
8.ページコントローラ
(1) 各ページのログイン済みチェック
全てのページ(ログイン画面を除く)でログイン済みチェックが必要
『モレ』を防ぐ方法、コードの共通化の方法としてオブジェクト継承と継承元でのチェックを採用する。
System.Web.UI.Page
各業務画面ページ.
System.Web.UI.Page
BasePage
各業務画面ページ
現状のモデル 改造後のモデル
これまでFrameworkが用意したSystem.Web.UI.Pageを(自動的に)継承してPageを実装していたが、その中間に自らコーディングする中間クラスを作成する。
8.ページコントローラ
(2) オブジェクト指向一般
オブジェクト指向設計はモノ中心、データ中心の設計である。
申請書
+状態+申請書番号+申請日+申請者
+申請する()+保存する
図書購入申請書
+図書名
+申請する()+保存する()
通勤定期申請書
+区間
+申請する()+保存する()
出張申請書
+出張先
+保存する()
C言語からみれば『クラス=ロジックつきの構造体』(ロジック=メソッド)
継承とは『構造体を含む構造体、ロジックは上書き』
8.ページコントローラ
(3) 実習8-1:WebSite12オブジェクト指向
前ページのクラス図を具現化したプログラムWebSite12の動作を調査する
8.ページコントローラ
(4)ページイベント
C#では、Pageのイベントのデリゲートに += の演算子でメソッドを登録することにより、各イベントでの処理を実行させる。(例)
AbortTransactionCommitTransactionDataBindingDisposedError
InitInitCompleteLoadLoadCompletePreInit
PreLoadPreRenderPreRenderCompleteSaveStateComplete
this.Load += Page_LoadNew ;
protected void Page_LoadNew(object sender, EventArgs e) {}
ただし、System.Web.UI.Pageを継承するクラスではPage_イベント名のメソッドがあった場合にそれぞれのデリゲートに自動的に登録される。そのため、メソッド登録処理は通常コーディングする必要はない
(例) PageLoadメソッドを作成するとLoadイベント発生時に呼び出される。
Pageイベント
8.ページコントローラ
(5) 実習8-2:WebSite13 BasePageの作成とイベントの調査
①新規Webサイト → WebSite13(App_Codeフォルダはコピー/web.config設定)
② 新規クラスのファイルを作成 - BasePage.csSystem.Web.UI.Pageを継承させる。
④ BasePageに以下のメソッドを作成しコンストラクタでデリゲートに登録(メソッドの中身はログ取得)
③ Defaultページの継承元をSystem.Web.UI.PageからBasePageに変更
public abstract class BasePage : System.Web.UI.Page{}
BasePage_Init(object sender, EventArgs e) BasePage_Load(object sender, EventArgs e)
BasePage_Init(object sender, EventArgs e) BasePage_Load(object sender, EventArgs e)
⑤ Defaultページ及びBasePageで全イベント処理にログ取得を実装
8.ページコントローラ
(6) Template Method
ページコントローラでは一般にTemplate Methodパターンを利用する場合が多い。
通常のPageとSystem.Web.UI.Pageの関係がこのパターン
BasePageはこの中間に位置し、さらに細かいTemplate Methodパターンを形成することもある。
(初期処理)Method1()(中間処理)Method2()(終了処理)
protected virtual void Method1() {(処理なし)
}protected virtual void Method2() {
(標準的な処理)}
BasePageクラス 各ページのクラス
protected override void Method1() {(具体的な処理)
}protected override void Method2() {
(具体的な処理)(base.Method2();)
}
8.ページコントローラ
(7) 実習8-3:WebSite14 BasePageでのセッション管理
① WebSite10のコピー → WebSite14(スタートページの設定/ページ先頭に実習名)
② BasePage.csを作成し、ログイン済みチェックを実装。(ログイン済みでないときはログイン画面へ遷移する)
③ Default.aspxも含め全aspx.csのクラスの継承元クラスをBasePageに変更する。
④ Defaultページではログイン済みチェックをしないような工夫を実装する。(数十画面のシステムでまれにログインチェックしない画面がある想定で実装)
⑤ 【拡張1】ログインチェックNG時はログイン画面にタイムアウトメッセージを表示。
⑥ 【拡張2】ログアウトボタンのときはログインチェックしないようにする。(なるべくBasePageのみの変更で実装する。)
8.ページコントローラ
(8) 実習8-4:WebSite15 情報編集画面の実装
① WebSite14のコピー → WebSite15(スタートページの設定/ページ先頭に実習名)
② 情報編集画面を実装する。ただし、DBアクセスエラーのような続行不可のエラー対処は別途対応することとし、ログイン画面以外では実装しない。
③ 情報編集画面BasePageを継承する。Page_Load時:IsPostBackプロパティの値により、セッション情報のIDのデータをDBのテーブルから読込み、画面データをセットする。
変更ボタンクリック時:入力情報をDBにUpdateする。【拡張1】DBをUpdateする前に所属部署コードがDBにあるかチェックする。
戻るボタンクリック時:メニュー画面へ遷移する。
8.ページコントローラ
(9) 画面遷移
ログイン画面
フィールド:IDパスワード
ボタン:ログイン
メニュー画面
フィールド:(なし)
リンクボタン:情報編集ボタン:ログアウト
情報編集画面
フィールド:ユーザ名(表示)所属部署コード(表示・入力)所属部署(表示)備考(表示・入力)
ボタン:変更、戻る
情報編集
ログアウトログイン
戻る
変更
PersonalProfileのメンテナンスユーザ名:Name所属部署コード:DepartmentCode備考:Text
8.ページコントローラ
(10) 実習8-4:WebSite15 情報編集画面の実装 (上級コース)
① WebSite14のコピー → WebSite15 (スタートページの設定/ページ先頭に実習名)
② 情報編集画面と入力確認画面を実装する。ただし、DBアクセスエラーのような続行不可のエラー対処は別途対応することとし、ログイン画面以外は実装しない。
③ 情報編集画面Page_Load時:セッション情報とIsPostBackプロパティにより、DB読込または
セッション情報から画面データをセットする。
変更ボタンクリック時:入力情報をセッション情報にセットし画面遷移する。【拡張1】セッション情報をセットする前に所属部署コードがDBにあるかチェックする【拡張2】チェック時の情報を元にセッション情報に所属部署名も追加し、入力確認画面で表示する。
戻るボタンクリック時:セッション情報を整えてメニュー画面へ遷移する。
④ 入力確認画面Page_Load時:セッション情報より画面データをセットする。
確認ボタンクリック時:セッション情報の内容でDBをUpdateし、正常終了でセッション情報を整えてメニュー画面へ遷移する。
戻るボタンクリック時:セッション情報を整えて情報編集画面へ遷移する。
8.ページコントローラ
(11) 画面遷移
ログイン画面
フィールド:IDパスワード
ボタン:ログイン
メニュー画面
フィールド:(なし)
リンクボタン:情報編集ボタン:ログアウト
情報編集画面
フィールド:ユーザ名(表示)所属部署コード(表示・入力)備考(表示・入力)
ボタン:変更、戻る
入力確認画面
フィールド:ユーザ名(表示)所属部署コード(表示)備考(表示)
ボタン:確認 戻る
情報編集
戻る
ログアウトログイン
戻る
変更
確認
PersonalProfileのメンテナンスユーザ名:Name所属部署コード:DepartmentCode備考:Text
8.ページコントローラ
(12) 画面引継情報
各画面でセッション情報にセットするべき画面引継情報の設計例を以下に示す。
共通 № Name 型 内容 備考1 _UserID string ログインID 全画面共通:ログアウト時はRemove
ログイン画面 № Name 型 内容 備考1 (実装済み?) エラー内容
メニュー画面 № Name 型 内容 備考なし string
情報編集画面 № Name 型 内容 備考1 Source string DB読込の有無 ”DB”:DBより取得、"SS":セッション情報2 Name string ユーザ名 Source="SS"のとき有効3 DepartmentCode string 所属部署コード Source="SS"のとき有効4 Text string 備考 Source="SS"のとき有効
入力確認画面 № Name 型 内容 備考1 Name string ユーザ名2 DepartmentCode string 所属部署コード3 Text string 備考
Page_Load時の動作PageのIsPostBackプロパティがtrue→画面フィールドはそのまま false→Source !="SS"のとき、画面フィールド←DB →Source=="SS"のとき、画面フィールド←セッション情報
9.クロスサイトスクリプティング対策
(1) 実習9-1:スクリプト文字列の表示
① PersonalProfileのName列に以下の文字列をセットする。
<script>alert(“XSS”);</script>
② WebSite15を実行してみる。
これはプログラムの脆弱性であり、何らかの対策が必要!!
ASP.NETではLabelコントロールにスクリプト文字列をセットすると、クライアントで実行されてしまう。
テキストボックスでは問題は発生しない
9.クロスサイトスクリプティング対策
(2) 実習9-2:スクリプト文字列の入力
WebSite06を実行してみる。
①パターンAIDフィールドに『x<y』を入力して、ログインボタンをクリックする。
②パターンB再度パスワード入力後、引続き『<a』を入力して、ログインボタンをクリックする。
WebSite13でも実行して、実行後ログを確認してみる。
9.クロスサイトスクリプティング対策
(3) スクリプト文字列の入力時の例外
ASP.NETではTextBoxに『<』と英文字を入力するとHttpRequestValidationExceptionの例外が発生する。
これはクロスサイトスクリプティングを防止するためにASP.NETに組み込まれた仕組みである。
このチェックで例外が発生した場合、別画面に遷移させる以外は難しい。
リクエスト(入力情報・ブラウザ情報・・・)
レスポンス(画面情報(html)など)
クライアント(ブラウザ)
WEBサーバー
DB
このポイントでのチェック
受信処理
送信処理
9.クロスサイトスクリプティング対策
(4) 問題点の整理
① スクリプト文字列をLabelにセットした場合、ブラウザで実行される。→Labelコントロールの中身にHTMLタグがあればHTMLとして解釈される。
② 『<』+英文字をサーバに送信するとサーバ処理で例外となる。→HTMLタグ文字などを入力した場合にFrameworkで例外を検出する。
③ Page_Load、ボタンClick、Page_Initなどの処理以外で例外が発生する。→ ページイベント以外の部分(コーディングしていない部分)で
例外が発生することがある。→ その場合にはイベント処理に記述しているtry・・・catchでは
その例外は捕捉できない。
9.クロスサイトスクリプティング対策
(5) スクリプト実行対策
Labelコントロールに文字列をセットするときには、意図的にHTMLタグをセットしている部分以外は以下の置換を行う。ただし、プログラム内でセットした固定文字列など100%安全な場合は対象外としてよい。
『&』→&『<』→<『>』→>
『&』→&『<』→<『>』→>
さらに必要であれば、改行文字も”<br>”に、スペースも ;に置換する。(改行文字は0x0D 0x0Aの場合と0x0Aの場合がある。)
文字列 = 文字列.Replace(”¥r¥n”, “<br>”) ;文字列 = 文字列.Replace(”¥n”, “<br>”) ;文字列 = 文字列.Replace(”“, “ ”);
※いずれの場合も置換処理の順序に注意する。また、変換後にLabelコントロールのTextプロパティが代入文の右辺にならないように注意する。
Labelコントロール以外も使用するときはEncodeの要否を確認して使用する。
この置換処理はSystem.Web.HttpUtilityクラスのHtmlEncode()に実装されているので利用すればよい。
9.クロスサイトスクリプティング対策
(6) スクリプト文字列入力対策
そもそも、スクリプト実行対策が完璧であればスクリプト文字列の入力チェックは不要である。(『念には念を』の考え方には反するが、チェック採用のトレードオフも大きい。採用には充分検討が必要)
① 全ページでスクリプト文字列の入力チェックをしない方法
web.configの<pages>項目のアトリビュートを追加し、以下のようにする。
<pages validateRequest="false" />
② ページごとにスクリプト文字列の入力チェックを無効にする方法
aspxファイルをソースモードで開き、先頭行の@Page ディレクティブにValidateRequest=“false” 属性を追加する。
9.クロスサイトスクリプティング対策
(7) エラー対策
① 例外の捕捉ページイベント以外の部分で発生した例外に対して以下の箇所で捕捉して処理する。
■Page_ErrorBase_Pageに実装することで『モレ』を防止する。
■Global.asaxアプリケーションレベルのエラーをApplication_Errorイベントで捕捉。
■Web.configエラー捕捉の最後の砦。通常は静的なページ(xxxx.html)を設定
9.クロスサイトスクリプティング対策
(7) エラー対策
(Web.configの設定例)
<configurations>・・・・・・・・
<location path="Customers"> <system.web>
<customErrors mode="On" defaultRedirect="error.htm"><error statusCode=“401" redirect="CustomerError401.aspx"/><error statusCode=“404" redirect="CustomerError404.aspx"/>
</customErrors></system.web>
</location></configurations>
HTTPのエラーコードごとにジャンプ先ページは設定可能。
9.クロスサイトスクリプティング対策
(7) エラー対策
② エラー詳細の取得Page_Error/Global.asaxでは Server.GetLastError()で例外を取得する。
(例)
Exception err = Server.GetLastError();
③ エラー処理Page_Error/ Global.asaxではクライアントのRequestコマンドを詳細に取得しよう
とすると再度例外となる可能性が高い。→ ログ取得後にエラー表示画面などにTransferする。
(Transferで別ページに制御が移るとエラー状態は解消される)
※別ページに遷移しない場合はServer.ClearError()を実行し、Response.Write()でHTMLを出力する。
9.クロスサイトスクリプティング対策
(8) 実習9-3:WebSite16 クロスサイトスクリプティング対策
① WebSite15のコピー → WebSite16 (スタートページも設定/ページ先頭に実習名)
② スクリプト文字列の表示対策→可能性のあるフィールド全て。(想定:DB内のデータは危険が潜んでいる可能性があるものとする)
③ エラー表示画面を追加
④ ページイベント外の例外対策を実装。ファイル-新しいファイルグローバルアプリケーションクラスGlobal.asaxのApplication_Error()に以下を追加
void Application_Error(object sender, EventArgs e) {Exception exep = Server.GetLastError();LogWriter.WriteLine(exep.ToString()) ;Server.Transfer(エラー表示画面) ;
}
⑤ 全ページのスクリプト文字列入力チェックを無効化する。(web.configでの対応)
10.レコードロック対策
(1) Webアプリケーションのレコードロック
2人のユーザが同時に同じレコードを編集しようとした場合を考える。
所属部署
備考
東京支社
プログラマ
所属部署
備考
東京支社
営業
所属部署
備考
東京支社
プログラマ
所属部署
備考
岡山支社
プログラマ
WEBサーバー
Bさんの入力がDBに記録され、結果的にAさんの入力した備考の内容が失われる。
Aさん
Bさん
10.レコードロック対策
(2) 楽観的ロック
HTTPインタフェースでは、通常画面表示が完了すればTCP通信としては通信のセッションは完了しており、ブラウザが閉じられたことはサーバ側では検知できない。
したがって、Webアプリケーションではバッチ処理で使われるようなロック方法は使用できないため、『楽観的ロック』といわれる手法が採用されることが多い。
■楽観的ロック(Optimistic lock、オプティミスティックロック)データを読んでから更新という処理を行う際に、読み取るタイミングで対象のデータ(行)へのロックを行わず、更新時にデータが他のユーザによって更新されていなければ、更新するという排他手法。
■悲観的ロック(Pessimistic lock、ペシミスティックロック)読み取るタイミングで排他的なロックをかけてしまう排他手法。
10.レコードロック対策
(3) 実習10-1:WebSite17 楽観的ロックの実装
① WebSite16のコピー → WebSitre17 (スタートページも設定/ページ先頭に実習名)
② 各レコード更新時にUpdateTimeがGETDATE()で更新されていることを確認する。
③ 情報編集画面でDBよりレコード選択時にDBエンジンより時刻を取得する。(例)
Select GETDATE() as ‘SelectTime’, ID , Name, Text from PersonalProfilewhere id = @id ;
④ 取得した時刻をセッション情報に格納する
⑤ 更新時に対象レコードのUpdateTimeと比較し問題がない場合は更新処理を行う。UpdateTimeが新しい場合はエラー表示する。『他の操作によって情報が更新されておりますので、メニュー画面まで・・・・』
(※ GETDATE()はSQL Serverでの関数)
おわりに
Webアプリケーションの注意点
Webアプリケーションでのセキュリティ対策やDBレコードロック対策について実習してきたが、その他にも注意しなければならない点がある。
■static変数、 global変数、 Application領域多数のユーザから同時にアクセスがあっても、同一の領域を使用するのでユーザ(ブラウザ)ごとに異なる値を保持するために利用してはならない。(テストでは発見しにくいために充分に注意する)
■ブラウザ操作(2重POST)ボタンを2度クリックすると、サーバAPは2回動作しようとする。2度実行されてはならない処理は対策を施す。(NWエラーも考慮しての対策)
■ブラウザ操作(更新ボタン、戻るボタン、進むボタン)同じデータがPOSTされるので、考慮しておく。
■ブラウザ操作(表示途中でのボタンクリック)表示途中での操作は期待外のデータとなることがあるので、考慮する。
おわりに
Webアプリケーションの注意点(つづき)
■無駄なPostBackASP.NETではボタンクリック以外にも無駄なPostBackを発生しやすく、テスト環境では気がつきにくい。イベントには注意する。
■コード体系ASP.NETではデフォルトでUTF-8のコード体系であり、SQL Serverは Shift-JISである。 『¥』の表示やデータの文字数、バイト数に差異が発生するので、考慮する必要がある。