配列と連想配列

目標

多くのプログラミング言語は配列などのデータ構造を持つ。
JavaScriptも例外ではなく、配列などのデータ構造を持っている。
ここでは、JavaScriptが持つこれらのデータ構造について学ぶ。

  • 配列の概念について理解する。
  • JavaScriptのプログラムの中で、多次元配列を作ることができるようになる。
  • JavaScriptのプログラムの中で、多次元配列を参照することができるようになる。また、要素の追加、結合、取得、削除ができるようになる。
  • 連想配列の概念について理解する。
  • JavaScriptにおける連想配列とオブジェクトの関係について理解する。
  • JavaScriptのプログラムの中で、連想配列を作ることができるようになる。
  • JavaScriptのプログラムの中で、連想配列を参照することができるようになる。また、要素の追加、結合、取得、削除ができるようになる。
  • 強いパスワードとはどういうものかを理解する。
  • TLSについて理解する。

配列

配列の生成

配列は、ひとつの変数を区切ってたくさんのデータ(配列の要素と呼ぶ)を保存できるようにしたものである。
データは順番を持つ箱に格納される。
データを保存したり取り出したりする時は、変数名[番号]という形で指定する。
配列では、添字に数字を使い、番号は0から始まる。
添字とはカギ括弧の中に書かれる値のことである。

配列
配列

配列の生成方法には、下記のようなものがある。
下記の例は全て同じ配列を生成している。

JavaScript
// パターン1
var array = ['hoge', 'fuga'];

// パターン2
var array = new Array('hoge', 'fuga');

// パターン3
var array = Array('hoge', 'fuga');

// パターン4
var array = [];
array[0] = 'hoge';
array[1] = 'fuga';

// array[0] が 'hoge'
// array[1] が 'fuga'

配列の操作

前にも述べたように、配列には、様々なメソッドが用意されている。
例えば、2つの配列を連結したい場合には次のようにする。
この例では、array1とarray2のそれぞれの配列を始めに生成し、その2つを4行目で結合して、arrayという変数に代入している。
結果としてarrayの中身は「[“佐藤”, “鈴木”, “田中”, “渡辺”, “伊藤”, “山本”]」となる。

JavaScript
var array1 = ["佐藤", "鈴木", "田中"];
var array2 = ["渡辺", "伊藤", "山本"];

var array = array1.concat(array2);

他にも下記の表のようないろいろなメソッドが用意されている。
ただし、基になる配列を変更してしまうもの(破壊的操作)と、concat()のように新しい配列を生成してそれを返すもの(非破壊的操作)があるので注意が必要である。

メソッド 説明
concat(ary) aryで指定した配列を現在の配列の後ろに連結した新しい配列を生成し、それを返す。
join(str) 配列の各値を 区切り文字列str でつなぐことによって得られる文字列を返す。
pop() 配列末尾の要素を値として返し、その後その要素を削除する。
push(val) valで指定した値を要素として配列の最後に加える。
reverse() 配列の要素の並び順を反転させる。
shift() 配列先頭の要素を値として返し、その後その要素を削除する。
slice(start[, end]) startからend-1番目の要素を抜き出す。
sort([func]) 要素を昇順に並び替える。funcで並び替えに使う関数を指定することもできる。
splice(start, cnt [, rep [, …]]) 配列内のstart〜start+cnt-1番目の要素をrepで置き換える。
toString() 要素を「,」で区切って並べた文字列を返す。
unshift(val) valで指定した値を要素として配列の最初に加える。

また、メソッドではないが、プロパティとして「length」がある。
lengthを使うことによって配列の要素の数を知ることができる。

プロパティ 説明
length 配列の中の要素の数

連想配列

配列の添字に意味のある文字列を使うと、ある文字列に関係する値をすぐに引き出すことができる。
例えば、学籍番号の入った配列を作ってその添字に名前を使うとすると、名前をもとに配列にアクセスすることにより、すぐに学籍番号を引き出すことができる。
このような配列のことを一般的に連想配列と呼ぶ。

