Windowsの起動・終了時にバッチファイルを実行させて勤怠管理

以前イベントログを読んで勤怠管理をするというエントリを書いたが
https://monqy.blogspot.com/2019/04/powershell_16.html

もっと簡単な方法がある。

以下のような二つのバッチファイルを作る。


「hajimari.bat」
echo start:  %date% %time% >> c:\users\taro\working.txt

「owari.bat」
echo end:  %date% %time% >> c:\users\taro\working.txt

グループポリシーエディタを起動

「コマンドを指定して実行」で下記を実行

gpedit.msc


左側のツリーで「ローカルコンピューターポリシー」「コンピューターの構成」「Windowsの設定」「スクリプト(スタートアップ/シャットダウン)」

を選択

右側の「スタートアップ」、「シャットダウン」をそれぞれダブルクリックし、
「追加...」をクリックしてバッチファイルを選択する。


コマンドプロンプトで下記コマンドを実行してポリシーの変更を反映させる。

gpupdate

バッチファイルのリダイレクト先はパスをつけないと、
バッチファイルのある場所ではなく、変な場所に保存される。
しかも、スタートアップとシャットダウンで別々に。

同じファイルに書き込みたいので、パスをつける。

こっちのほうが簡単。

「勤怠管理」といってもあくまでも簡易的なものなので。

細かいことは言わないでください。






HTTP通信が安全でない理由


前回のエントリ

で実行したcgiが、もしHTTP通信で実行されていたら、
その通信をキャプチャすると下記のように入力したパスワードがわかる。


これが「暗号化されていない通信(HTTP)は危険」である理由である。

「キャプチャすると通信内容がわかる」ということは、その通信が経由する装置、回線、サーバ、等にアクセスできる人々すべてに内容を把握されるおそれがあるということである。

これを防ぐためには、「暗号化」が必要である。

暗号化していない通信であれば、パスワードそのものでなくハッシュを保存していたとしても、サーバ管理者もユーザが入力したパスワードを知ることができてしまう。


「パスワードを暗号化して保存する」ではダメな理由

perlでcgiを作ってみた。

ハッシュアルゴリズムにmd5を使っているが、
md5もはや安全ではないらしい。

このスクリプトはあくまでもハッシュを作ることを実感するためのサンプルである。
実際にはより安全なアルゴリズムに変える必要がある。



ユーザ名とパスワードを登録するcgi

register_pwd.cgi
---
#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "register password";
print '<form action="save_pwd.cgi" method="post">';
print '<p>';
print 'name:<input type="text" name="username" size="20">';
print '</p>';
print '<p>';
print 'password:<input type="text" name="password" size="20">';
print '</p>';
print '<p>';
print '<input type="submit" value="送信"><input type="reset" value="リセット">';
print '</p>';
print '</form>';
---


ユーザ名とパスワードのソルト付きハッシュを保存するcgi

users.txt というファイルに保存する。
すでに同じユーザ名が登録されているかのチェックもする。

ソルトの作り方がアレかもしれないが、
ここもサンプルなので見逃してほしい。


save_pwd.cgi
---
#!/usr/bin/perl

use Digest::MD5 qw(md5 md5_hex);

if ($ENV{'REQUEST_METHOD'} eq 'POST') {
  read(STDIN, $alldata, $ENV{'CONTENT_LENGTH'});
} else {
  $alldata = $ENV{'QUERY_STRING'};
}
foreach $data (split(/&/, $alldata)) {
  ($key, $value) = split(/=/, $data);

  $value =~ s/\+/ /g;
  $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
  $value =~ s/\t//g;

  $in{"$key"} = $value;
}

print "Content-Type: text/html; charset=Shift_JIS\n\n";
print "<html>\n";
print "<head><title>save password</title></head>\n";
print "<body>\n";

$my_user = $in{'username'};
$my_pwd = $in{'password'};

print "<p>username: $my_user</p>\n";
print "<p>password: $my_pwd</p>\n";

$digest = md5_hex($my_pwd);

print "md5 digest:".$digest."<br>";

$num = int(rand(1000000));

$salt = '$1$'.$num.'$';

print '<br>'.$salt.'<br>';

$crypt = crypt($my_pwd, $salt);

print "md5 with salt: ".$crypt."<br>";

print '<br>';

$count = 0;

open (IN, "users.txt");
while(<IN>){
        @tmp_user = split(/,/,$_);
        if (@tmp_user[0] eq $my_user){
                $count++;
        }
}

if ($count > 0 ) {
        print("username ".$my_user." has been already registered.<br>");
}else {
        open (OUT,">> users.txt");
        print OUT $my_user.",".$crypt."\n";
        close (OUT);
        print 'registered<br>';
        print '<a href="/index.html">'.'index.html'.'</a>';
}
print "</body>\n";
print "</html>\n";

exit;
---

パスワード確認cgi
(ログインのイメージ)

