3流プログラマのメモ書き

元開発職→社内SE→派遣で営業支援→開発戻り浦島太郎状態の三流プログラマのIT技術メモ書き。 このメモが忘れっぽい自分とググってきた技術者の役に立ってくれれば幸いです。

(PHP)XSSとSQLインジェクション対策

今回 PHPSmarty でモバイル向けWEBサイト作ってるんですが、やはりセキュリティ対策は講じないといけません。

とりあえず、XSS,SQLインジェクション対策としてサニタイズをしようかと思います。

(まぁモバイル機上ではスクリプト動くことはあんまりないと思うんですが、念のため。。)

まず、クロスサイトスクリプティング対策として、スクリプトに使われそうな文字をエスケープするという方法をとってます。

で、(CakePHP)$form->textareaはサニタイジングしてくれない!?でも使った h() 関数を使おうと思ったんですが、そんな関数無いって怒られます。

どうやら、h() って CakePHP オリジナルの関数なんですね。ってきり標準で入ってるのかと思ってました。。。

ということで、h() の実体である htmlspecialchars() でフォーム入力やDBから取得のタイミングでサニタイズします。

参考:

WEBデザイナーの為のXSS(クロスサイトスクリプティング)入門[to-R]

次にSQLインジェクション対策です。

こちらも基本的にはサイニタイズで対処することにします。

今回 MySQL を使ってるんですが、MySQLは基本的に一回にひとつのクエリしか発行できないため(クエリスタックができない)、他のRDMSのように ; で区切って別クエリを走らしてDBの情報を盗み出すみたいなことはできません。

それでも、テキストボックスに OR とかでクエリつながれたりしたら終わりです。

ということで、MySQLの場合もちゃんとしたサニタイズをしないといけません。

PHP の場合、各DB毎に専用にエスケープ関数が用意されています。(一部用意されてないRDBMSもあるようですが。。)

MySQL だと mysql_real_escape_string() という関数です。

この関数はDB接続がされてないと使えないので実際にクエリに値を埋め込むときに使ったほうがいいようですね。

また、すでにエスケープされているものに対し mysql_real_escape_string() 使うと2重エスケープで意味がなくなるので、ここも注意が必要です。

特にマジッククォート機能が有効の場合は、これを無効にするかエスケープを一旦取り除いてから処理したらいいようです。

この辺の情報は、MySQLとPHPにおけるSQLインジェクション対策についてで詳しく説明されてました。

firegoby » PHP+MySQLでSQLインジェクション対策によると、mysql_real_escape_string() を使う場合は、MySQL への文字コードの指定に、set names ではなく、mysql_set_charset() を使わないと意味が無いようです。

(set names と mysql_set_charset() については、(PHP)MySQLへの接続時SET NAMESは使わないほうがいいで書いてます。)

たぶん、この問題は(PHP)HTML側がShift-JISだと SET NAMES sjis でのMySQLクエリはおかしくなるとも関連してると思います。

mysql_set_charset() を使ってやるのがベータですが、それができないサーバの場合は、別文字コードに変換して処理してやるのがいいでしょう。

(どっちにしろ、set names sjis で Shift-JIS 使うと文字化けの問題も発生するので。。。)

MySQLにもプレースホルダ(プリペアードクエリとも言うらしい)があるみたいですが、mysqli や PEAR などの拡張モジュールやクラス使わないといけないので、いまいち使おうという気になりません。。。

ということで、XSS対策とSQLインジェクション対策の場合のDBアクセスを書いてみました。

//パラメータ取得 このときXSSサニタイズ

$findName = EscapeHtmlSanitize(trim(urldecode($_GET["findname"])));

$findTEL = EscapeHtmlSanitize(trim(urldecode($_GET["findtel"])));

 

 

//DB接続

$cn = DB_Connect();

if (! $cn ){die;}

 

//クエリ生成 このときSQLインジェクションのためのサニタイズ

$query = sprintf("SELECT * FROM tablename WHERE name LIKE '%s' AND tel = '%s' ",

SqlSanitize("%" . $findName . "%") ,

SqlSanitize("%" . $findTEL . "%") );

 

//クエリ実行

$result = mysql_query($query);

//結果出力

while ($row = mysql_fetch_assoc($result)) {

echo htmlspecialchars($row["name"]) . "
"
;

echo htmlspecialchars($row["tel"]) . "
"
;

}

 

//DB接続

function DB_Connect(){

$db_host = "localhost";

$db_user = "root";

$db_passwd = "passwd";

$db = mysql_connect($db_host,$db_user,$db_passwd);

if ( ! $db ){

return false;

}

// MySQL DB 選択

if (!(mysql_select_db("realstyle_web"))) {die;}

mysql_set_charset(文字コード , $db); //mysql_set_charset が使えないときでShift-JISの場合は、別文字コードに変換するのがいい。

return $db;

}

 

//XSSのサイニタイズとマジッククォート対策

function EscapeHtmlSanitize($inputStr){

//XSS対策(HTMLエスケープ)

$inputStr = htmlspecialchars($inputStr);

//マジッククォートの有り無し

if(get_magic_quotes_gpc()) {

//余計なエスケープ文字を取り除く

$inputStr = stripslashes( $inputStr );

}

return $inputStr;

}

 

function SqlSanitize($inputStr){

//SQLインジェクション対策

$inputStr = mysql_real_escape_string($inputStr);

return $inputStr;

}

たぶん↑で問題ないですよね?

参考:

@IT:今夜分かるSQLインジェクション対策

ThinIT:第1回:SQLインジェクションによるデータベース操作

mysql_real_escape_string ここの例 3. "うまいやり方" のクエリが参考になります。