JavaScriptには厳密には連想配列は存在しないが、Objectを使うことによって同様のことを実現できる。
下記の例では、objという変数に’taro’と’jiro’という2つの値が格納されており、それぞれにアクセスするための添字(ラベル)が’sato’と’suzuki’となっている。

JavaScript
// パターン1
var obj = { sato: 'taro', suzuki: 'jiro'};

// パターン2
var obj = { 'sato': 'taro', 'suzuki': 'jiro' };

// パターン3
var obj = {};
obj.sato = 'taro';
obj.suzuki = 'jiro';

// パターン4
var obj = {};
obj['sato'] = 'taro';
obj['suzuki'] = 'jiro';

// パターン5
var obj = new Object();
obj.sato = 'taro';
obj.suzuki = 'jiro';

// obj['sato'] が 'taro'
// obj['suzuki'] が 'jiro'

二次元配列

表計算のようなことをしようとする場合、これまで使ってきた配列だけでは機能が不足することがある。
例えば、次のようなデータがあるとしよう。

日付 始値 高値 安値 終値
2011年1月10日 82.690000 83.540000 82.379900 82.940000
2011年1月17日 82.800000 83.489900 81.819900 82.580000

これは、2011年の週毎のドル円の為替データである。
このデータを扱うためにはどのようにすれば良いでだろうか?
1つ考えられる方法としては、それぞれのカラムを配列として扱うことである。
例えば次のようになる。

var date =  ["2011年1月10日", "2011年1月17日", ...];
var start = [82.690000, 82.800000, ...];
var high =  [83.540000, 83.489900, ...];
var low =   [82.379900, 81.819900, ...];
var end =   [82.940000, 82.580000, ...];

確かにこれでもデータを扱うことはできるが、もう少し良い方法がある。
この様な時に使えるのが二次元配列である。
二次元配列とは表のように列と行によってデータを格納するための配列である。
例えば、次のように記述する。

var usdjpy = [
  ["2011年1月10日",82.690000,83.540000,82.379900,82.940000],
  ["2011年1月17日",82.800000,83.489900,81.819900,82.580000],
  ...
];

この例では、日付・始値・高値・安値・終値を1つの行として配列にし、更に週毎にその配列を作っている。
つまり「[“2011年1月10日”,82.69,83.54,82.3799,82.94]」もひとつの配列だし、それを集めた全体も配列である。
上の例ではusdjpyという変数には配列の配列、二次元配列が格納される。

さて、データを二次元配列に入れる方法は説明したが、これはどのようにして使えば良いのでだろうか?
使い方は二次元配列が配列の配列であることが解れば難しくない。
例えば、1行目の2列目を使うためには「usdjpy[0][1]」のように参照する。
配列は0番目から始まるため、1行目を表す添字は0になるし、2列目を表す添字は1となる。

var usdjpy = [
  ["2011年1月10日",82.690000,83.540000,82.379900,82.940000],
  ["2011年1月17日",82.800000,83.489900,81.819900,82.580000]
];
 
alert(usdjpy[0][1]);

実際に上のプログラムを実行すると、「82.69」という数字がアラートで表示されます。

セキュリティ

ネットワークを使うにあたって、セキュリティについて知っていることは非常に重要である。
本節では、配列の利用例もおりまぜながらセキュリティに関するスキルを身に着ける。

パスワードの強さと攻撃

皆さんに身近なセキュリティの話題の1つにパスワードがある。
パスワードはシステムにログインする場合など、様々な場面で使われる。
パスワードをつける際には、ある程度以上の長さにしなければいけない、大文字と小文字を混ぜるべき、数字や記号を入れるべき、辞書にある単語をそのまま使わない、などと言われることが多い。
これはなぜなのだろうか。
ここでは、実際の例などを示しながらその重要性を考えてみたい。

ブルートフォース攻撃

あるパスワードを破れ、と言われたらみなさんどういう方法を試すだろうか。
おそらく一番始めに思いつくのは総当りでは無いでだろうか。
これは力業だが、パスワードが短い場合は有効な手段である。
そのため、このような手段を、「力ずくで」、「強引に」という意味の「brute force」を使って、ブルートフォース攻撃という。