kensho_pwd.cgi
---
#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "verify password";
print '<form action="hash_kensho.cgi" method="post">';
print '<p>';
print 'name:<input type="text" name="username" size="20">';
print '</p>';
print '<p>';
print 'password:<input type="text" name="password" size="20">';
print '</p>';
print '<p>';
print '<input type="submit" value="送信"><input type="reset" value="リセット">';
print '</p>';
print '</form>';

exit;
---

ユーザの入力したユーザ名を保存したユーザ名から検索し、
保存されているソルトを付加してハッシュを計算し、保存されているハッシュと比較する

users.txtを読んで、同じユーザ名を探す。

実際はDBを使うなどするのだろうがこれは実験なので。

---
#!/usr/bin/perl

use Digest::MD5 qw(md5 md5_hex);

if ($ENV{'REQUEST_METHOD'} eq 'POST') {
  read(STDIN, $alldata, $ENV{'CONTENT_LENGTH'});
} else {
  $alldata = $ENV{'QUERY_STRING'};
}
foreach $data (split(/&/, $alldata)) {
  ($key, $value) = split(/=/, $data);

  $value =~ s/\+/ /g;
  $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('C', hex($1))/eg;
  $value =~ s/\t//g;

  $in{"$key"} = $value;
}

print "Content-Type: text/html; charset=utf-8\n\n";
print "<html>\n";
print "<head><title>password kensho</title></head>\n";
print "<body>\n";

$my_user = $in{'username'};
$my_pwd = $in{'password'};

print "username: $my_user<br>";
print "password: $my_pwd<br>";

open (IN,"users.txt");
#$record = <IN>;

$count=0;
$tmp_user="";
$tmp_digest="";

while(<IN>){
        @tmp = split(/,/,$_);
        if(@tmp[0] eq $my_user){
                $count++;
                $tmp_user = @tmp[0];
                $tmp_digest = @tmp[1];
        }
}
close (IN);

if($count<1){
        print "unknown username<br>";
        exit;
}

chomp($tmp_digest);


$salt = substr($tmp_digest,0,10);

$crypt = crypt($my_pwd, $salt);

print "md5 with salt: ".$crypt."<br>";
print "saved md5    : ".$tmp_digest."<br>";

if ($tmp_digest eq $crypt ) {
        print "Welcome, ".$my_user." !!<br>";
}else{
        print "Incorrect password<br>";
}

print "</body>\n";
print "</html>\n";

exit;
---

実行例

ユーザ名とパスワードを入力

保存されたソルト付きハッシュ
上に表示されているのは、ソルトなしのハッシュ
$で挟まれているのがソルト。
ハッシュの先頭に付加されるのでそれを含めて保存する。


登録したユーザ名とパスワードを入力する


保存してあるハッシュと一致した。


違うユーザ名で同じパスワードを登録してみる。

jiro, password



ソルトなしのハッシュは、taro, passwordの場合と全く同じ

ソルト付きのハッシュは、同じパスワードから生成したものであっても、
taroのものと異なる。





「暗号化して保存する」と「ハッシュを保存する」は違う。

暗号化したデータは、しかるべき操作をすると元のデータが復号できるが、
ハッシュはできない。

もし悪意あるものがサーバにアクセスしてハッシュを入手したとしても、
できることは、元のデータを推測して、
そのデータのハッシュを計算して比較し、同じであるかどうかを確認することだけである。

ハッシュでできることは、元のデータを復元してそれを入力された値と比較することではない。
比較するのはあくまでもハッシュであり、
サーバ管理者でさえも、ユーザのパスワードが何かはわからないのである。
もしパスワードを暗号化して保存し、それを復号化して比較するのであれば、
サーバ管理者がユーザのパスワードを知ってしまうことになる。

また、もし暗号化されたパスワードを保存しているサーバにアクセスされてしまったら、
高確率で復号化する仕組みにもアクセスされるだろう。

そのことを知らなかったら、「暗号化して保存したってどうせ復号するんだから意味はない」と考えて、平文で保存してしまうのではないだろうか?

だから、「暗号化して保存する」と「ハッシュを保存する」の区別は重要なのである。


・・・と思ったのだが、記事を一回公開してから気づいた。
ハッシュを計算するときに、見ようと思えばユーザのパスワードは知ることができる。

先ほどのperlのスクリプトで、ユーザがフォームに入力したパスワードをprintで表示すればよい。
(実際、している。もちろん、本当に実装するときにそんなことはしないが。)

フォームにパスワードを入力してサーバに送信する前に、ハッシュを計算して、その結果を送信しなければいけないのか?

そんなことは不可能か?

通信が暗号化されていればサーバに到達するまではパスワードは他者に知られないが、

ハッシュ計算時には知ることができる。

それを保存さえしなければいいのか?


またこの時、もしソルトを付加せずに計算したハッシュを保存したらどうなるか。

1万人のユーザのハッシュの中で、同じものが50件あったとすると、
そのパスワードは誰でも思いつくパスワードであると想像でき、元のデータの推測が容易になってしまう。

