ログイン情報を自動入力するブックマークレットの作り方

Webページの入力情報を読み取る

  • 上記ページで以下のAppleScriptを実行すると...


tell application "Safari"
activate
do JavaScript "(function(d, s, r, url_key, inputs, options, i){
url_key=d.location.protocol.replace(/[^A-Za-z]/g,'_')+'__'+d.location.hostname.replace(/[^A-Za-z]/g,'_')+d.location.pathname.replace(/\\W/g,'_');
inputs=d.getElementsByTagName('input');
options=d.getElementsByTagName('option');
for(i=0;i<inputs.length;i++){
if(inputs[i].type!='hidden'&&inputs[i].type!='image'&&inputs[i].type!='button'){
s.push(eval('('+'{type: \"'+inputs[i].type+'\", id: \"'+inputs[i].id+'\", name: \"'+inputs[i].name+'\", value: \"'+inputs[i].value+'\", checked: '+inputs[i].checked+', index: \"'+i+'\"}'+')'));
}
}
for(i=0;i<options.length;i++){
if(options[i].selected){
s.push(eval('('+'{type: \"option\", name: \"'+options[i].name+'\", value: \"'+options[i].value+'\", selected: '+options[i].selected+', index: \"'+i+'\"}'+')'));
}
}
r[url_key]=s;
return r;
})(document,[],{})" in document 1
end tell

  • URLをキーとした、以下のレコードが返ってくる。


{
https___healthcare_mb_softbank_jp_pc_assets_setup_web_explanation_input_php:{
{checked:false, |id|:"telno3", value:"00012345678", type:"text", |name|:"", |index|:"0"},
{checked:false, |id|:"passwd", value:"1234", type:"password", |name|:"passwd", |index|:"1"},
{|index|:"3", value:"4", type:"option", |name|:"undefined", selected:true}
}
}

  • URLをキーにして、以下3項目のフォームの情報を配列として保持している。
    • 体組成計の電話番号(テキストフィールド)
    • ユーザー番号(プルダウンリスト)
    • 暗証番号(パスワードフィールド)

自動入力するJavaScript

  • 上記AppleScriptレコードは、以下のようなJSONに変換できる。
{
  https___healthcare_mb_softbank_jp_pc_assets_setup_web_explanation_input_php:[
    {checked:false, id:"telno3", value:"00012345678", type:"text", name:"", index:"0"}, 
    {checked:false, id:"passwd", value:"1234", type:"password", name:"passwd", index:"1"}, 
    {index:"3", value:"4", type:"option", name:"undefined", selected:true}
  ]
}
  • そして、上記JSONを利用して、自動入力するJavaScriptコードを考えてみた。
(function(){


var json={
  https___healthcare_mb_softbank_jp_pc_assets_setup_web_explanation_input_php:[
    {checked:false, id:"telno3", value:"00012345678", type:"text", name:"", index:"0"}, 
    {checked:false, id:"passwd", value:"1234", type:"password", name:"passwd", index:"1"}, 
    {index:"3", value:"4", type:"option", name:"undefined", selected:true}
  ]
};
var url_key=document.location.protocol.replace(/[^A-Za-z]/g,'_')+'__'+document.location.hostname.replace(/[^A-Za-z]/g,'_')+document.location.pathname.replace(/\W/g,'_');
var j=json[url_key];

for(var i=0;i<j.length;i++){
  if(j[i].type=='text'||j[i].type=='password'){
    if(document.getElementsByName(j[i].name).length > 0){
      document.getElementsByName(j[i].name)[0].value=j[i].value
    }else{
      document.getElementById(j[i].id).value=j[i].value
    }
  }else if(j[i].type=='radio'||j[i].type=='checkbox'){
    document.getElementsByTagName('input')[j[i].index].checked=j[i].checked
  }else if(j[i].type=='option'){
    if(document.getElementsByTagName('option')[j[i].index].value==j[i].value){
      document.getElementsByTagName('option')[j[i].index].selected=j[i].selected
    }
  }
}
alert('auto_login works!');


})()
javascript:(function(){for(var b={https___healthcare_mb_softbank_jp_pc_assets_setup_web_explanation_input_php:[{checked:!1,id:"telno3",value:"00012345678",type:"text",name:"",index:"0"},{checked:!1,id:"passwd",value:"1234",type:"password",name:"passwd",index:"1"},{index:"3",value:"4",type:"option",name:"undefined",selected:!0}]}[document.location.protocol.replace(/[^A-Za-z]/g,"_")+"__"+document.location.hostname.replace(/[^A-Za-z]/g,"_")+document.location.pathname.replace(/\W/g,"_")],a=0;a<b.length;a++)"text"==b[a].type||"password"==b[a].type?0<document.getElementsByName(b[a].name).length?document.getElementsByName(b[a].name)[0].value=b[a].value:document.getElementById(b[a].id).value=b[a].value:"radio"==b[a].type||"checkbox"==b[a].type?document.getElementsByTagName("input")[b[a].index].checked=b[a].checked:"option"==b[a].type&&document.getElementsByTagName("option")[b[a].index].value==b[a].value&&(document.getElementsByTagName("option")[b[a].index].selected=b[a].selected);alert("auto_login works!")})();


