D04:ファイルを暗号化する

特定の人だけにファイルをダウンロードさせたい場合、パスワードの入力を求める方法などがありますが、サーバの環境によってはファイルのURLが分かればダウンロードできてしまう事があると思います。
今回は、ファイルを暗号化するようなCGIを紹介します。
ファイルを暗号化しておけば、ダウンロードされたとしても、キーが分からなければ複合化できない事になります。

ファイルを開く

解説
#!/usr/local/bin/perl
プロバイダがPerlを置いている場所を記述します。
ここでは/usr/local/bin/perlにあるとしています。
require "Blowfish_PP.pm";
Blowfishというライセンスフリーの暗号化モジュールを利用します。
☆今回のポイント☆
暗号化にはライセンスフリーモジュールを利用しよう!
$max=2;
送られてくるデータの数を$maxに代入しています。
データはファイル以外も可能です。
$size=$ENV{'CONTENT_LENGTH'};
送られてきたデータのサイズを$sizeに代入しています。
ただし、複数のデータが決まったフォーマットに従ってまとめて送られてくる為、実際にはデータ本体以外の部分も合わせたサイズになっています。
if($size>1024*1024){
  exit;
}
送られてきたデータのサイズが1MBより大きい場合にはプログラムを終了しています。
送られてきたデータがあまりにも大き過ぎると色々と不都合があるので、ここでは1MBに制限しています。
binmode(STDIN);
送られてくるファイルの種類を特に決めていないので、バイナリデータとして扱う為にbinmode(STDIN)を書いています。
while($size){
  $len=sysread(STDIN, $buf, $size);
  if($len==0){
    last;
  }
  $size=$size-$len;
  $data=$data.$buf;
}
送られてきたデータを$dataに代入しています。
$x=index($data,"\r\n");
$cut=substr($data,0,$x);
$cut="\r\n".$cut;
複数のデータが送られてくる場合、特定の文字列で区切られています。
その区切りの文字列を$cutに代入しています。
for($i=0;$i<$max;$i++){
  $x=index($data,"\r\n\r\n")+4;
  $filename[$i]=substr($data,0,$x);
  $filename[$i]=~s/\n//g;
  $filename[$i]=~s/.*filename="//;
  $filename[$i]=~s/".*//;
  $filename[$i]=~s/.*\\//g;
  $filename[$i]=~s".*/""g;
  $y=length($data);
  $data=substr($data,$x,$y-$x);
  $x=index($data,$cut);
  $y=length($data);
  $file[$i]=substr($data,0,$x);
  $data=substr($data,$x+2,$y-$x-2);
}
$maxの数だけ送られてきたデータを$file[xx]に代入しています。
データがファイルの場合はファイル名が$filename[xx]に代入されます。
xxに入る数字は0〜$max-1の数字になります。
binmode(STDOUT);
出力するデータをバイナリ形式で扱うのでbinmode(STDOUT)を書いています。
print "Content-type:application/octet-stream\n";
print "Content-Disposition:attachment;filename=e_$filename[1]\n\n";
出力するデータがダウンロードする形式である事を出力します。
$key_in="12345678" . $file[0];
$change=new Crypt::Blowfish_PP($key_in);
キーは8バイト以上必要なので、入力が空だった場合でも暗号化できるように、12345678という文字列を先頭に追加しています。
$key_inをキーとして暗号化する準備として$changeを作成しています。
@data_in=unpack("C*",$file[1]);
送られてきたデータを1バイト単位の配列にしています。
$len=$#data_in+1;
$dummy=$len % 8;
if($dummy==0){
  $dummy=8;
}
$loop=int(($len+$dummy)/8);
@data_inは0〜$#data_inの配列になります。
暗号化は8バイトずつ行うので、データのサイズが8で割り切れるように、$dummyのバイト分だけダミーデータを付加します。
また、$loopで暗号化する回数を計算しています。
for($i=0;$i<$dummy;$i++){
  $data_in[$len+$i]=~$data_in[$len-1];
}
ダミーデータは最後の1バイトをビット反転したものにします。
for($i=0;$i<$loop;$i++){
  $data_block="";
  for($j=0;$j<8;$j++){
    $data_block=$data_block . pack("C",$data_in[$i*8+$j]);
  }
  $data_out=$change->encrypt($data_block);
  print $data_out;
}
データを暗号化して出力しています。
exit;
プログラムを終了させます。