情報処理安全確保支援士試験(平成31年度春期)

情報処理安全確保支援士試験を受けた。

日曜日。

去年の秋にネットワークスペシャリストに受かったので午前1は免除。

会場は日大経済学部。水道橋にある。立派な校舎だ。しかも経済学部と書かれたビルがいくつもある。

早めに出かけたが、試験開始の1時間半も前についてしまう。

いくらなんでも早すぎだ。

校舎の前にベンチのようなものがあったのでそこに座ったが日陰で寒いので、ドトールに移動した。


30分くらい前に会場に入った。まだ午前1の試験が終わっていない時間だが、会場に人はまばらで試験は実施されていない。午前1免除の人たちだけの会場だったようだ。

情報処理技術者試験は何度か受けたが空席が多い。受験申し込みをしても当日来ない人が
けっこういるのだ。私も申し込んで受けなかったことが一度ある。

NWのときは3、4割くらいだったと思うが、今回は半分以上埋まっていた。けっこうな欠席率だが、情報処理技術者試験にしては出席率は良いほうだと感じた。午前1免除の人達だからということもあっただろう。

20分前に席に着くようにとの指示が受験票に書いてあるが、平然と10分前くらいに来る人がいる。

まったく悪びれる様子もない。試験官も特に注意することもない。

午後1のときは試験が始まってから入室してきたつわものがいた。やはり無表情であせりさえしていない。

午前2の1時間後に午後1が始まるのだが、20分前に着席しなければならないので、昼休みは40分しかない。

それはもうわかっていたので、コンビニでおにぎりを買っておいた。

肝心の試験の内容であるが、簡単だという印象。

午前2は全部解いて、正解だと確信できない問題が11問。

でも、見直しても多分これしかないだろう、という答え。

合格点は60点だから、10問間違えられる。

昔の情報処理試験は午前問の合格点は8割だったと記憶しているが...

確信できないといっても11問のうち半分まちがっているとしても余裕だ。

後で自己採点したら1個しか間違っていなかった。今までこんな高得点はとったことがない。



午後1は3問から2問選択。1問目によく知らないスクリプトのコードが書いてあったので、2・3問を選択。

2問目がやや不安だがなんせ合格点が6割だから。6割はとれてるだろうという感触はあった。

午後1を終えて、ここまではパスできているという自信があった。

あとは午後2。

2時間で2問中のどちらか1問を解く。

ざっと読んで、2問目が簡単そうだなと思ったが、最初の3つの穴埋めがわからない。

最初の穴埋めは簡単なはずなので、ここがわからないようではダメだ、と1問目を選ぶ。


構成やプロトコル等は自分が経験したことのあるものとそっくりで、何を言っているかわからないところもほとんどなく、すいすいと解答していった。

逆に、「こんな簡単な答えでいいのか?」と、不安になる。

1時間でとりあえず解き終える。

もうこのまま退席しても多分大丈夫、という感触だったが、一応見直す。

30分残して退席。


試験というものは大体、簡単だった、できた、絶対合格した、と感じたときはあまりできていないものなので、こういう記事を合格発表前に書くことはしないのだが、
今回はそれにしても、というくらい自信がある。


終わってからネットで検索したのだが、やはり、「SC(情報処理安全確保支援士)は簡単」、という人が散見される。


ネットワークスペシャリストに受かって、IPAの試験が大したことがないのがわかった。

昔より明らかに簡単になっている。合格点も低いし、合格率も高くなっている。

エンジニア不足を懸念してか、「落とす試験」から「受からせる試験」になっていると感じる。

さらに、安全確保支援士は合格後登録して有料の講習を受けるという特別な資格であり、合格者増はIPA(かどこだか知らないが)の収入増になるから、受験者はいいお客さんである。

NW、SCは高度な資格に区分されているが、今なら言えるが、CCNPの方が難しい。試験のタイプが違うので単純に比較はできないが、少なくとも必要な勉強時間はCCNPの方がはるかに多い。費用もかかるし。今は4万円くらいか?3つで12万円。それに加えてテキストとか、ping-tとか、実機を買ったりとか、そういうのもあるし。

一番大きいのはIPAの試験は過去問と解答、コメントまで公開されていることだ。NWもSCも、過去問を解く以外の対策はほとんどしていない。


....こんなことを書いて落ちてたら恥ずかしい。その時はこのエントリーを消そう。

(2019/6/21 追記)
受かった!
合格発表前に公開された午後2の解答例が全然自分が書いたものと違っていて不安になっていたが...
AM2:96, PM1:79, PM2:71 と、だいたい感触通りの結果。
午後問はやっぱり、かなりの部分点がもらえる模様。



パスワードを平文で保存してはいけないならどうやって保存するのか?

あるサービスが「パスワードを平文で保存していた」というニュースに対してある有名な人が「平文で保存するなんてありえない!」と怒っていた。

「平文で保存していた」というニュースは何度か見たことがある。

では、平文で保存してはならないのならば、どうやって保存すればいいのか?