下のようなタイプの鍵を見たことは無いだろうか。
この鍵はそれぞれのリングに0〜9の数字が書かれており、3つの番号を合わせることによって鍵を開けることができる。

key

もし、3桁の0〜9までの数字のパスワードだということがわかっている場合、コンピュータがそのパスワードを解くのにどれくらいかかるだろう。
次のプログラムを見て欲しい。
このプログラムでは、3重ループのfor文の前後で時刻を計測し、その差、つまりかかった時間をalert()で表示している。
3重ループの部分では000〜999までの数字を表示させている。
0〜999を数え上げることも、コンピュータにとってはあっという間も無いような時間であることが確認できる。

HTML部分
<input type="button" value="3桁の0〜9までの数字、総当り" onclick="n3()">
<input type="text" id="text080201" value="000">
JavaScript部分
function n3() {
    var str = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
    var ele = document.getElementById("text080201");

    var t_start = (new Date).getTime();

    for (var i=0; i<str.length; i++) {
        for (var j=0; j<str.length; j++) {
            for (var k=0; k<str.length; k++) {
                ele.value = str[i] + str[j] + str[k];
            }
        }
    }

    var t_end = (new Date).getTime();

    alert("かかった時間は" + (t_end - t_start) + "ミリ秒です");
}
動作例



このように、0〜999まで数え上げるくらいのことはコンピュータにとっては非常に短時間に済んでしまう作業である。
このため、数字だけの短い文字列のパスワードは推奨されないのである。

では、小文字のアルファベットまで使えるようになるとどう変化するだろうか。

HTML部分
<input type="button" value="3桁の0〜9までの数字と英小文字、総当り" onclick="na3()">
<input type="text" id="text080202" value="000">
JavaScript部分
function na3() {
    var str = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
               "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
               "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
    var ele = document.getElementById("text080202");

    var t_start = (new Date).getTime();

    for (var i=0; i<str.length; i++) {
        for (var j=0; j<str.length; j++) {
            for (var k=0; k<str.length; k++) {
                ele.value = str[i] + str[j] + str[k];
            }
        }
    }

    var t_end = (new Date).getTime();

    alert("かかった時間は" + (t_end - t_start) + "ミリ秒です");
}
動作例



総当りにかかる時間が、だいぶ長くなったことが確認できただろう。

辞書攻撃

それでは、文字の種類を増やしたり、桁数を増やせば安心なのだろうか。
そうでもない。
一般的な単語をパスワードとして使ってしまうと、思いがけず素早くパスワードを当てられてしまうことがある。
辞書攻撃と言われる攻撃で、攻撃する側が辞書を持っていて、それを順に試してくるのである。

例えば、"university"という文字列があったとする。
10文字もあるので、ブルートフォース攻撃ではそこそこの時間がかかる。
しかし、辞書攻撃を用いれば、これもあっという間にパスワードを見つけることができる。
英語の辞書に載っている単語は、多くても数十万程度であろう。
少し多く見積もって100万語の辞書を使ってパスワードを使って試してみたとする。
これは実は、6桁(000000〜999999)のパスワードを試すことと同じで、コンピュータにとっては大した時間ではない。

これらの理由により、パスワードをつける際には、「ある程度以上の長さにしなければいけない、大文字と小文字を混ぜるべき、数字や記号を入れるべき、辞書にある単語をそのまま使わない」と言われるのである。

Webページのパスワード認証

Webページにアクセスをしていると、パスワードを求められることがある。
これは、Basic認証やDigest認証と呼ばれる機能を使って実現できる。
Basic認証もDigest認証もIDとパスワードのペアによってユーザを認証しているが、Basic認証の場合は暗号化されていないパスワードがネットワークを流れる。
このため、可能であればDigest認証を使うべきである。
以前は、Digest認証に対応していないブラウザもあったが、現在では非常に稀で、ほぼすべてのブラウザがDigest認証に対応している。