見事!元どおりに入力された!

安全の問題

しかし、このブックマークレットをこのまま使うのは危険である!

  • 見てのとおり、コードの中にパスワードなどの重要な情報がそのまま保存されている。
  • もしも、このブックマークレットが何らかの原因で外部に漏洩すると、不正ログインが簡単に実行できてしまう。
  • このページはスマート体組成計のページなので大した実害はないが、(体重や脂肪の計測値が見られるだけ)
  • 仮に金融機関のページのログイン情報だと、痛い目に遭う可能性大。

重要な情報は暗号化しておかなければならないのだ!

  • 暗号化を実現するコマンドとJavaScriptライブラリを探してみた。

opensslコマンドの環境

  • OSX 10.9.4に標準インストールされているopensslコマンドのバージョンは、0.9.8y。
$ openssl version
OpenSSL 0.9.8y 5 Feb 2013
  • 巷を騒がせたopensslのバグ"Heartbleed"は、openssl 1.0.1〜1.0.1fに含まれる。
  • よって、OSX標準インストールされるopnessl 0.9.8yには、バグは存在しない。
  • 仮に openssl 1.0.1〜1.0.1fを利用している場合は、1.0.1gへアップデートする。

opensslコマンドで暗号化する

  • 例:文字列"hello world!!"を"1234"というパスワードで暗号化するしてみる。
    • 暗号化によって、見えない文字コードに変換されてしまうとターミナル出力で確認できない。
    • -a = -base64オプション。ここではbase64変換で確実に見える文字にして出力してみた。