そう聞いたら、多くの人がこう答えるだろう。

「暗号化して保存する」

私もそうだと思っていた。

しかしそれは間違いで、正しくは

「パスワードのハッシュを保存する」

であった。


そういうとおそらく「暗号化もハッシュも同じようなものだ、そんな細かいことはどうでもいい」という人も多いだろう。

でも、そういう、「細かいことはどうでもいい」が積み重なって、「平文でも大丈夫」になってしまうのではないだろうか?

だから、ちょっと「細かいこと」にこだわってみよう。

ハッシュとか、暗号化とか、sha1とかmd5とかいうものの存在は知っていた。

ソフトウェアなどをダウンロードしたときにmd5チェックサムがついていて、
ファイルが壊れていないかを確認することができる、とか、

WEBサーバは、sha1は古いアルゴリズムだから、sha256を使わないといけないとか。


しかし、実際に自分がサーバ管理者やサービス提供者になったとして、
顧客のパスワードを安全に保存するにはどうすればいいかと聞かれたら答えられない。

最近、当たり前のことなのだが、知ってちょっと驚いたことがある。

それは、ハッシュというものは同じ値に対してはだれが計算しても同じ結果になる、
ということだ。


windowsのコマンドプロンプト(またはパワーシェル)で、
ハッシュを計算することができる。

別に何かのアプリなどをインストールする必要はない。

a.txt

というファイルがある。
中身は

あいうえお(改行)

で、文字コードをutf-8で保存した。


PS C:\users\taro\desktop> certutil -hashfile a.txt
SHA1 ハッシュ (対象 a.txt):
471399aeda50586bdc982b558bfde1da642c0fb3
CertUtil: -hashfile コマンドは正常に完了しました。

PS C:\users\taro\desktop> certutil -hashfile a.txt md5
MD5 ハッシュ (対象 a.txt):
0f2f3458c1553eb96d499508dc5184b4
CertUtil: -hashfile コマンドは正常に完了しました。

PS C:\users\taro\desktop> certutil -hashfile a.txt sha256
SHA256 ハッシュ (対象 a.txt):
0007a58ae5789f92155f87d6bc51edd4c9b036277d23154fe06a9daf33d0e514
CertUtil: -hashfile コマンドは正常に完了しました。


sha1(デフォルト)、md5、sha256で計算してみた。


それでは、このファイルを、私がさくらのVPSで使っているcentosのサーバにアップロードし、そこでハッシュを計算してみよう。

centosにもハッシュを計算するコマンドがある。

# sha1sum a.txt
471399aeda50586bdc982b558bfde1da642c0fb3  a.txt

# md5sum a.txt
0f2f3458c1553eb96d499508dc5184b4  a.txt

# sha256sum a.txt
0007a58ae5789f92155f87d6bc51edd4c9b036277d23154fe06a9daf33d0e514  a.txt


結果は全く同じである。

ハッシュというのは元の値に対して、人の目からは無意味である程度長く、元の値を推測も計算することもできないような値を生成する。

それはわかっていた。

しかしもうひとつ肝心なことは、同じアルゴリズム(sha1とかmd5とか)を使用すれば、
同じ値からは同じハッシュ値が生成されるということだ。

そうでなければ、たとえばユーザのパスワードのハッシュ値を保存しても、
ユーザのパスワードが正しいかを検証できない。

当たり前のことであるが。


私もパソコン、スマホ、さまざまなインターネット上のサービスなどを利用していて、
「パスワード」というものを何十個、何百個と設定して使用してきたが、
そのパスワードがテキストで読める状態で保存されているのは見たことがない。
パスワードをメールや郵便物に記載して送付されることはあったが。

あらためて、先ほどのcentosのサーバにパスワードがどのように保存されているかを見てみた。

/etc/shadow

というファイルである。

私は複数のアカウントを作成しているが、そのうちいくつかは異なるアカウントに同じパスワードを設定していた。

たとえば、taroというユーザのパスワードが password、
jiroというユーザのパスワードも password
という感じである。

「同じ値のhash値は誰が計算しても同じ」
ということは、taroとjiroのパスワードから計算したhash値は同じ文字列のはずだ......

しかし、実際には違う文字列であった。

あれ?


調べると、パスワードを保存する際にはhash値をもとの文字列からそのまま計算せず、
「ソルト(塩)」と呼ばれるランダムな値を付加して計算するのだそうだ。

その付加する方法は、文字列としてくっつけるのか、二進数でなんらかの演算をするのかわからないが、とにかくもうひと手間加えて、同じパスワードでも同じハッシュにならないようにする。


同じハッシュ値だからといっても、元の値を知ることは事実上不可能なことにはかわりないが、「複数のアカウントが同じパスワードである」という事実があれば、それらのアカウントに使用されているパスワードは誰もが思いつきそうな単純なパスワードであると推測できる。

また、よく使われそうなパスワード文字列を用意し、そのハッシュ値を計算しておいて、
ハッシュ値同士を比較すれば元の文字列がわかる。(これをレインボーテーブルという)