また、後に述べるTLSを用いることによって、通信路そのものを暗号化してしまうことも可能である。
この場合は、Basic認証でもパスワードは暗号化された通信路の中を流れることになる。

Digest認証の基本的な使い方は、あるディレクトリの下にアクセスするためにID/パスワードを求めるというものである。
例えば、public_html/info2/08 というディレクトリの下にアクセスする、
つまり https://web.sfc.keio.ac.jp/~[ログイン名]/info2/08/ にアクセスするときにID/パスワードを求めるようにするためには、
public_html/info2/08/.htaccessというファイルを作る必要がある。
public_html/info2/08/.htaccess には次のような内容を記載する。
最後に空行を入れることに注意して欲しい。

AuthUserFile	/home/[ログイン名]/.htdigest
AuthName	"Members Only"
AuthType	Digest
require 	valid-user
 

その後、Digestファイルを作成する。
Digestファイルは、ターミナルを立ち上げて、下記のようにして作成する。
ここで「$」から始まる行が実際に入力する行である。
ただし、「$」はプロンプトで入力する必要はない。

$ /usr/sbin/htdigest -c ~/../.htdigest "Members Only" [ユーザ名1]
Adding password for [ユーザ名1] in realm Members Only.
New password:
Re-type new password:
$ /usr/sbin/htdigest ~/../.htdigest "Members Only" [ユーザ名2]
Adding password for [ユーザ名2] in realm Members Only.
New password:
Re-type new password:

1番始めの行の「-c」は新しいファイルを作成することを指定している。
2人目以降のユーザを追加する際には「-c」は指定しない。
[ユーザ名1][ユーザ名2]のところには、ログインに使いたいユーザ名を指定する。
そうするとパスワードを聞かれるので、2回入力する。
これでDigestファイルが作成される。

より詳しくは http://www.sfc.itc.keio.ac.jp/ja/network_web_server.html を参照してほしい。

TLS

これまでは、作成したWebページにアクセスするためには、「http://...」といった感じのURLを使っていた。
これはプレーンなHTTPを使ってWebにアクセスすることを示している。
しかし、プレーンなHTTPでは、通信は暗号化されておらず、通信が傍受された場合には通信内容が覗き見られてしまう可能性がある。

このような問題を解決するためには、Transport Layer Security(TLS)を使う。
以前は、Secure Sockets Layer (SSL)と呼ばれる技術が使われていたが、現在は強固な暗号化に対応したより洗練されたTLSが開発され、WebにおいてもTLSが利用されている。
ただ、世の中では実際にはTLSが使われている場合でも、「SSL通信」のように呼ばれていることも多く、少々混乱しているので注意が必要である。

WebにTLSを使ってアクセスする場合は、「http://...」の代わりに「https://...」を使う。
最後の「s」がTLSを使っていることを示している。
「https://...」と指定することによって、WebサーバとWebブラウザの間で実際に使用するプロトコルのバージョンなどが調整され、暗号化された通信路が確立される。

httpとhttps

一般的にTLSの接続が確立されるときには、サーバ側からクライアント側に「サーバ証明書」と呼ばれるものが送られてくる。
サーバ証明書の確認には、公開鍵暗号系の暗号が使われる。
公開鍵暗号系の暗号の特徴は次のとおりである。
公開鍵暗号系では、公開鍵と呼ばれる広く公開してよい鍵と、秘密鍵と呼ばれる本人しか知らない鍵のペアが使われる。
その上で、秘密鍵で暗号化した文書は公開鍵で復号可能、公開鍵で暗号化した文書は秘密鍵のみで復号可能である。
このため、秘密鍵で暗号化された文書を見ると、間違いなく秘密鍵をもっている人がその暗号化された文章を作ったことが確認できる。
また逆に、公開鍵で暗号化された文書は秘密鍵を持っている人しか復号することができない。

先に述べた「サーバ証明書」、通信相手が確かに意図したサーバかを確認するために使うもので、認証局と呼ばれる機関が秘密鍵で署名(暗号化)をしている。
認証局の公開鍵は広く配られているので、その鍵を使って送られてきた「サーバ証明書」が正しいかどうかは誰でも確認(復号)できる。
ちなみに、認証局は階層構造で管理されており、その大元締めとなっている認証局の公開鍵は、ブラウザと一緒に配布されている。