$ echo 'hello world!!' | openssl enc -e -a -salt -aes-256-cbc -pass pass:1234
U2FsdGVkX1/ViT/1pTYv4dZsujl2feiy8Wuq72NlWCI=
enc 共通鍵による暗号化
-e エンコードする(暗号化する)
-d デコードする(復号化する)
-a(-base64 base64エンコード・デコードを有効にする
-salt 鍵生成ルーチンでソルトを利用する。特に理由(0.9.5以前との互換性を維持する等)がない限り、-saltは必ず指定しておくべき。
-aes-256-cbc 特に理由(暗号化・復号化の処理スピードの問題など)がない限り、鍵長が最も長い-aes-256-cbcを選択しておいた方が良さそう。
  • 今度は、暗号化された”U2FsdGVkX1/ViT/1pTYv4dZsujl2feiy8Wuq72NlWCI=”を復元してみる。
$ echo U2FsdGVkX1/ViT/1pTYv4dZsujl2feiy8Wuq72NlWCI= | openssl enc -d -a -salt -aes-256-cbc -pass pass:1234
hello world!!

元の文字列"hello world!!"が復元できた!

JavaScriptで復号化する

暗号文をブックマークレットに含める場合、それを復号するためのJavaScriptが必要になる。当初、二つのAES JavaScriptライブラリを見つけた。

JavaScript Implementation of AES Advanced Encryption Standard in Counter Mode | Movable Type Scripts
  • このライブラリは、AES-CTR(CounTeR:カウンタ)モードの暗号化・復号化を行う。


  • サンプル:
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>aes-256-ctrサンプル</title>
</head>

<body>
<p>原文 : hello world!!</p>
<p id="encrypted_text">0</p>
<p id="decrypted_text">0</p>
</body>

<!-- AES implementation in JavaScript(c) Chris Veness 2005-2014 -->
<script src="http://www.movable-type.co.uk/scripts/js/crypto/aes.js">/* AES JavaScript implementation */</script>
<script src="http://www.movable-type.co.uk/scripts/js/crypto/aes-ctr.js">/* AES Counter Mode implementation */</script>

<script type="text/javascript">
    var encrypted = Aes.Ctr.encrypt("hello world!!", "1234", 256);
    document.getElementById('encrypted_text').innerText = "暗号化: " + encrypted;
    
    var decrypted = Aes.Ctr.decrypt(encrypted, "1234", 256);
    document.getElementById('decrypted_text').innerText = "復号化: " + decrypted;
</script>
</html>
  • 上記コードをファイル名"aes_ctr.html"で保存して、開いてみると...
原文 : hello world!!

暗号化: xwHwoweU3FMNRuMVxVY1bVG3qJNP

復号化: hello world!!
opensslコマンドとの互換性
  • OSX 10.9.4に標準インストールされるOpenSSL 0.9.8yは、aes-ctrに対応していない。
  • 一方、HomebrewでインストールしたOpenSSL 1.0.1hでは、aes-ctrに対応している。
  • しかし、同じaes-ctrでありながら、このJavaScriptライブラリとopensslコマンド相互の暗号化・復号化ができない。
    • 例:opensslコマンドで暗号化したものを、このJavaScriptライブラリで複合化できなかった...。(なぜだろう?Saltの扱いの違い?)
  • たとえ互換性がなくても、このJavaScriptライブラリで暗号化すれば問題なく復号化できるので、躊躇する必要はない。
crypto-js - JavaScript implementations of standard and secure cryptographic algorithms - Google Project Hosting
  • このライブラリは、AES-256-CBC(Cipher Block Chaining:暗号ブロック連鎖)モードの暗号化・復号化を行う。
    • 読み込むライブラリ(ファイル)によって、AES-256-CBC以外にも様々な暗号化に対応しているようだ。
  • サンプル:
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>aes-256-cbcサンプル</title>
</head>

<body>
<p>原文      : hello world!!</p>
<p id="encrypted_text">0</p>
<p id="decrypted_text_bin">0</p>
<p id="decrypted_text_utf8">0</p>
</body>

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>
<script type="text/javascript">
    var encrypted = CryptoJS.AES.encrypt("hello world!!", "1234");
    document.getElementById('encrypted_text').innerText = "暗号化     : " + encrypted;
    
    var decrypted_bin = CryptoJS.AES.decrypt(encrypted, "1234");
    document.getElementById('decrypted_text_bin').innerText = "復号 文字コード: " + decrypted_bin;
    
    var decrypted_utf8 = decrypted_bin.toString(CryptoJS.enc.Utf8);
    document.getElementById('decrypted_text_utf8').innerText = "復号 UTF8 : " + decrypted_utf8;
</script>
</html>
  • 上記コードをファイル名"aes_cbc.html"で保存して、開いてみると...
原文      : hello world!!

暗号化     : U2FsdGVkX1/jmFp4MNDcqnb8zmD/3GB+xSEWuEs+qnI=

復号 文字コード: 68656c6c6f20776f726c642121

復号 UTF8 : hello world!!
opensslコマンドとの互換性
  • OSX 10.9.4に標準インストールされるOpenSSL 0.9.8yと、完全な互換性がある。
  • 試しに、上記で暗号化したものをopensslコマンドで復号化してみると...
$ echo U2FsdGVkX1/jmFp4MNDcqnb8zmD/3GB+xSEWuEs+qnI= | openssl enc -d -a -salt -aes-256-cbc -pass pass:1234
hello world!!
  • その逆も然り...
$ echo 'hello world!!' | openssl enc -e -a -salt -aes-256-cbc -pass pass:1234
U2FsdGVkX1/ZwwQ3vwuYHqYvPDhfAKria033cuKWHEs=
原文      : hello world!!

暗号化     : U2FsdGVkX1/ZwwQ3vwuYHqYvPDhfAKria033cuKWHEs=

復号 文字コード: 68656c6c6f20776f726c6421210a

復号 UTF8 : hello world!!


どちらも、ちゃんと復号化できた!

ログイン情報を暗号化する

  • 余分な改行を削除したログイン情報のテキスト。
'{https___healthcare_mb_softbank_jp_pc_assets_setup_web_explanation_input_php:[{checked:false, id:"telno3", value:"00012345678", type:"text", name:"", index:"0"}, {checked:false, id:"passwd", value:"1234", type:"password", name:"passwd", index:"1"}, {index:"3", value:"4", type:"option", name:"undefined", selected:true}]}'
  • 上記テキストをaes-256-cbcで暗号化してみる。
$ echo -n '{https___healthcare_mb_softbank_jp_pc_assets_setup_web_explanation_input_php:[{checked:false, id:"telno3", value:"00012345678", type:"text", name:"", index:"0"}, {checked:false, id:"passwd", value:"1234", type:"password", name:"passwd", index:"1"}, {index:"3", value:"4", type:"option", name:"undefined", selected:true}]}' | openssl enc -e -salt -aes-256-cbc -pass pass:MSTER_PASS | base64
U2FsdGVkX1/uT2sjZl3uT43ciqzmXYZpB8mwYxMpyGk0EN/Gz0hzixJ4+Js2mU68qOWohJmpPGgr5g1+eaxL8aeLh+ooMgxXYLZozYUhWaJ99+0+tKV9GDarpbOMrfA6zE8hvCy62XChqZZINsPOA7G3KuFs54+BKNXFqKbsyA2f0MciDxQcmZgeTC8ci+rS1WZHyTMK5EPh6GhPK/QpoejG1JdXpcGdV94pN7R8flpT4iyMdGdbCfSeL7xjnmZs3BMvc5S50VZEtIdsD9BCeixQFf7GCZvuTJH4zxQHoPZMLc1T85ReBliiya9PvHcD6VfxTk4iuvodqIAC3J/pF5IP5fEhR/24XOZ/5gcEa92u8HulCp0LgfAeUxrgABo0a+ELvpysYBKQmwuTNU9ssFh9QA841RXvLh/JUnQAE8YTksS27PF50nhZap6C1OC5rOIRvIdrTgSgImjQ6TxhiA==
  • 暗号化されたログイン情報を利用したブックマークレットに修正してみた。
    • JavaScriptライブラリをロードする時間を考慮して、setTimeoutで0.5秒待機してから実行している。
    • ネットワーク環境やブラウザの性能によって、より多くの待機時間が必要になるかもしれない。
(function(){

//JavaScriptライブラリをロード
var e=document.createElement('script');
e.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js';
document.body.appendChild(e);

setTimeout(function(){
//暗号化ログイン情報を復号する
var passwd_raw=prompt('auto_login Password','');
var json_text_cipher='U2FsdGVkX1/uT2sjZl3uT43ciqzmXYZpB8mwYxMpyGk0EN/Gz0hzixJ4+Js2mU68qOWohJmpPGgr5g1+eaxL8aeLh+ooMgxXYLZozYUhWaJ99+0+tKV9GDarpbOMrfA6zE8hvCy62XChqZZINsPOA7G3KuFs54+BKNXFqKbsyA2f0MciDxQcmZgeTC8ci+rS1WZHyTMK5EPh6GhPK/QpoejG1JdXpcGdV94pN7R8flpT4iyMdGdbCfSeL7xjnmZs3BMvc5S50VZEtIdsD9BCeixQFf7GCZvuTJH4zxQHoPZMLc1T85ReBliiya9PvHcD6VfxTk4iuvodqIAC3J/pF5IP5fEhR/24XOZ/5gcEa92u8HulCp0LgfAeUxrgABo0a+ELvpysYBKQmwuTNU9ssFh9QA841RXvLh/JUnQAE8YTksS27PF50nhZap6C1OC5rOIRvIdrTgSgImjQ6TxhiA==';
var json_text=CryptoJS.AES.decrypt(json_text_cipher, passwd_raw).toString(CryptoJS.enc.Utf8);
var json=eval('('+json_text+')');

//URLに対応したログイン情報を取り出す
var url_key=document.location.protocol.replace(/[^A-Za-z]/g,'_')+'__'+document.location.hostname.replace(/[^A-Za-z]/g,'_')+document.location.pathname.replace(/\W/g,'_');
var j=json[url_key];

//自動入力する
for(var i=0;i<j.length;i++){
  if(j[i].type=='text'||j[i].type=='password'){
    if(document.getElementsByName(j[i].name).length > 0){
      document.getElementsByName(j[i].name)[0].value=j[i].value
    }else{
      document.getElementById(j[i].id).value=j[i].value
    }
  }else if(j[i].type=='radio'||j[i].type=='checkbox'){
    document.getElementsByTagName('input')[j[i].index].checked=j[i].checked
  }else if(j[i].type=='option'){
    if(document.getElementsByTagName('option')[j[i].index].value==j[i].value){
      document.getElementsByTagName('option')[j[i].index].selected=j[i].selected
    }
  }
}
alert('auto_login works!');
}, 500);//setTimeout


})()
  • ブックマークレット中に含まれるのは暗号化されたログイン情報である。
  • ログイン情報を復号するためのマスターパスワードの入力が、毎回必要になるが、
  • たとえ漏洩したとしても、パスワードを知らない限り、不正利用はできないのだ。

これでログイン情報は暗号化され、安全になった!

スターパスワードの照合

  • これでブックマークレットは安全になったが、一つ問題が発生した。
  • スターパスワードが正しければ正常に自動入力されるのだけど、
  • スターパスワードが間違っていると、何のリアクションもない。
  • 仮にパスワードが違っていた場合は「パスワードが違います」くらいの出力をしたいものだ。
  • 照合するためには、正しいマスターパスワードをどこかに保存しておく必要がある。
  • ところが、どこかに保存すると、それが漏洩したときの危険性が気になる。
  • できることなら、マスターパスワードはどこにも保存せずに、照合するのがベスト。
  • では、正しいマスターパスワードを知らずして、正誤判定する方法なんてあるのか?

ハッシュ値を使うと、それができる!

ハッシュ値の特性

  • 原文からハッシュ値を素早く求めることはできるが、
    • ハッシュ値から原文を推測することは相当困難。(現状は不可能)
  • 原文が同じ内容なら、必ず同一のハッシュ値になる。
    • 1文字でも違っていると、その値は大きく変化する。
  • 上記の特性を利用するとこで、パスワードの照合を安全に行うことができる。
    1. パスワードは原文を保存せず、そのハッシュ値を保存しておく。
    2. ユーザーがパスワード入力したら、そのハッシュ値を計算する。
    3. 上記1.と2.のハッシュ値の一致を確認すれば、入力されたパスワードが正しいと判定できる。
  • 仮にハッシュ値が漏洩しても、パスワード原文は推測できないので安全なのだ。
    • 一方、パスワード原文を保存していた場合は、それが漏洩した時に大問題になる。

opensslコマンドでsha256ハッシュ値を取得する

  • sha256を利用してマスターパスワードの照合を行うことにした。
    • sha1からsha256への移行が推奨されているため、sha256を利用した。
$ echo -n 1234 | openssl dgst -sha256
03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4

JavaScriptでsha256ハッシュ値を取得する

sha256ハッシュを計算するJavaScriptライブラリでも試してみた。

‎user1.matsumoto.ne.jp/~goma/js/sha1.js
  • サンプルコード
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>SHA-256サンプル</title>
</head>

<body>
<p>原文      : 1234</p>
<p id="encrypted_text">0</p>
</body>

<script src="http://user1.matsumoto.ne.jp/~goma/js/sha256.js"></script>
<script type="text/javascript">
    var encrypted = sha256.hex("1234");
    document.getElementById('encrypted_text').innerText = "sha256     : " + encrypted;
</script>
</html>
  • 上記サンプルコードをSafariで開いてみると...
原文      : 1234

sha256     : 03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4
crypto-js - JavaScript implementations of standard and secure cryptographic algorithms - Google Project Hosting
  • サンプルコード
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>sha256サンプル</title>
</head>

<body>
<p>原文      : 1234</p>
<p id="encrypted_text">0</p>
</body>

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha256.js"></script>
<script type="text/javascript">
    var encrypted = CryptoJS.SHA256("1234");
    document.getElementById('encrypted_text').innerText = "sha256     : " + encrypted;
</script>
</html>
  • 上記サンプルコードをSafariで開いてみると...
原文      : 1234

sha256     : 03ac674216f3e15c761ee1a5e255f067953623c8b388b4459e13f978d7c846f4


opensslコマンド・Javascriptライブラリ、いずれのハッシュ値も同一の値だ!

sha256でマスターパスワードを照合

$ echo -n MASTER_PASS | openssl dgst -sha256
3b70f81b4a16b6546ea8114e43be495ce154c3a1e92056a59d7af3db6a9914fd
  • 上記ハッシュ値を利用して、パスワードを照合するコードを追加してみた。
(function(){

//JavaScriptライブラリをロード
(function(e){
  e=document.createElement('script');
  e.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js';
  document.body.appendChild(e);
  e=document.createElement('script');
  e.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha256.js';
  document.body.appendChild(e);
})()

setTimeout(function(){
//マスターパスワードを照合する
var passwd_sha256='3b70f81b4a16b6546ea8114e43be495ce154c3a1e92056a59d7af3db6a9914fd';
var passwd_raw=prompt('auto_login Password','');
if(CryptoJS.SHA256(passwd_raw) != passwd_sha256){
  if(passwd_raw != ''){alert('Password is NG.');}return;
}

//暗号化ログイン情報を復号する
var json_text_cipher='U2FsdGVkX1/uT2sjZl3uT43ciqzmXYZpB8mwYxMpyGk0EN/Gz0hzixJ4+Js2mU68qOWohJmpPGgr5g1+eaxL8aeLh+ooMgxXYLZozYUhWaJ99+0+tKV9GDarpbOMrfA6zE8hvCy62XChqZZINsPOA7G3KuFs54+BKNXFqKbsyA2f0MciDxQcmZgeTC8ci+rS1WZHyTMK5EPh6GhPK/QpoejG1JdXpcGdV94pN7R8flpT4iyMdGdbCfSeL7xjnmZs3BMvc5S50VZEtIdsD9BCeixQFf7GCZvuTJH4zxQHoPZMLc1T85ReBliiya9PvHcD6VfxTk4iuvodqIAC3J/pF5IP5fEhR/24XOZ/5gcEa92u8HulCp0LgfAeUxrgABo0a+ELvpysYBKQmwuTNU9ssFh9QA841RXvLh/JUnQAE8YTksS27PF50nhZap6C1OC5rOIRvIdrTgSgImjQ6TxhiA==';
var json_text=CryptoJS.AES.decrypt(json_text_cipher, passwd_raw).toString(CryptoJS.enc.Utf8);
var json=eval('('+json_text+')');

//URLに対応したログイン情報を取り出す
var url_key=document.location.protocol.replace(/[^A-Za-z]/g,'_')+'__'+document.location.hostname.replace(/[^A-Za-z]/g,'_')+document.location.pathname.replace(/\W/g,'_');
var j=json[url_key];
if(j==null){
  alert('No auto_login info.');return;
}

//自動入力する
for(var i=0;i<j.length;i++){
  if(j[i].type=='text'||j[i].type=='password'){
    if(document.getElementsByName(j[i].name).length > 0){
      document.getElementsByName(j[i].name)[0].value=j[i].value
    }else{
      document.getElementById(j[i].id).value=j[i].value
    }
  }else if(j[i].type=='radio'||j[i].type=='checkbox'){
    document.getElementsByTagName('input')[j[i].index].checked=j[i].checked
  }else if(j[i].type=='option'){
    if(document.getElementsByTagName('option')[j[i].index].value==j[i].value){
      document.getElementsByTagName('option')[j[i].index].selected=j[i].selected
    }
  }
}
alert('auto_login works!');
}, 500);//setTimeout


})()


これでマスターパスワードの照合もできるようになった!

  • 状況に応じて、以下のメッセージを出力するのだ。
    • スターパスワードが違っていたら、'Password is NG.'
    • ログイン情報が見つからなかったら、'No auto_login info.'
    • 正常に自動入力できた場合は、'auto_login works!'

onloadすればsetTimeoutが不要

  • JavaScriptライブラリをロードする時間を考慮して、setTimeoutで大雑把に0.5秒待機する、という運を天に任せる的なコードは、常々どうにかしたいと思っていた。
  • 0.5秒も待つ必要ないかもしれないし、あるいはもっと待つ必要があるかもしれない。
  • 理想は、JavaScriptライブラリのロードが完了したタイミングで、本体のブックマークレットを実行するべき。

ロードが完了したタイミング=onloadイベントハンドラを使えばいいのだ!

  • scriptタグを追加する時に、onload属性を追加して、ブックマークレット本体の関数を呼び出すように変更してみた。
(function(f,e){
//JavaScriptライブラリをロード
e=document.createElement('script');
e.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js';
document.body.appendChild(e);
e=document.createElement('script');
e.src='//crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha256.js';
e.onload=function(){f()};
document.body.appendChild(e);

})(function(){
//マスターパスワードを照合する
var passwd_sha256='3b70f81b4a16b6546ea8114e43be495ce154c3a1e92056a59d7af3db6a9914fd';
var passwd_raw=prompt('auto_login Password','');
if(CryptoJS.SHA256(passwd_raw) != passwd_sha256){
  if(passwd_raw != ''){alert('Password is NG.');}return;
}

//暗号化ログイン情報を復号する
var json_text_cipher='U2FsdGVkX1/uT2sjZl3uT43ciqzmXYZpB8mwYxMpyGk0EN/Gz0hzixJ4+Js2mU68qOWohJmpPGgr5g1+eaxL8aeLh+ooMgxXYLZozYUhWaJ99+0+tKV9GDarpbOMrfA6zE8hvCy62XChqZZINsPOA7G3KuFs54+BKNXFqKbsyA2f0MciDxQcmZgeTC8ci+rS1WZHyTMK5EPh6GhPK/QpoejG1JdXpcGdV94pN7R8flpT4iyMdGdbCfSeL7xjnmZs3BMvc5S50VZEtIdsD9BCeixQFf7GCZvuTJH4zxQHoPZMLc1T85ReBliiya9PvHcD6VfxTk4iuvodqIAC3J/pF5IP5fEhR/24XOZ/5gcEa92u8HulCp0LgfAeUxrgABo0a+ELvpysYBKQmwuTNU9ssFh9QA841RXvLh/JUnQAE8YTksS27PF50nhZap6C1OC5rOIRvIdrTgSgImjQ6TxhiA==';
var json_text=CryptoJS.AES.decrypt(json_text_cipher, passwd_raw).toString(CryptoJS.enc.Utf8);
var json=eval('('+json_text+')');

//URLに対応したログイン情報を取り出す
var url_key=document.location.protocol.replace(/[^A-Za-z]/g,'_')+'__'+document.location.hostname.replace(/[^A-Za-z]/g,'_')+document.location.pathname.replace(/\W/g,'_');
var j=json[url_key];
if(typeof j==='undefined'){
  alert('No auto_login info.');return;
}

//自動入力する
for(var i=0;i<j.length;i++){
  if(j[i].type=='text'||j[i].type=='password'){
    if(document.getElementsByName(j[i].name).length > 0){
      document.getElementsByName(j[i].name)[0].value=j[i].value
    }else{
      document.getElementById(j[i].id).value=j[i].value
    }
  }else if(j[i].type=='radio'||j[i].type=='checkbox'){
    document.getElementsByTagName('input')[j[i].index].checked=j[i].checked
  }else if(j[i].type=='option'){
    if(document.getElementsByTagName('option')[j[i].index].value==j[i].value){
      document.getElementsByTagName('option')[j[i].index].selected=j[i].selected
    }
  }
}
alert('auto_login works!');
})