これらを防ぐために、ソルトというものが使われる。

しかし私はそこでひとつ疑問がわいた。

パスワードは正しい値であることを検証しなければならない。


再訪したユーザが入力したパスワード値にソルトを付加してハッシュ値を計算し、保存されたハッシュ値と同じであるかを比較しなければならない。

ではソルトをハッシュ値と一緒に保存するのか。
それは鍵をかけた金庫の横に鍵を置くようなものだ。

/etc/shadowを見てももちろんソルト値は書かれていない...

調べてみた。

ソルト値の隠し場所があった。

金庫のたとえで言うなら、鍵は金庫の横に置かれてこそいないが、
横にテープで貼り付けるくらいのことしかされていなかった。

ソルト値を使ってパスワードからhashを計算したら、
/etc/shadowに書かれているhash値と一致した。

# cat /etc/shadow | grep hiroshi
hiroshi:$6$9Qpwui9R$jrswaF/U2NRgq7Yhuk2YH7T2POuOYuDq/2eH71KMJkRDNaLxHEa25BLvmAnT3zz.ijxv/e6qUK.cEN24FN.GD0:18005:0:99999:7:::

# perl -e 'print crypt("hiroshidesu", "\$6\$9Qpwui9R");'
$6$9Qpwui9R$jrswaF/U2NRgq7Yhuk2YH7T2POuOYuDq/2eH71KMJkRDNaLxHEa25BLvmAnT3zz.ijxv/e6qUK.cEN24FN.GD0


pythonの例

# python -c 'import crypt; print(crypt.crypt("hiroshidesu", salt="$6$9Qpwui9R"))'
$6$9Qpwui9R$jrswaF/U2NRgq7Yhuk2YH7T2POuOYuDq/2eH71KMJkRDNaLxHEa25BLvmAnT3zz.ijxv/e6qUK.cEN24FN.GD0


そもそも、ハッシュ値で保存するのも、さらにその際にソルトを加えるのも、
サーバにアクセスされてかつサーバの管理アカウントでログインされてしまった場合を想定してのことである。

平文で保存していたのも、サーバにログインされないための対策を十分にしているから、
と考えてのことだろう。


sha512の計算方法とか、saltのかけ方とかはわからないが、
それを使ってますよ、というだけで、パスワードの保存方法というのはそんなに難しいことをしているわけではないというのが、分かった。

ただし、繰り返しになるが、それはあくまでも推測したパスワードからハッシュを計算して一致するかを調べる、ということであって、ハッシュ計算の脆弱性があるとか、ハッシュ値から元の値が算出できるとかいうことではない。


※関連エントリ

https://monqy.blogspot.com/2019/04/blog-post_27.html

python3のcgiでUnicodeEncodeError

腹立つなァ....

python3のcgiで日本語を表示させたらページが真っ白に。

ログを見たら、

UnicodeEncodeError: 'ascii' codec can't encode characters in position...

python2の時はちゃんと動いていたのに。


いろいろ調べた結果、
下記を追加して動いた。

#!/usr/bin/python
import sys #↓以下2行を追加 import io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') print( "Content-Type: text/html") print() print('糞野郎')
(参考)
http://lab.knightstyle.info/%E7%A7%81%E3%81%8Cpython3%E3%81%A7unicodeencodeerror%E3%81%AA%E3%81%AE%E3%81%AF%E3%81%A9%E3%81%86%E8%80%83%E3%81%88%E3%81%A6%E3%82%82%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E6%96%87%E5%AD%97/

pip install mecab-python3 が失敗する

腹立つなァ....

検索してみるとみんな苦労しているようだが、
エラー内容もその対応も様々。



# pip install mecab-python3
Collecting mecab-python3
  Using cached https://files.pythonhosted.org/packages/ac/48/295efe525df40cbc2173748eb869290e81a57e835bc41f6d3834fc5dad5f/mecab-python3-0.996.1.tar.gz