GlobalオブジェクトとMathオブジェクト

Globalオブジェクト

システムで用意されている関数は、どこかのオブジェクトに属しています。Globalオブジェクトは、JavaScriptで最も基本的なオブジェクトです。以下にGlobalオブジェクトに属している関数の例を挙げます。

メンバ 意味
parseInt(str) 文字列strを整数に変換する。小数点以下は切り捨てる。
parseFloat(str) 文字列strを実数に変換する。
String(val) valを文字列に変換する。
Number(val) valを数字に変換する。

他にもたくさんの関数がありますので、自分でも調べてみてください。

Mathオブジェクト

三角関数や平方根など、数学に関わる演算機能を提供するのがMathオブジェクトです。例えば、Math.abs(数値)とすることによって、数値の絶対値を得ることができます。下に主要なMathオブジェクトの関数を挙げておきます。メンバがabs(num)の時には、先ほどのようにMath.abs(num)と記述します。

メンバ 意味
abs(num) numの絶対値を求める。
ceil(num) numの小数点以下を切り上げる。
floor(num) numの小数点以下を切り捨てる。
round(num) numの小数点以下を四捨五入する。
max(num1, num2) num1、num2のうち大きい数を求める。
min(num1, num2) num1、num2のうち小さい数を求める。
pow(num1, num2) num1のnum2乗を求める。
sqrt(num) numの平方根を求める。
exp(num) 指数関数。eのnum乗を求める。
log(num) numの自然対数を求める。
sin(num) サインを求める。numはラジアン。
cos(num) コサインを求める。numはラジアン。
tan(num) タンジェントを求める。numはラジアン。
random( ) 0〜1未満の乱数を返す。

上記の中でrandom()だけは、引数をとらないちょっと風変わりなものになっています。random()は、何も無いところから0〜1未満の乱数を生成します。乱数というのは毎回変わる適当な値です。例えば、「n = random()」のようにすると、nには0〜1未満の値(0.1や0.2345)が入ります。

また、Mathオブジェクトには定数も設定されています。これは関数ではありませんが、以下に挙げておきます。例えば、円周率πを使いたい場合には、プログラムの中で「3.1415...」と書く代わりに「Math.PI」と書くことができます。

メンバ 意味
PI 円周率π。
E 自然対数の底。あるいはネイピア数e。

練習問題8-

配列を使った簡単なおみくじプログラムを作成しなさい。
ただし、JavaScriptのスケルトンは次のとおりである。

function mikuji(){
  var unsei = new Array("大吉","中吉","小吉","吉","末吉","凶","大凶");
  var uranau = /* ここを書く */;

  document.getElementById("res").innerHTML = /* ここを書く */;
}

練習問題8-

上の例で出てきた、数字3桁のブルートフォース攻撃をためし、かかった時間をアラートで表示するプログラムを作成しなさい。

練習問題8-

小文字のみ使った英数字3桁のブルートフォース攻撃をためし、かかった時間をアラートで表示するプログラムを作成しなさい。

練習問題8-

大文字小文字の両方を使った英数字3桁のブルートフォース攻撃をためし、かかった時間をテキストエリアに表示するプログラムを作成しなさい。

練習問題8-

数字6桁のブルートフォース攻撃をためし、かかった時間をテキストエリアに表示するプログラムを作成しなさい。
これは、100万単語から成る辞書を使った辞書攻撃と同じ時間である。

練習問題8-

認証局は証明書を発行する機関であり、多くの場合一般企業である。
また、証明書は電子ファイルであるため、生成するのに費用はほとんどかからない。
これはボロ儲けのチャンスである。
しかし実際にはそうはなっていない。
なぜ認証局を立ち上げるのが難しいか考え、その考えを示したWebページを作成しなさい。

またそのページに、ID: info2, Password: Keio#University でアクセスできるようにDigest認証をかけなさい。