GoogleとYahooを訪れたことがある場合、下記リンクの下線が画像で表示されます。
Google
Yahoo
これだけなら単なる装飾ですが、実はこれ、Webビーコンそのものです。
前JavaScriptで訪問履歴が取得できるという話をしましたが、JavaScriptをオフにしていても訪問履歴を取得する手段があったりします。
その手段とはCSSを用いるもので、:visited疑似クラスとbackground-imageプロパティを使用します。
両者ともに元々のCSSの機能であり脆弱性でも何でもありませんが、この二つを組み合わせるだけでこんなことができてしまいます。
index.php
1
2
3
4
5
6
7
8
9
10
11
12
|
<html>
<head>
<style TYPE="text/css"><!--
a#site-google:visited{background:url("site-google.jpg");}
a#site-yahoo:visited{background:url("site-yahoo.jpg");}
--></style>
</head>
<body>
<a href="http://google.co.jp/" id="site-google">Google</a><br />
<a href="http://yahoo.co.jp/" id="site-yahoo">Yahoo</a>
</body>
</html>
|
:visited疑似クラスはそのリンクが訪問済であった場合にだけ有効化される疑似クラスです。
で、その中でbackground-imageプロパティを呼ぶと、リンクが訪問済だった場合に指定したURLにpingが飛ぶというわけです。
上記のリンクは単に画像を呼んでいるだけですが、URLを"hoge.php?site=google"みたいにすれば普通にPHPで受け取ることができます。
逆にjpgの拡張子でPHPが呼び出されるようにしておけばユーザからは画像を呼んでいるようにしか見えません。
あとは引数に適当にIDでも割り振っておけばIPアドレスとの照合も簡単にできてしまいます。
使用しているのはCSSだけなので、ユーザ側で無効化しにくいというのも問題点(利点?)です。
最近はとりあえずCSSが無いとどうしようもないサイトばっかりですのでそうそう無効化するわけにもいきません。
実はこれ、2002年から議論され続けているにもかかわらず未だに解決していない問題だったりします。
https://bugzilla.mozilla.org/show_bug.cgi?id=147777
それだけ根が深くてどうにもならない問題だということなのでしょう。
アイコンはここから拾いました。
http://www.tubumikan.com/
Qtip 1.0.0-rc3
http://craigsworks.com/projects/qtip/
リンク上にカーソルを動かしたときにツールチップを表示するjQueryプラグインです。
onMouseOverで適当にテキストを挿入するなり、なんならJavaScriptを使用せずCSSだけでもできたりはします。
またalt属性やtitle属性を指定すれば大概のブラウザはその内容を自動的に表示したりしてくれます。
http://phpspot.org/blog/archives/2008/03/javascriptcss_2.html
まあとりあえずcssとqTipでそれぞれツールチップを書いてみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="jquery.qtip-1.0.0-rc3.min.js"></script>
<script type="text/javascript"><!-- //JavaScriptでツールチップ
$(document).ready(function(){
$('#qtip_tooltip').qtip({ //id="qtip_tooltip"に対して
content: 'qTipで吹き出し' //表示する文
,style:{ //スタイル
color:"red"
,background:"lightgreen"
,border:{ //外枠
color:"#000000"
,radius:2
}
,tip: {corner:"leftTop"} //角
}
,show: 'mouseover' //表示、非表示にするタイミング
,hide: 'mouseout'
});
});
--></script>
<style type="text/css"><!-- /* CSSでツールチップ */
/* ツールチップを隠す */
#css_tooltip span#css_tooltip_span {
display:none; padding:2px 3px; margin-left:8px; width:130px;
}
/* マウスオーバーで表示する */
#css_tooltip:hover span#css_tooltip_span{
display:inline; position:absolute;
background:lightgreen;
border:1px solid #000000;
color:red;
text-decoration:none;
}
--></style>
</head>
<body>
<a href="#" id="css_tooltip">
CSSでツールチップ
<span id="css_tooltip_span">CSSで吹き出し</span>
</a>
<br />
<a href="#" id="qtip_tooltip">
qTipでツールチップ
</a>
</body>
</html>
|
実際の動作はこのようになります(マウスオーバーで動作)
CSSでツールチップ CSSで吹き出し
qTipでツールチップ
この程度ならCSSでもqTipでもたいして変わらないですね。
qTipが便利なのは、ポップアップする場所やタイミングといった内容をHTMLに直接書くこと無しに、jQueryのセレクタやイベントを使って書けるということでしょう。
マウスオーバー時だけではなく、クリック時やオンロード時といった好きなタイミングで自由なポップアップを行うことができます。
セレクタもIDさえあればonclickみたいなロジックを書く必要も無く、HTMLからロジックを完全に排除して書くことができるようになります。
よくある左右に動くサムネイルです。
jQueryのプラグイン、jCarouselがそのまんまの機能を持っていますのでさくっと作ってみます。
jCarousel 0.2.3
http://sorgalla.com/projects/jcarousel/
libフォルダがライブラリ本体、skinsフォルダが見た目のCSS、imagesフォルダがロード中の画像です。
examplesフォルダには例が入っています。
使い方は簡単で、まず<ul><li>で画像の要素を並べ、<ul>のIDをオンロードでjcarouselに突っ込むだけです。
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript"
src="jcarousel/lib/jquery.jcarousel.pack.js"></script>
<link rel="stylesheet" type="text/css"
href="jcarousel/lib/jquery.jcarousel.css" />
<link rel="stylesheet" type="text/css"
href="jcarousel/skins/tango/skin.css" />
<script type="text/javascript">
//オンロードでjcarouselを実行
jQuery(document).ready(function() {
jQuery('#id_mycarousel').jcarousel({
scroll: 1
,initCallback:jcarousel_initCallback
});
});
//コールバック関数が必要なら書く
function jcarousel_initCallback(carousel, state){}
</script>
</head>
<body>
<ul id="id_mycarousel" class="jcarousel-skin-tango">
<li><img src="a.jpg" width="75" height="75" alt="" /></li>
<li><img src="b.jpg" width="75" height="75" alt="" /></li>
<li><img src="c.jpg" width="75" height="75" alt="" /></li>
<li><img src="d.jpg" width="75" height="75" alt="" /></li>
<li><img src="e.jpg" width="75" height="75" alt="" /></li>
<li><img src="f.jpg" width="75" height="75" alt="" /></li>
<li><img src="g.jpg" width="75" height="75" alt="" /></li>
<li><img src="h.jpg" width="75" height="75" alt="" /></li>
<li><img src="i.jpg" width="75" height="75" alt="" /></li>
<li><img src="j.jpg" width="75" height="75" alt="" /></li>
</ul>
</body></html>
|
まあ、同封されているサンプルそのまんまなのですが、このように簡単に表示が行えます。
引数で動作をある程度制御することができます。
scroll: 1で一枚づつスクロール、vertical:trueで縦表示、というふうに指定します。
それ以上複雑なことが行いたい場合は、色々な状態についてコールバック関数を指定することができます。
例として挙げているinitCallbackは、読み込まれた時に一回だけ実行してくれます。
見た目はCSSで変更します。
skins.cssを適当にいじくるだけです。
デフォルトではjcarousel-skin-tangoクラスとなっていますが、その中のサブクラスjcarousel-containerで全体のコンテナを設定、jcarousel-itemで個々のアイテムについての設定といったかんじになっています。
どうでもいいのですが、表示される要素には別に画像だけではなく文字を入れることもできるので、ちょっと使いにくいメニューなんてものも作成できます。
意味はありませんが。
JavaScriptではテキストの状態を取得・操作する各種プロパティやメソッドがありますが、その中のひとつにテキストの色を操作するものがあります。
このcss(name)を<a>タグに使ってみるとどうなるかというと、リンク先を訪問済の場合と未訪問の場合で違う色が返ってくるわけです。
さて、Livedoor ad4Uというサービスがありますが、このサービスは上記を利用して訪問履歴を調べています。
http://ad4u.drecom.co.jp/
http://www.drecom.co.jp/pr/release/20080711/
http://takagi-hiromitsu.jp/diary/20081211.html
<input type="file">でどの画像を指定したのかわからなくなった、うっかり違うファイルをアップロードしてしまった、なんてことはよくあると思いますが、それを防ぐために確認画面を用意しているサイトもあります。
ただ個人的に確認画面要らないと思うのですがどうでしょう。
というわけで画面遷移せずに指定した画像をプレビューできるjQueryプラグインを見つけたので使ってみます。
jQuery.autouploader 1.0.1
http://tech.kayac.com/archive/jquery-autouploader-plugin.html
使い方は簡単。
適当な引数付けて<script src>で呼ぶだけというお手軽さです。
とりあえず作ってみましょう。
jquery_index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<!--
jquery.autouploader.jsを呼び出すときに、srcに引数を入れる
api=/api/image/set
サーバ上の処理プログラムのURL
send_name=img
送信する画像ファイル名
loading=''
ロード中別の画像を表示する場合
no_exec=''
trueにすると自動プレビュー無効
その場合は↓のように手動で呼び出す
$(function(){$('form[enctype="multipart/form-data"]').autouploader();});
-->
<script type="text/javascript" src="./jquery.js"></script>
<script type="text/javascript" src=
"./jquery.autouploader.js#api=./jquery_api.php&send_name=img">
</script>
<form method="POST" action="./jquery_submit.php" enctype="multipart/form-data">
<input type="file" name="data"><br /><br />
<input type="submit" name="submit"></form>
|
#api=./jquery_api.php&send_name=img
という形で呼び出しています。
これは、サーバ側で画像ファイルの処理を行うのが./jquery_api.phpで、$_FILESの名前となるのがimgということです。
デフォルトがimgで、<input type="file">のname属性は無視されます。
本来、フォームは
<input type="submit">
ボタンを押した時点で送信されますが、今回は画像ファイルを指定して「開く」を押した時点でjQueryがそれを察知し、jquery.autouploader.jsがそのファイルを取得して送信します。
送信先のURLで行うなことは、送信されてきた画像を適切に保存することと、その画像のURLを返すことです。
jquery_api.php
1
2
3
4
5
6
|
move_uploaded_file(
$_FILES["img"]["tmp_name"]
,'/image/'.$_FILES['img']['name']);
print('/image/'.$_FILES['img']['name']);
|
アップロードされたファイルを/imageフォルダにコピーし、そのファイル名をprintしてるだけです。
アップロードファイルのエラーチェックやファイル形式チェックも本来は必要ですが、どうせローカルなので一切やってません。
公開するときにはこんなことやっちゃいけません。
たったこれだけで画像プレビュー機能が追加できてしまいました。
ちなみに、プレビュー時に「削除」ボタンが出ますが、このボタンは単にプレビューを非表示にするだけで、サーバ上のファイルを消すわけではありません。
そんな機能作ってないですし。
というわけで延々プレビューを繰り返すと、そのぶんだけサーバにファイルが増えていきます。
うっかり機密ファイルを指定しちゃったからやり直した、という場合でもファイルはしっかり盗られています。
なんかに使えそう。
問題点としては、日本語ファイルが使用できません。
保存はできるのですが、表示するアドレスとして日本語が使用できません。
何の文字コードで送っても駄目なので、日本語自体アウトと思われます。
サーバ側で適当に名前を割り振る必要があります。
さて、私はあなたを犯罪者にすることができます。
先日児童ポルノサイトへリンクを張った人が逮捕されました。
http://slashdot.jp/yro/article.pl?sid=09/07/08/0414236
http://www.j-cast.com/2007/05/09007471.html
http://www.yomiuri.co.jp/national/news/20090708-OYT1T00078.htm
http://news.livedoor.com/article/detail/4240457/
http://blog.livedoor.jp/dankogai/archives/51232218.html
他の記事にはまったくコメントが付いていないのにこの記事にはコメントがありますが、
http://yuubiseiharukana.blog.shinobi.jp/Entry/132/
の記事を読もうとした時点で自動的に貴方の足跡でここにコメントが書き込まれるようになっています。
今回はなんでもないコメントですが、これを妖しいサイトのアドレスを書き込むようにすることは非常に簡単です。
そうすると児童ポルノサイトへリンクを張った罪で貴方が逮捕されます。
現状のように私のブログに張ってしまえば私も逮捕されるので自爆ですが、他所の掲示板やブログに矛先を向けるのも難しくありません。
したらばBBSのように外部からの投稿を弾くようになっている場合は手間がかかりますが、回避方法もいろいろ存在します。
今回使用したのもまったくたいした技術ではなく、多少JavaScriptが使えるなら誰でも理解できる内容です。
ネカフェからどっかの緩いXSS脆弱性のある掲示板にでも投稿スクリプトを貼り付けてしまえば、作者を追うことがほぼ不可能な犯罪者作成装置が完成することでしょう。
つうか忍者ブログって外部からのPOST素通しかよ。
あぶねーなおい。
ちなみに名前や本文の後に付いてる変な数字は多重投稿チェックをスルーする為のものです。
jQuery 1.2.6
http://jquery.com/
jQueryとは、JavaScriptのライブラリ集のようなものです。
で、これが尋常ではなく便利極まりない。
動的メニューやテキストの動的変更といった、ちょっとした演出が非常に簡単に行えます。
まず公式サイトからファイルをダウンロード。
何種類かありますが全部同じもので、Uncompressedがソース閲覧用、Minifiedが使用するためにコメントやインデントを全て取り払ったものです。
jquery.jsこれひとつがjQueryの全てです。
適当なフォルダに突っ込んだらHTMLから呼び出してみます。
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="jquery.js" type="text/javascript"></script>
</head>
<body>
<div class="Class1">DIVでClass1です</div>
<div class="Class2">DIVでClass2です</div>
<p class="Class1">PでtestClassです</p>
<div id="TestID">DIVでtestIDです</div>
<script type="text/javascript">
$("DIV.Class1")
.css("color","red")
.html("DIVのClass1を発見しました")
.animate({fontSize: "2em"},2000 );
$("#TestID")
.css("background","blue")
.css("width","120px")
.css("height","120px")
.css("padding","10px")
.mouseover(function () {$(this).slideUp();});
</script>
</body>
</html>
|
基本は、$の後にタグ.class、もしくはタグ#idで対象を見つけ出し、それに対して行いたいことを.で繋げて書いていくだけです。
色々なメソッドがあるのですがとりあえずスクリプトを別ファイルにしましょう。
ちょっとだけ特殊な書き方が必要で、
index.html
<script src="index.js" type="text/javascript"></script>
index.js
$(document).ready(function(){
//ここにメソッド
});
これで分離できます。
あとは適当にidなりclassなりの単位でメソッドに突っ込めばいいのですが、中にはAjaxでファイルを動的に取得するメソッドも存在します。
かつてこんなに長々とソースを書いていたものが、
$("#id").click(function(){$(this).load("sample.txt");});
だけで終わっちゃうとかもうねどうなのよこれ。
innerHTMLとXMLHttpRequestを利用してKWICを作ってみましょう。
http://ja.wikipedia.org/wiki/KWIC
簡単に言うとテキスト検索ですが、その前後の文字ごと表示することによって実際に探している文脈をみつけやすくするものです。
Google検索結果でいうところの検出サイトの下に出てくる文字列、まさにあれです。
実のところJavaScriptで行う部分はXMLHttpRequestの送受信とinnerHTMLの変更だけなので、JavaScript2-1でやってることとほぼ同じです。
具体的な文書解析やらHTML構文作成やらはどうしてもPHPのほうが楽なので、そちらに任せてしまいます。
本来ならばXMLやDOMツリー等を使ってJavaScript側で行うべきなのですが、個人的にXML嫌いなので。
XMLといいJavaといいcssといいどうして面倒なほうに進んでいくのでしょうかね。
HTML構文とかは相当適当なので気にしない方針で。
あとJavaScriptももっと構造化すべきなのですがそこらへんもスルーの方向で。
上から順につらつら書いているとどうしてもこんな再利用が困難な書き方になってしまいます。
大事なHTML生成部分はすべてPHPに投げ渡してしまっているので、次回はPHP部分を作りましょう。
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0.1//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html lang="ja"> <head> <meta http-equiv="Content-Type" Content="text/html;charset=Shift_JIS"> <link rel=stylesheet type="text/css" href="./index.css"> <meta http-equiv="Content-Script-Type" content="text/javascript"> <script type="text/javascript" src="./index.js"></script> <title>いんくりめんと</title> <script type="text/javascript"> <!-- function openFile(){ //検索して表示 peekQuery('gingatetsudono_yoru.txt'); //1秒毎に実行 window.setTimeout("openFile();",1000); } --> </script> </head> <body onload="openFile()"> <div id="inclementWatch2"></div> <h2>いんくりめんと</h2><br /> <form> <div id="ooooo" style="display:none;"></div> <input type="text" name="sampleWatch" size="60" style="display:none;"> 検索ワードを入力 <input type="text" size="30" maxlength="30" onClick="true" id="searchText"> <br /> <div id="searchField" style="display:none;"></div> <br /> <input type="button" value="全文を読む" onClick="loadText('gingatetsudono_yoru.txt')" id="dispButton"> </form> <div id="dispField" style="display:none;"></div> </body> </html> |
index.js
/*==========================================================*/ |
index.css
body{ background:#fafafa; margin:10px 3% 0px; } div{ margin:2px; padding:3px; border-width:1px 1px 1px 1px; border-style:solid; } div a{ text-decoration:none; } div:hover{ background:#eff; } |
さて、
http://yuubiseiharukana.blog.shinobi.jp/Entry/17/
のフォームをJavaScriptでバリデートしてみましょう。
JavaScriptでバリデートを行う理由は、サーバ側の負担軽減のためです。
素のフォームのままだと、毎回サーバにデータを送信しチェックを行うのでサーバに負荷がかかります。
ユーザ側のブラウザでチェックを行わせることで、サーバ側の負担を和らげることができます。
まずformタグを変更します。
<form method="POST" action="<?php print(htmlspecialchars($_SERVER['SCRIPT_NAME'])); ?>" onSubmit="return check()">
formのonSubmit部分に関数を追加しました。
これでJavaScript対応ブラウザでsubmitボタンを押した場合、check()関数が発動します。
未対応ブラウザの場合は、そのまま送信されます。
では早速関数checkを作りましょう。
function check |
入力されているかどうかだけを簡単にチェックしています。
onSubmitで呼ばれる関数がfalseを返さない限り、フォームはそのままactionを実行してしまいます。
エラーが見つかった場合のみfalseを返すことで、その場合は先に進まないようにしています。
特になんて難しいことはありませんね。
今後基本的にJavaScriptは別ファイルにして、
<SCRIPT TYPE="text/javascript">
で呼び出すことにします。
未対応ブラウザのためにコメントで囲ったりという手間も無くなり、ソースも見やすくなるので推奨されます。
index.php
<?php |
check.js
function check var errorMessage=""; if(document.form[0].shimei.value == ""){ errorMessage +="氏名を入力してください\n"; } if(document.form[0].mail.value == ""){ errorMessage +="メールアドレスを入力してください\n"; } if(document.form[0].sex.value == ""){ errorMessage +="性別を選択してください\n"; } if(errorMessage == ""){ return true; }else{ alert(errorMessage); return false; } } |
さて、JavaScript側とPHP側、両方で入力のチェックを行っているわけですが、では二重に同じ事を行うのは無駄だからどちらかを省くべきでしょうか?
いいえ。この場合は、両方でチェックを行うべきです。
なぜならば、知ってのとおりJavaScriptによるチェックは簡単にすり抜けられるからです。
ちなみにJavaScriptの代入演算子+=は、数字でなければ文字の連結を行ってくれますが、
PHPで+=と書くと無理矢理数値に変換した上で計算してくれます。
PHPで文字列連結は.=と書かなければなりません。
PHPの自動型変換は時に相当不可解な振舞いを見せてくれるので注意が必要です。