Installing collected packages: mecab-python3
  Running setup.py install for mecab-python3 ... error
    Complete output from command /usr/bin/python3.6 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-hzcdo768/mecab-python3/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-5w7so2s3/install-record.txt --single-version-externally-managed --compile:
    /usr/lib64/python3.6/distutils/dist.py:261: UserWarning: Unknown distribution option: 'long_description_content_type'
      warnings.warn(msg)
    running install
    running build
    running build_py
    running build_ext
    building '_MeCab' extension
    swigging MeCab.i to MeCab_wrap.cpp
    swig -python -shadow -c++ -I/usr/local/include -o MeCab_wrap.cpp MeCab.i
    /usr/local/include/mecab.h:848: Warning(302): Identifier 'set_sentence' redefined by %extend (ignored),
    MeCab.i:88: Warning(302): %extend definition of 'set_sentence'.
    creating build
    creating build/temp.linux-x86_64-3.6
    gcc -pthread -Wsign-compare -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/usr/local/include -I/usr/include/python3.6m -c MeCab_wrap.cpp -o build/temp.linux-x86_64-3.6/MeCab_wrap.o
    MeCab_wrap.cpp: In function ‘swig_module_info* SWIG_Python_GetModule()’:
    MeCab_wrap.cpp:2452: error: ‘PyCObject_Import’ was not declared in this scope
    MeCab_wrap.cpp: In function ‘void SWIG_Python_SetModule(swig_module_info*)’:
    MeCab_wrap.cpp:2521: error: ‘PyCObject_FromVoidPtr’ was not declared in this scope
    MeCab_wrap.cpp:2512: 警告: unused variable ‘swig_empty_runtime_method_table’
    MeCab_wrap.cpp: In function ‘swig_type_info* SWIG_Python_TypeQuery(const char*)’:
    MeCab_wrap.cpp:2544: error: ‘PyCObject_AsVoidPtr’ was not declared in this scope
    MeCab_wrap.cpp:2549: error: ‘PyCObject_FromVoidPtr’ was not declared in this scope
    error: command 'gcc' failed with exit status 1

    ----------------------------------------
Command "/usr/bin/python3.6 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-hzcdo768/mecab-python3/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-5w7so2s3/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-install-hzcdo768/mecab-python3/


ログを見てみる。

#### # pip install mecab-python3

実行したコマンド。
コマンドがないとかいうエラーはない。

#### Collecting mecab-python3
####
####   Using cached https://files.pythonhosted.org/packages/ac/48/295efe525df40cbc2173748eb869290e81a57e835bc41f6d3834fc5dad5f/mecab-python3-0.996.1.tar.gz


このtar.gzファイルをダウンロードしてインストールしているようだ。

#### Installing collected packages: mecab-python3
####
####   Running setup.py install for mecab-python3 ... error

で、setup.pyが error になっている。




####     Complete output from command /usr/bin/python3.6 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-hzcdo768/mecab-python3/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-5w7so2s3/install-record.txt --single-version-externally-managed --compile:

以下が setup.pyのエラーの詳細かな。

####
####     /usr/lib64/python3.6/distutils/dist.py:261: UserWarning: Unknown distribution option: 'long_description_content_type'

これはワーニング

####
####       warnings.warn(msg)
####
####     running install
####
####     running build
####
####     running build_py
####
####     running build_ext
####
####     building '_MeCab' extension
####
####     swigging MeCab.i to MeCab_wrap.cpp
####
####     swig -python -shadow -c++ -I/usr/local/include -o MeCab_wrap.cpp MeCab.i


「swigがインストールされていない」という人がいたが、swigはインストールされている。

####
####     /usr/local/include/mecab.h:848: Warning(302): Identifier 'set_sentence' redefined by %extend (ignored),

これもワーニング

####
####     MeCab.i:88: Warning(302): %extend definition of 'set_sentence'.

これもワーニング

####
####     creating build
####

buildを作成

####     creating build/temp.linux-x86_64-3.6
####
####     gcc -pthread -Wsign-compare -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/usr/local/include -I/usr/include/python3.6m -c MeCab_wrap.cpp -o build/temp.linux-x86_64-3.6/MeCab_wrap.o

gccを呼び出し

####
####     MeCab_wrap.cpp: In function ‘swig_module_info* SWIG_Python_GetModule()’:
####
####     MeCab_wrap.cpp:2452: error: ‘PyCObject_Import’ was not declared in this scope


エラー。PyCObject_Import が宣言されていない。

####
####     MeCab_wrap.cpp: In function ‘void SWIG_Python_SetModule(swig_module_info*)’:
####
####     MeCab_wrap.cpp:2521: error: ‘PyCObject_FromVoidPtr’ was not declared in this scope

エラー。PyCObject_FromVoidPtr が宣言されていない。

####
####     MeCab_wrap.cpp:2512: 警告: unused variable ‘swig_empty_runtime_method_table’

警告。未使用の変数。

####
####     MeCab_wrap.cpp: In function ‘swig_type_info* SWIG_Python_TypeQuery(const char*)’:
####
####     MeCab_wrap.cpp:2544: error: ‘PyCObject_AsVoidPtr’ was not declared in this scope

エラー。PyCObject_FromVoidPtr が宣言されていない。

####
####     MeCab_wrap.cpp:2549: error: ‘PyCObject_FromVoidPtr’ was not declared in this scope

エラー。PyCObject_FromVoidPtr が宣言されていない。

####
####     error: command 'gcc' failed with exit status 1

gccが失敗した。

####
####
####
####     ----------------------------------------
####
#### Command "/usr/bin/python3.6 -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-hzcdo768/mecab-python3/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-5w7so2s3/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-install-hzcdo768/mecab-python3/
####
####
####

違うエラーなのだが、「setup.pyがpython3に対応していない」という人がいる。
多分そんなところだろう。

setup.pyはどこにあるのか。
探すと...

/home/monqy/mecab/mecab/python/setup.py

