最近はウェブサービスが乱立している事もあり、パスワードが漏洩する事件も良く聞くようになりました。この時に複数のサイトでパスワードが共通にしている人は、被害が他のサイトにも拡大していく可能性があります。
それを避けるために複数サイトで異なるパスワードを使うように推奨されていますが、人間がパスワードを生成すると何かの単語になりがちです。Appleのように厳しいパスワードを求めてくるサイトもあるので、自動で生成してみましょう。
#
# 'A', 'B', 'C' ... と言うパスワードに用いる文字のベクターを作る関数
# begin: 開始文字
# len: 長さ
# exception: 除外する文字
#
make_set <- function(begin, len, exception=c()){
v <- strsplit(rawToChar(as.raw(0:(len-1) + as.integer(charToRaw(begin)))), "")[[1]]
v[!v %in% exception]
}
#
# A〜Z、a〜z、0〜9のベクターを作る
# ただし紛らわしいIとl、Oと0は除外
#
chr_set_A <- make_set("A", 26, c("I", "O"))
chr_set_a <- make_set("a", 26, c("l"))
chr_set_0 <- make_set("0", 10, c("0"))
# 三つを合成する
chr_set <- c(chr_set_A, chr_set_a, chr_set_0)
# 乱数を初期化
# 注意:Seedの生成方法が分かるとパスワードを特定されやすくなるので、ある程度の長さの任意の数字(secret_code)を加えるなどした方が安全。
secret_code <- 192837465
set.seed(as.integer((1000*as.numeric(format(Sys.time(),"%y%j%H%M%OS3")) + secret_code) %% .Machine$integer.max))
#
# Apple IDを意識して、三種類の文字がないと脆弱パスワードと見なす関数
# clst: パスワードに使う文字のベクター
#
is_strong <- function(clst){
if(!any(clst %in% chr_set_A)){
return(FALSE)
}
if(!any(clst %in% chr_set_a)){
return(FALSE)
}
if(!any(clst %in% chr_set_0)){
return(FALSE)
}
# 三連続同一文字の禁止
len <- length(clst)
if(3 > len){
return(FALSE)
}
for(i in 1:(len-2)){
if(clst[i]==clst[i+1] & clst[i]==clst[i+2]){
return(FALSE)
}
}
TRUE
}
#
# 強度のあるパスワードを作る関数
# args: [パスワードの長さ#1],[パスワードの長さ#2]...
#
mkpasswd <- function(...){
digit <- length(chr_set) # パスワードの各桁の文字種の数
# Rらしく引数の数は不定
len <- c(...)
num <- length(len)
# 戻り値を初期化しておく
r <- character(num)
for(i in 1:num){
# 3以下のパラメーターは計算できないので、除外
if(3>len[i]){
r[i] <- NaN
next
}
# 強度を満たすまで生成しなおす
clst <- c()
while(!is_strong(clst)){
# +0.5がないと、最初の一文字の発生確率が半分になってしまう
clst <- chr_set[round(runif(len[i], 0, digit) + 0.5)]
}
# collapse引数が無いとベクターは合体しない
r[i] <- paste(clst, collapse="")
}
r
}
# 8文字のパスワードを作ってみる
sprintf("生成されたパスワード: %s", mkpasswd(8))
本当の問題は、どうやって大量のパスワードを管理するかなのですが、強度のあるパスワードを簡単に作る事ができます。