これはmecab本体かな。

/home/monqy/mecab-python-0.996/setup.py

こっちか。

setup.pyを実行してみるとエラーになる。オプションが必要のようだ。

ログをもう一度見てみると

Complete output from command
/usr/bin/python3.6 -u -c "import setuptools, tokenize;

__file__='/tmp/pip-install-hzcdo768/mecab-python3/setup.py';

f=getattr(tokenize, 'open', open)(__file__);

code=f.read().replace('\r\n', '\n');

f.close();

exec(compile(code, __file__, 'exec'))"

install --record /tmp/pip-record-5w7so2s3/install-record.txt
--single-version-externally-managed
--compile:

多分、最初の方にあるURLからファイルをダウンロード、/tmpに展開し、
そこのsetup.pyを実行している。

/tmp/pip-install-hzcdo768/mecab-python3/

というフォルダはない。
インストールに失敗した後に消しているのだろう。


よーし、では、tar.gzを持ってきて、解凍→インストールしてみよう。

wget https://files.pythonhosted.org/packages/ac/48/295efe525df40cbc2173748eb869290e81a57e835bc41f6d3834fc5dad5f/mecab-python3-0.996.1.tar.gz


ダメ。

swigのバージョンが古いのだろうか。

# swig -version

SWIG Version 1.3.40


でもswigの新しいバージョンの入れ方がわからない。

twitterで、ある人がバージョン指定すればいけると。

pip install mecab-python3==0.7

...いけた。

# pip install mecab-python3==0.7
Collecting mecab-python3==0.7
  Downloading https://files.pythonhosted.org/packages/25/e9/bbf5fc790a2bedd96fbaf47a84afa060bfb0b3e0217e5f64b32bd4bbad69/mecab-python3-0.7.tar.gz (41kB)
    100% |????????????????????????????????| 51kB 2.4MB/s
Installing collected packages: mecab-python3
  Running setup.py install for mecab-python3 ... done
Successfully installed mecab-python3-0.7


とりあえずpythonからmecabが使えるようになった。

ふう。

powershellでイベントログから勤務時間を知る (改良版)

年を自動取得 月は指定可、指定がなければ前月 うるう年対応
echo ("args: " + $args.Length)

$myyear = (Get-Date).year

echo ("myyear: " + $myyear)

if ($args.Length -lt 1) {
        $lastday=(Get-Date -Day 1).AddDays(-1).day
        $lastmonth=(Get-Date -Day 1).AddDays(-1).month
}
Else {
        $mymonth = [Int]$args[0]
        $nextmonth=(Get-Date -Year $myyear -Month $mymonth -Day 1).AddMonths(1).Month
        $lastday=(Get-Date -Year $myyear -Month $nextmonth -Day 1).AddDays(-1).day
        $lastmonth=$mymonth
}



echo ("lastday: " + $lastday)

echo ("lastmonth: " + $lastmonth)


$LogName = "System"

$StartTime = New-Object System.DateTime $myyear,$lastmonth,01,00,00,00,00

$EndTime = New-Object System.DateTime $myyear,$lastmonth,$lastday,23,59,59,59


# 指定した種類・期間のイベントログを取得する
$EventsList = Get-WinEvent -FilterHashTable @{LogName=$LogName ; StartTime=$StartTime ; EndTime = $EndTime }

$EventsList = $EventsList |Where-Object {($_.Id -eq 12 -and $_.ProviderName -eq "Microsoft-Windows-Kernel-General") -or ($_.Id -eq 109)}

# イベントのリストを出力する
$EventsList | Format-Table -Property  TimeCreated

$EventsList | Format-List -Property  LogName, MachineName, LevelDisplayName, TimeCreated, ProviderName, Id, UserId, Message

↓ もっと簡単な方法
 https://monqy.blogspot.com/2019/04/windows.html

ポートスキャン - 「ポートが開いている」とはどういう状態か?

セキスペの過去問を解いていたらポートスキャンに関する問題が出てきた。

synを送ってその反応を見る、というもので、

まず「それを何というか」という問題があり、「ポートスキャン」と回答したが、
正解は「SYNスキャン」であった。

SYNスキャンはポートスキャンの一種なので間違いではないが、
SYNだけ送って、そのあとSYNACKが返ってきてもセッションを確立しないので、
スキャン先に接続履歴を残したくないときに使うそうだ。

もう一つの問題は、「ポートが閉じているときにどういう反応があるか」というもので、
私は「応答なし」と答えたのだが正解は「RSTACK受信」および「応答なし」であった。

私は応答がないと思い込んでいたので、えっと思って
パソコンでwiresharkを起動して自分のサーバへのアクセスで試してみた。

接続できないはずのtelnetアクセスをしてみると応答がない。

しかしそれはポートが開いてるかどうか以前にiptablesで許可していないからだと気づき、
一時的にiptablesを無効にして、再度telnetしてみた。

RST,ACKが返ってくる。

なるほど。

以前nmapでポートスキャンしたときに、「ポートにアクセスできないがフィルターされているかもしれない」みたいな結果が出たことがあった。

これはsynに応答がなかった場合であろう。
サーバでtelnetサービスが動いていてもiptablesとかその前のFW装置で許可されていなければ応答が返ってこない。
だから応答が返ってこない場合はポートが開いているか閉じているかはわからない。

今書いていて思ったのだが、ポートが開いていないときにRSTを返さないような設定ができるんじゃないだろうか?
できないか?

まあいい。ふつうはRST ACKが返るということがわかった。

ところで、「ポートが開いている」という言い方をよくするが、
私はまだ若いころ、その確認方法がよくわからなかった。

どこかに設定ファイルがあって、開けるポート番号または閉じるポート番号を指定してあるのだと思っていた。

しかし、サーバ(RedHat9)設定のどこを探してもそのような設定はない。

そのサーバではiptablesは設定していなかった。設定方法も知らなかった。
検証環境のサーバだからだ。

調べていくうちにiptablesのこともわかってきたのだが、
iptablesは確かに通信を許可するポートを開けたり閉めたりするが、
いわゆる「ポートが開いている」というのはそのことを言うのではない。
まあ、ユーザや運用管理者にとっては「FWで閉じている=閉じている」
ということでよいだろう。

しかし、「FWで開いている=開いている」にはならない。

というか、そもそも「ポートが開いている」という言い方が不正確で誤解を生む。

開いているという言い方だとFWで開け閉めするのを想像するが、
正確には「そのポートをリッスンしているサービスが稼働している」
という状態である。

たとえばtelnetサービスを有効にしていてその使用ポートが23であれば、
「TCP23が開いている」となる。

使用ポートは変更できるので、telnetサービス(正確にはtelnetサーバサービス)が動いている=23番が開いているとも言えない。

そんな人はいないと思うが、telnetで80番を使うようにすることもできる(よね?)。

私のサーバではtelnetは動作していず、sshが動作していてポート番号は22でないものに変更している。

このとき、「TCP23は開いていず、SSH接続できるがTCP22は開いていない」という状態になる。

そして、iptablesでTCP23とTCP22は拒否しているので、
ポートスキャンするといずれも応答が返らない。

しかし、その結果だけからは、サーバの手前の別装置のFWやアクセスリストで拒否されているのか、
サーバのiptablesで拒否されているのか、サーバがダウンしているのか、はわからない。
アクセスしているIPアドレスが間違っている可能性もある。

pingは返ってくる、80番はアクセスできる、23番は応答がない、
であればFWかサーバのiptablesで拒否されているとわかる。

powershellスクリプトでイベントログから勤務時間を知る

いつもイベントビューアで見ているのだが、
ちゃっとスクリプトで表示できないかなと

powershellを起動

管理者として

スクリプト実行はデフォルトで許可されていないので

Set-ExecutionPolicy RemoteSigned

y


---
$month=1
$lastday=30

$month=[int]$Args[0]

if($month -eq (1 -or 3 -or 5 -or 7 -or 8 -or 10 -or 12)){
        $lastday=31
}else{
        if($month -eq 2){
                $lastday =28
        }else{
                $lastday=30
        }
}

$LogName = "System"

$StartTime = New-Object System.DateTime 2019,$month,01,00,00,00,00

$EndTime = New-Object System.DateTime 2019,$month,$lastday,23,59,59,59

$EventsList = Get-WinEvent -FilterHashTable @{LogName=$LogName; StartTime = $StartTime; EndTime = $EndTime}

$EventsList = $EventsList |Where-Object {($_.Id -eq 12 -and $_.ProviderName -eq "Microsoft-Windows-Kernel-General") -or ($_.Id -eq 109)}

$EventsList | Format-Table -Property TimeCreated
---

eve.ps1 というファイル名にし、月を指定する。

実行例

PS C:\users\taro> .\eve.ps1 3

TimeCreated
-----------
2019/03/28 19:00:30
2019/03/28 19:00:09
2019/03/23 6:02:26
2019/03/23 6:02:05
2019/03/21 18:23:56
2019/03/21 18:23:31
2019/03/14 17:22:30
2019/03/14 17:22:10
2019/03/13 21:50:36
2019/03/13 21:50:16
2019/03/13 12:39:06
2019/03/13 12:38:46
2019/03/13 2:31:16
2019/03/13 2:31:00
2019/03/12 17:37:02
2019/03/12 17:36:44
2019/03/11 8:46:52
2019/03/11 8:46:32
2019/03/10 15:42:36
2019/03/10 15:42:13
2019/03/10 4:02:14
2019/03/10 4:01:50
2019/03/10 3:51:38


並び順が降順なのと、終了時間が2回出るのがアレ

月を指定しないとエラーになるのもアレ

うるう年未対応、月末日の求め方がアレ

年が固定で書いてあるのがアレ


まあ、イベントビューアを見るのとあまり変わらないけど、
PowerShellでいろいろできることがわかった。