簡単に疑似乱数を作る方法は難しい

職場で作っている検証用システムでこういうことをやりたかった。

出力値の候補を16個用意しておいて、これを一定時間ごとに切り替える。

この16個の値をいろいろな順番で出力したい。

16個の順番を並べる全パターンは16!=2.1×1013個もあるから、

全切替パターンをプログラムに記載するのは不可能である。


ということで乱数を使いたかったのだが、乱数ライブラリなんてないんだよな。

自分で疑似乱数生成器を作ろうということで、まず思いついたのはメルセンヌツイスター。

しかしどんな実装なのだろうと調べたが、概要を見て無理だと思った。

というのも624個もの32bit整数を内部状態として持つ必要があるからである。

周期が長くランダム性が高い疑似乱数が生成できる方法だが、

そのためには内部状態としてそこそこ巨大なものを持たないといけない。


だからといって線形合同法はわりと規則性が見えてしまうよなぁ。

デジタル回路でよく使われるLFSRはビットシフトが主体なので、これもほとんど規則性のある変化しかしない。

もうちょっと実装が容易で質の良い方法がないかと思ったら、Lagged Fibonacci法というのを見つけた。

疑似乱数列 Sn を Sn=Sn-j + Sn-k(mod m)という形で計算するというもの。

過去の乱数を2つ取り出して足すという簡単な計算ですね。

足し算以外の演算子、例えばXORでもLagged Fibonacciではあるようだが、普通は別の名前が付いている。


ただ、いろいろ調べてわかったのは、この成否を決めるのはk,jの数字で、

この最大値がある程度大きくて、初期値としてそこそこ質の良い乱数が用意できるかどうかが決め手になるようだ。

最初、k=1,j=7で試していたのだが、下位ビットの質がよくない。

それで典型例として書いてあったk=31,j=63でやると下位ビットまで質がよさそう。

質の良い乱数を作るには内部状態を大きくしなければならないと。単純にはそういう話らしい。

メルセンヌツイスターほどではないし、バッファさえ持てればよいので実現性はあるが。


しかし線形合同法だって下位ビットを捨てればそこそこ質がよいという話もあるんだよな。

線形合同法の欠点として、2個の乱数をペアにすると偏るというのがあって、

それを今回の目的に適用すると、出力値の遷移が偏ってしまうということになる。

ただ、下位ビットを捨てればその影響は減るという話があって、それは繰り上がりがあるからなんだと思うけど。

線形合同法 Xn=A×Xn-1 + B (mod m) で、A=214013,B=2531011,m=232(Visual Studioで使われている数字らしい)を適用して、

この結果を4bitごとに区切って2つペアにすると、下位12ビットまでの組み合わせは発生回数0回というのが存在する。

しかし、15~12bitでは発生回数が最大・最小の回数差がほとんどなくなる。

実際、Visual Studioでは下位16ビットを捨てているらしい。


32bitで計算して下位16bitを捨てても、16bit残って、これは16個の出力値選択を同時に4つ操作出来るということである。

それで実用上十分なランダム性が得られるように見えるので、これでよいのでは?

ただ、この下位ビットをいくつ捨てるとうまくいくのかというのもようわからん話ですがね。

疑似乱数の評価も理論的なところとそうでもないところがあるからなんとも。


というわけで最初は線形合同法を忌避してLagged Fibonacci法を選ぼうとしたが、

それはそれで乱数を63個置いておかないといけないのでけっこう大変だと。

線形合同法はなんやかんやシンプルなのが魅力である。

それで下位ビットを捨てればある程度いけるやろというのも知見であろう。

シミュレーションで露骨に偏らないことが確認出来たら、それでもいいのかもね。

今回の用途で求められるのはその程度のランダム性だし。


というわけでランダムに切り替わるというのもそれはそれで難しいという話ですね。

このシステムで他に乱数的に使えるものがないか考えてみたけど、

どうにも難しそうと言うことで、乱数を自分で用意しなければならなくなったと。

ところがこの疑似乱数というのは本当に難しい。

真の乱数発生器を取り付けたいぐらいめんどくさいが、真の乱数もそれはそれで難しいらしい。

キーボード触ってもスリープ解除してくれるな

デスクトップPCをスリープ状態にしてたはずなのに解除されてて、

なんでかなと思ったのだが、そういえばキーボードを交換したんですよね。

で、Windowsでキーボードにやっていた設定が吹き飛んでいたようだ。

というわけで再度同じ設定をするも、キーボードに触れると起動してしまう。

なぜだろうか。


そもそも、スリープモード解除をキーボード・マウスなどの操作でやるかどうかは、

Windowsのデバイスマネージャで行うというのがちょっとした驚きである。

デバイスマネージャでキーボード・マウスなどのプロパティを開くと、「電源の管理」というタブがある。

そこに「このデバイスで、コンピュータのスタンバイ状態を解除できるようにする」というチェックボックスがある。

標準ではONなのでこれを外すとキーボード・マウスを触ってもスリープ解除されなくなる。

新しいキーボードに取り替えたら別のデバイスとして扱われるようになり、

この設定が飛んでしまったんですね。そこまではすぐに理解できた。


しかし、これを無効にしても起動してしまうのでおかしいと調べたところ、

コマンドプロンプトで下記入力するとスリープ解除できるデバイスが表示されるという。

>powercfg /devicequery wake_armed

これで表示されたデバイスは……なんと指紋認証器だった。

いやいや、指紋認証器を触ったわけじゃないんだけどなぁと。

特に指紋認証器でスリープ解除できる必要はないのでこちらも同じ設定をして解除。

再度同じコマンドで確認すると「NONE」の表示。

こうするとキーボードを触ってもスリープは解除されなくなった。


というわけで誤爆だったわけですね。

マウスとキーボードのどちらかを封じたのに、封じた方のデバイスで解除できてしまうとか、そういうことはあるらしいから、それと同じかね。

デスクトップPCの場合、電源ボタンを唯一の解除手段にしたいことが多いと思うので。

ノートPCでもフタ以外の解除手段は使いたくないことはあるでしょうし。


このデスクトップPCのスリープを使う理由というのが、

不意にキーボードなど触れてしまってもなにも起きないようにという目的が大きい。

このPCは電源ボタンをスリープに割りあててあるから、テレビを見るとき(PCのディスプレイと共用)など、ワンタッチでスリープに入れてしまえば手軽で、

それで元に戻るときは電源ボタンを押せば、すぐにもとの状態に戻せる。

省エネというよりは誤操作防止という意味が大きいのはそういうことである。

だから勝手に復帰してもらっては本末転倒なのだが、標準設定はそうではないから戸惑う。

というかキーボード交換で変わってしまうのも、なかなか困るのだが。

msg形式ってなんで使ってるんだ?

今日、仕事で過去のE-mailデータを何らかの方法でアーカイブ化することに迫られ、

結局はMicrosoft Outlookからドラッグ&ドロップでmsg形式として保存することになった。

Outlookがないと中身が見られないなどの課題はあるのだが、

この方法を選ばざるを得なかったこともmsg形式のためである。


msg形式はOutlookの独自形式である。

メールデータをファイルとして出力する方法としてはeml形式がある。

eml形式はMozilla FirefoxもOutlookも対応している標準的な形式である。

でもOutlookでeml形式の保存はできなかった気がするな。読めたとは思うけど。

この形式はシンプルにメールデータをテキストで書き出しただけ。

Base64やQuoted-printableで変換されたデータもあるから、テキストファイルで開いてそのまま読めるとも限らないが、だいたい読めるんじゃないか。

それに対してmsg形式はOutlook以外で読むのは至難の業で、調べると困っている人が見つかる。


正直どうかと思う形式なのだが、他のメールの引用をmsgファイルの添付でやってくる人というのが社内にはけっこういる。

というか、多分うちだけの問題でもなく、Outlookの機能として「添付ファイルとして転送」というのがあるので、そうするとこうなる。

受け取った側がOutlookがないと使い物にならないのだけど……

社内なら問題にならんでしょうけどね。


どうしてこんな機能が使われるのかという話だけど、

複数のメールをまとめて転送したいというニーズがまず1つあると思う。

実際、複数のmsgファイルが添付されたメールというのはけっこうあった。

あとは添付ファイルとかHTMLメールの装飾とかもまとめて転送したいとか。

まぁ転送まで意識されていないメールというのはあると思うけど。


もちろんテキストで済むならばそっちの方が汎用性が高い。

別件ではQ&Aのメールをアーカイブ化するために、テキストで保存している。

OutlookでTXT形式で出力するだけで、それなりのテキストにはなる。

それをアップロードすれば、検索も容易である。

でも、メールを忠実に残せるわけではないので補助的なものではある。

実際、これじゃあさっぱりわからんなと思ったことはある。


というわけでmsg形式のせいでmsg形式を選ばざるを得ないのだった。

まぁ勤務先がOutlookをやめるということはないでしょう。

あれだけMicrosoft製品にズブズブなんだから。日本企業ではありがちでしょうけど。

実際、良いところはたくさんありますからね。Outlookも良く出来たソフトである。

ただ、このmsg形式がOutlook独自形式であるというのは、どうかと思うポイントで、Outlookを使わない人にとっては非常に不便な形式である。


なんでE-mailなんて古典的なもので、独自形式なんてあるんだよと思っちゃうけどね。

msg形式のファイルで保存するのも課題はあるけど、ここでは後からファイルを確認する頻度はそこまで高くないはずなので。

IPv6だけにできるとうれしい

先日、NTTドコモで通信障害があったが、そのきっかけになったのがIPv6シングルスタック方式の導入に伴う構成変更の影響だったという。

「IPv6シングルスタック方式」の提供を開始-IPv4アドレス枯渇問題へ通信事業者として対応- (NTTドコモ)

各端末にIPv6アドレスのみを付与する方式である。

日本では初めてだが、世界的に見ればモバイルインターネットでは普及しつつあるらしい。


従来、NTTドコモではIPv4とIPv6のデュアルスタックをやってきた。

IPv6対応のサイトへはIPv6で、IPv4のみ対応のサイトにはIPv4で通信すると。

この方式の欠点はIPv6とIPv4のパケットが混在することで機器への負荷が大きいこと。

そこで流れるパケットをIPv6に一本化したいということがある。

こういう考えは珍しいことでは無く、NTTのNGN網はIPv6しか流せないのもこのため。

ゆえにフレッツ光でIPoE方式を使う場合は、IPv6を使うことが必須になっている。

IPoEにしてみた


とはいえ、これだけだと、このサイトを含むIPv4のみ対応のサイトにアクセス出来なくなる。

うちのフレッツ光の場合、契約しているBB.exciteはDS-Lite方式でIPv4への接続をサポートしている。

IPv4の通信はルーターでIPv6のパケットにカプセル化され、プロバイダーのIPv4接続装置に送られて、ここでIPv4のNATが行われてインターネットに通じる。

家庭のインターネットではこうしてIPv6への一本化を図っている。

しかし、家庭内のLANではIPv6とIPv4が混在しており、ルーターがIPv6に一本化しているという状況である。


一方でモバイルインターネットのIPv6シングルスタックというのは、

各端末からの通信をIPv6に一本化するということである。

どうやってそういうことができるのか?

NTTドコモの資料には「NAT64/DNS64方式」「464XLAT方式」というキーワードがある。


NAT64/DNS64方式というのは、IPv4のアドレスにプリフィックスを付けたIPv6アドレスをIPv4宛の通信に使う方法である。

テクログ|「誰でも使える サーバーサイド Open NAT64実証実験」のご紹介 (A10)

IPv6のアドレスは128bit、IPv4のアドレスは32bitだから、96bitのプリフィックスを付ければすっぽり収まる。

まず、DNSのAレコードにあるIPv4アドレスを、プリフィックス付きのIPv6アドレスに変換して、AAAAレコードとしてクライアントに回答する。

これにより、IPv4しか対応していないサイトもIPv6のサイトのように見える。

そのIPv6アドレスに通信をすれば、変換装置でIPv4に変換されるというわけである。

こうすると、端末~変換装置の間はIPv6に一本化できるというわけである。


しかし、ブラウザみたいなのはこれでいいかもしれんが、

IPv4アドレスに直接通信しようとしたり、IPv6に対応しないアプリもあるだろう。

そこで補完的に使われるのが 464XLAT方式である。

Androidはあるバージョンからこの方式に対応していて、

NTTドコモはこれに対応した端末に限ってIPv6シングルスタックを導入している。

IPv4の通信を、IPv4のアドレスにプリフィックスを付けたアドレスへのIPv6の通信に変換する。

やってることはシンプルで、NAT64/DNS64方式とは補完的な技術と言える。


NTTドコモは「IPv4アドレス枯渇問題へ通信事業者として対応」と書いてあるが、

当然のことながら、各端末に振られたIPv4アドレスはプライベートアドレスであり、

IPv4では限られたグローバルアドレスを使い回すことでやっている。

NAT64/DNS64方式にしても、結局はIPv4の通信にはIPv4アドレスが必要で、

これもIPv4アドレスの使い回しを行うという点では同じ技術である。

ゆえにIPv6シングルスタックそのものはIPv4アドレス枯渇問題の対策というわけではないと思われる。

とはいえ、IPv6の導入自体は不可避であって、デュアルスタックよりシングルスタックの方が効率が良いということは事実である。


ちなみにWindowsも464XLAT方式に対応してるらしいですよ。

モバイルインターネットに直接接続する用途に限定されているけど。

もしかしたら固定回線でも普通に使われる技術になるかもね。


ちなみにBIGLOBEは固定回線にNAT64/DNS64方式を選択的に導入しているという。

世界初への挑戦!インターネットを快適にするNAT64/DNS64とは? (BIGLOBE Style)

これはMAP-E方式(これもIPv4をIPv6でカプセル化する技術)に対応したルーターがなくても、IPoE方式の恩恵を受けられるように考えられたものだという。

IPv6のパケットをそのままNGN網に出せるルーターさえあれば、

IPv4のみのサイトでも、DNSからIPv6アドレスを得て、変換装置までIPv6で通信できるのでIPoEの恩恵を受けられるということ。

しかしながら、ゲームなどでNAT64/DNS64方式ではうまく動かないものがあり、

そういうものは従来通りPPPoEで接続してもらうという形で回避する必要がある。

だからIPv6シングルスタックというわけではない。


まぁそんなこんなでIPv4とIPv6の共存技術にはいろいろあるんですね。

できるならどちらかに一本化したいが、一本化するにはそれなりの方法が必要ということである。

すでにNTTのNGN網の仕様とDS-Lite方式でこのあたりに少し触れていたから、

この話を調べて、なるほどなと納得したのだった。

Adblock用ブラウザがある

Webサイトの広告にも度が過ぎたものが多いので、

従来からPCではAdblock Plusの拡張機能をChromeに入れて対応してきた。

あんまり意識してないけど、ちゃんと効いてるみたいですね。

全画面表示の広告とかちゃんとブロックできてるみたいだし。


スマートフォン・タブレットは画面サイズに限りがある中で、

画面がほとんど広告に覆われてしまうこともしばしばある。

特に最近はGoogleがページ遷移時に全画面表示でブロックする広告を多用している。

Googleの全画面広告を表示しないように設定する方法 (サイト運営者の設定) (ぱらめでぃうす)

サイト管理者もこれはあんまりだと無効化していることもあるようだが、

こういう広告が出ることでサイトを去る人も多いという悪影響もあるからだ。

しかし意図せずに導入してそのままというサイトも多い。


しかしAndroidのChromeはPCのChromeと同じ拡張機能は入らないのである。

Adblockのためのアプリがあるが導入方法が複雑なこともしばしば。

それで諦めていたのだが、さすがにあまりにひどいだろうと思い調べたら、

Adblock機能が組み込まれたブラウザというのがあるんですね。

Adblockブラウザー (Google Play)

開くとABPというアイコンが表示されている以外はChromeとうり二つの見た目。

それもそのはず。Chromiumベースのブラウザに最小限のカスタマイズをしたものだからだ。


標準のフィルタだといろいろ足りなかったので調べてあれこれ追加。

それでこのブラウザで今まであまりにひどいと思ってたサイトをいくつか見てみたが、

画面の大半が広告で占拠されたり、全画面広告が出てくるようなことはなくなった。

早く導入しとけばよかったと後悔するほどに快適だった。

広告ブロックするとコンテンツが見られないような対抗措置にあうかと思ったが、

今まで煩わしいと思っていたサイトではあまり問題にならないようだ。


ただし、このブラウザはChromeの代わりになるものではないと考えている。

というのもパスワード管理がGoogleとは独立したものが備わっているためである。

アプリによってはGoogleや他のアプリのパスワード入力機能が使えることがあるが、

ブラウザ系のアプリはだいたいダメみたいですね。

なので、ログインが絡むところはChromeを使うのが基本になりそうだ。

広告が煩わしいのと、会員制サイトというのはだいたい両立しないので。

そのため今後も主たるブラウザはChromeである。


こういう機能を備えたAndroid用のブラウザはけっこういろいろあるらしい。

Vivaldiもそうなんですね。この名前を見て気付くかわからないが前身はOperaである。

そういえば昔はOpera使ってる人もちらほらいたよね。

結局のところ、これもChromiumベースであって、付加価値としてAdblockがあると。

しかし、どれを使っても結局はパスワード管理はそれぞれのブラウザで行われる。

そこがGoogleに連動するのがChromeであり、連動しないことが他のブラウザの長所だから。


Adblockが普及することによって広告収入を失うことはサイトの運営者にとっては問題である。

そこで自衛策として、広告が読み込まれなければ、ページを読ませないというような対策が取られることがある。

時々そういうサイトに遭遇しますけどね。でも昔より減った気がするな。

Adblockを有効化してると見られないようにするより、Adblockが普及している現状を受け入れた上で、

Adblockがあっても異常な動作とならないが、できるだけAdblockを回避して広告を出せるようにするという設計が求められているのかも知れない。

確かに取り除けない広告はけっこうある。Adblock Plusの機能不足というのもあるっぽいが。

しかし、目的のコンテンツと巧妙に混ぜられると除去できないのも道理とは思う。


とはいえ、今回の目的としてはこれでとりあえず満足。

あの全画面広告とか何タイプかの広告が除去できればとりあえずはいいや。

Power Appsの難しいところ

MicrosoftがOffice365の一部としてPower AppsというWebアプリを作れる機能を提供していて、勤務先でも一般の従業員が使えるようになった。

それで最近、既存の文書管理システムがSharePointに移転して、

どうにも使いにくい気がしたので、検索・表示機能をPower Appsで作れるか試してみた。

SharePointのリストを抽出するような処理はわりと簡単だった。


しかし、わりと簡単とは言ったものの制約が多いのも実情である。

Power Appsではリストの抽出処理をサーバー側に委任することができる。

このようにすれば大量のデータから必要なものだけを探してクライアントで取得できる。

1操作でクライアントが取得できるデータは500レコードとかに制限されているそう。

なので、もし委任機能がないとドキュメント番号が”XYZ-C1234”のドキュメントを1つ抽出するという処理すら実現できない可能性がある。

しかしサーバー上で処理できる操作にはいろいろ制限があるのもまた実情。


キャンバス アプリでの委任について (Microsoft)

わりと嘘も混ざっているのだけど。

SharePointに限ればこちらの方が詳細だがちょっとわかりにくい表記もある。

Power Apps は SharePoint の機能と操作を委任できます

データテーブルの入力を

Search(Docs,"DocNumber","DocTitle",SearchText.Text)

のようにすればSharePointリストのDocsからDocNumberとDocTitle列にSearchTextテキストボックスに入力された文字が含まれるものを抽出できる。

この処理はDocsのデータ数が多い場合でも、抽出後のデータが500レコード以下であれば期待通りに動くはず。

これはとてもシンプルで、こういう例を見せられるとPower Apps簡単だなと思うのだけど。


ここで作成者の情報で検索したいという話が出てくるが、これが容易ではなかった。

このシステムではドキュメントの作成者はAuthor列にPerson型で格納されている。

Person型は文字列ではないので、Search関数の検索対象にはできない。

さらに言えば、Filter関数での文字列比較に部分一致は使えない。

文字列の比較関数として委任可能なのは = と StartWith しかない。

このため、作成者名での検索は別に入力枠を設けて、

Filter(Docs,StartsWith(Author.DisplayName,AuthorSearch.Text))

のようにしなければならなかった。実際にはSearchの中にFilterを入れ子にしている。

しかし前方一致なんで “Tanaka, Haruki”のようなDisplayNameに対して”Haruki”で検索してもひっかからないんですよね。

何人も同じ名字の人がいると、名前の方が特徴的ならそっちの方が検索しやすいわけじゃないですか。

こういうのSharePointのデータ構造を決めるときに考慮されてるとよかったが。

まだ前方一致で名字の方からマッチするだけマシかもしれないけど。


あと、こういうのも委任できないというのが、○○が空白ではないデータを抽出するというもの。

IsBlank関数は委任不可だが、=Blank()という比較は委任できるという記載はあるが、

では、その逆はどうかという話である。

SharePointリストでは<>演算子、Not演算子は委任不可となっている。

このため、空白ではないデータだけを抽出するという処理は委任できない。

結果的にはこの問題は他の抽出処理で代替出来たのだが、なんでそれができないとなる。


このあたりのデータ処理は切実な問題で、Microsoftには改善を期待したいが。

それに比べればしょうもない話で、ダブルクリック(ダブルタップ)のイベントがないというのがある。

リストからリンクを開くというのをダブルクリックでできると便利だと思ったが。

リストを選択する処理とリンクを開くボタンを別に用意すれば、開くボタンではリストで選択されているデータのURLにLaunch関数で飛ばせばよいからとてもシンプルだが。

これについて調べたら、onselectイベントでボタンをクリックされた時刻差を測定して、条件分岐すればよいという案があった。

If(And(TappedUrl=Filelist.Selected.Link,

DateDiff(TappedTime,Now(),"Milliseconds")<1000 ),

Launch(TappedUrl);UpdateContext{TappedUrl:""}),

UpdateContext({TappedUrl:Filelist.Selected.Link, TappedTime:Now()}))

onselectイベント発生時にTappedUrlにジャンプ先とTappedTimeに現在時刻を保存し、

次のイベント発生時にジャンプ先が一致し、時間差が一定時刻以内なら飛ぶと。

なお、これをやる場合は、リストボックスの複数選択を切らないとうまくいかない。

複数選択可能だと、選択→選択解除になっちゃうからね。

まぁこの目的でリストボックスを使うなら複数選択にはしないでしょう。


Power Appsはデータを抽出して表示・編集するような用途がメインなんでしょうね。

SharePointリストってようわからん機能だなと思ってはいたけど、この用途ではかなり強力ではある。

整形表示しないとさっぱり使いにくいが、整形表示はPower Appsでできる。

Power AppsもWebページと同じように扱えるので、利便性はそこそこ高いのでは?

ただページを最初に開くときちょっと時間がかかりますけどね。

あと、処理するデータ数が増えると、サーバーに委任するにしても、サーバーから転送するにしても遅くなってしまう。

それがどれぐらいなのかな? というのは気になるところである。

画像保存を困難化してはいるが……

とあるサイトで画像が保存できなくて、一体どんな仕掛けなのかと気になって、

それでGoogle Chromeの開発者ツール(F12)で調べてみたのだが、

実はソースコードにimg要素がべた書きされていて、

それをJavascript・CSSで触れなくしてあっただけだった。


Javascriptで画像のダウンロードを困難化する話は聞いたことがあったが、

CSSも使われていたようである。

Javascriptでの対策としては、oncontextmenu, onselectstart, onmousedownの各イベントにreturn false;を設定するというもの。

oncontextmenuは右クリックや長押しなどでメニューを出すときのイベント、

onselectstartはテキストなどを選択開始する操作を開始するときのイベント、

onmousedownは左右問わずクリックするときに発生するイベントである。

いずれも発生時にreturn false;とすることで当該操作を無効化できるらしい。

主はoncontextmenuの無効化ですね。onselectstartはテキストのコピペ対策に使われることが多いようだが、画像にも併用されるようである。


このあたりはよく聞く対策なのだが、スタイルシートで pointer-events: none; というのが設定されていた。

これはポインタセットの対象としないというスタイルらしい。

通常はautoに設定されていて、要素に応じて適切に振る舞う。

どうも調べると、SVGで塗りつぶし部分は選択対象に含むか含まないかとか、そういう設定に使うことが予定されているスタイルらしい。

pointer-events (MDN Web Docs)


あとこのサイトでは使われてなかったけど、よくある対策として透明な画像を重ねるというものがあるらしい。

CSSで透明な画像を背景とした要素を前面に置くとかそんな形らしい。

このあたりから必要なものを選択して使っているのが実情らしい。


また、直リンク対策として、画像ファイルへのアクセス時のrefferを確認するというのがあり、

ソースコード見ればimg要素が丸見えだって言っても、そのURLに直接飛んでも保存できないようになっていた。

これはアクセス時のrefferをそのサイトのURLにすればよいのだが、

しかし、そのような操作ができない場合は確かに保存できない。


いろいろな対策はあるが、障害となるものを取り除き続ければ、結局は保存できてしまうものである。

特にこのサイトはimg要素へのイベント設定・スタイル設定が全てなので、

全要素のイベント設定・スタイル設定を無害なものに変更するJavascriptを作って、ブラウザで実行すれば普通に保存できてしまう。

そういうブックマークレットはそこら辺に転がってるので調べればいくらでも。


あと、開発者ツールはWebページがアクセスしたデータを確認する機能があり、

この機能を使えばJavascriptで動的に開かれた画像としても確認出来る。

今回の場合は画像の読み込みはimg要素でシンプルに実現されているが、

当然その場合もF12で開発者ツールを開き、アクセスしたデータを見れば当該画像データはある。

そこから保存してしまえば当然保存はできる。

一般の人は開発者ツールなんて使わないのが前提であろうけど。

(直リンク対策でrefferを確認する対策もそうだが、一般の人には回避困難な対策であろう)


おそらく一般の人にとってもっとも突破しやすい方法はスクリーンショットである。

スクリーンショットをWebページから回避・検出する方法はほとんどない。

考えようによっては最強の回避手段である。

どれだけ困難化してもスクリーンショットで保存されてしまうなら、

あまり手の込んだ画像保存対策を行う意味はないかもしれない。


どれだけ複雑でもブラウザはHTMLを解析・表示することと、CSSを当てはめること、Javascriptを実行することしかできない。

Javascriptは複雑な操作を行うことができるが、しかしどのような操作をしてるかはソースコードから判明する。

Instagramの画像はそれこそもっと複雑な保存回避が行われているが、

しかしInstagramのURLを投げれば、そこから画像のURLを抽出するサービスは世の中にたくさんある。

どれだけ複雑でも手間さえ掛ければ回避でき、Instagramは利用者が多いからそのような手間を掛けたとしても報われてしまうという、そういうことですね。

そもそもInstagramだってスクリーンショットを撮れば何も複雑なことは考えなくてよいのである。

この時点でどうしょうもない話じゃないかなと思う。


以前、集英社の電子書籍アプリでスクリーンショットをPC・Androidでは禁止、iOSでは検出して警告・アカウント停止を行っているという話を紹介した。

iOSではスクリーンショットできるんですか

iOSでは禁止ではなく警告→アカウント停止という対策になるとか、

PCの外側(HDCP非対応デバイス)やAndroidエミュレータBlueStacksで外側からキャプチャするという方法で回避できてしまうということを紹介した。

しかし、iOSでの警告も十分に効果的であり、回避手段はかなり特殊な方法である。

このぐらいの対策がなされていれば、それは実質困難といってよいでしょう。


しかし、このような回避手段が適用できる前提としては、閲覧にあたって専用のアプリをインストールする必要があり、Webサイトより作成者も閲覧者も手間がかかる。

それだけの手間をかける価値があるかは考えないといけない。

集英社のこのアプリは期間限定のコンテンツがあり、それは自社アプリでの閲覧に限っているので、これだけの手間をかけさせてもよいと考えているのだろう。

集英社という出版業界のガリバーだからこそとれる手段ということで。


それに比べればブラウザの画像保存対策はずいぶんいい加減なものである。

ここまで見てもわかるけど、技術的にはしょうもないものだらけである。

そういう中途半端な対策ならやらない方がよいという考えもあると思う。

しかしやはり時々は見ますね。なんやかんややってしまうんでしょうんね。

作成者も保存を完全回避できるとは思っていないとは思いますけどね。

Webサイトで容易に閲覧できるものは、保存できないわけがない。それに尽きるでしょう。

Chromium版Edgeはこんなとき便利

勤務先の標準ブラウザがMicrosoft Edgeになっていたのだが、

Edgeになったことは知っていたが、実のところ最近まであまり使っていなかった。

ご存じの方もおられるだろうが、現在のMicrosoft EdgeはChromiumベースになっている。

Microsoft Edgeという名前自体は変わっていないが、バージョン79以降と、それ以前ではまるで違うわけである。

勤務先でMicrosoft Edgeが標準ブラウザとなったのはChromium版以降である。


Chromium版Edgeが展開されたのはちょうど1年ぐらい前のことになるのかな。

それ以前の標準ブラウザはInternet Explorerということになっていたが、

しかし実際にはGoogle Chromeを使うことが多かった。

これはInternet Explorerでは社外サイト、一部の社内サイトでも不都合を来すことがあり、暫定措置として標準ソフト化されたという経緯がある。

一方でInternet Explorerしか使えない社内サイトもまだまだ多かった。

なので、用途によって使い分けていたというわけですね。


Chromium版Edgeを標準ブラウザとするという決定の後、

社内サイトのEdge対応の改修が進んだのだが、しかしChromiumとはGoogle Chromeのエンジンそのものである。

ということはEdge対応するということはChrome対応するということに等しかった。

従来通りChromeを使うことは禁じられていないのだから、Edgeに移行する意味はあまり感じていなかった。

Chromium版Edgeの普及に伴い、Chromeが使用禁止になった事実もない。


ただ、最近困った社内サイトがあって、それはセキュアPDFとExcel on Webを併用しているのである。

セキュアPDFはInternet Explorerしか対応していないという。

これはEdgeの標準ブラウザ化の後も変わっていない。

一方のExcel on WebはInternet Explorerで動かなくもないが、ものすごく重い。

画面にもMicrosoft Edgeを使えと表示される。MicrosoftのWebサイトのありとあらゆるところに書いてあるけど。


そこで気づいたのだがEdgeにはInternet Explorerモードがあるはず。

それでセキュアPDFだけInternet Explorerモードに切り替えれば、便利なのではないか。

これは大当たりだった。

セキュアPDFを開いたときだけ「Internet Explorerモードで再読み込みをする」とすればちゃんと開けたし、

あるいはそもそもセキュアPDFのリンクは「新しいInternet Explorerモードタブでリンクを開く」とすればよいのである。

当該タブでは完全にInternet Explorerとして振る舞うようだ。


そもそもChromium版Edgeを導入する理由が、Internet Explorerモードがあることでしたもんね。

しかし、Windows 10であれば、ChromeとInternet Explorerの使い分けでもそこまで変わらない。

Windows 11にはInternet Explorerはないから、EdgeのInternet Explorerモードでないとどうにもならない可能性はあるけど。

しかしシームレスに切り替えられることにメリットがあると認識したので、

これを機に仕事ではChromeをやめてMicrosoft Edgeに移行しようかなと思う。


ChromeとChromiun版Edgeは似たようなものだとは書いたが、

登録できるアカウントがGoogleアカウントかMicrosoftアカウントかというところは異なる。

業務ではGoogleアカウントは使っていないのでChromeにはアカウントを登録していなかった。

一方でMicrosoftアカウントは業務で使うので、Edgeはアカウントに紐付いている。

この点でも職場ではEdgeを使うことは妥当と考える。

逆にプライベートではスマートフォン・タブレットがAndroidなので、

そちらに合わせてPCもGoogle Chromeにして、パスワード管理をGoogleアカウントに一本化した経緯がある。

パスワードを没収されたので

プライベートでInternet Explorer必須のサイトはほとんどないのもある。


ちなみに業務にしても、Internet Explorer必須のサイトはだいぶ減っている。

これはEdge対応の改修を行ったり、陳腐化でシステムごと乗り換えたものが多いためである。

現在もInternet Explorerが必須なのは、セキュアPDFと、あとは社内の2サイトぐらいかな。

そういうサイトはEdgeに登録すれば、そこから90日間は自動的にInternet Explorerモードが選択されるようだ。

90日という制限はそのうちChromiumでも見られるように改修されることに期待しているのか?


しかしInternet Explorerモードも永遠に使える機能というわけではないだろう。

長期的には標準的なブラウザで見られるようにする必要があるだろう。

実際のところ、残るサイトは陳腐化のためシステム乗り換えが必要とは言われており、

いろいろ検討されているようだが、なかなか課題も多いようだ。

最近1つのサイトが既存の社内インフラを使って移行したのだが、使い勝手が……

使用頻度はそこまで高くないし、元々そんなに利便性も高くなかったので許容できるが。

部署独自にシステムを維持するのは大変という判断でこうしたのは理解できるが。

あと少しというところまで追い込んではいるが、しかし残るところはそれなりの難しさもある。どうなることやら。

内部ではUTF-32かUTF-16か

寝正月延長戦みたいになってはよくないと思い、東京までおでかけ。

東京国立近代美術館に行きコレクション展を見てきたが、

珍しくも特別展の一部がコレクション展のフロアに含まれていた。

特に検札係もいなさそうだったから、ここだけ見られそうだったが、それはせず。

京都ではよくあるんですけどね。東京では初めて見たかな。


最近、プライベートで実験目的でPythonをちょこちょこ触っている。

PerlでもRubyでもPythonでもできることはあまり変わらないが、

最近はPythonが無難で使いやすいと考えてこうしている。

習熟度で言えばPerlが一番高いけど、ライブラリとか総合的に考えればPythonかなと。


ふと、Pythonの文字列が内部的にどう扱われているかが気になった。

Unicodeということは知っているのだが……

調べたところ、現在はUTF-32を使っているようである。

sys.maxunicode (import sysが必要)で文字コードの最大値が確認出来るのだが、

これが1114111(0x10FFFF)になっている場合は、内部的にはUTF-32を使っている。

一部の環境ではUCS-2を使っていることがあり、その場合は65535(0xFFFF)となる。


現在は国際的な統一文字コードであるUnicodeが広く使われている。

Unicode自体は「田」に0x7530のようなコードを振ったもので、

そのUnicodeの文字列をどのように表現するかというのにはいろいろな方法がある。

しかし、文字列データを保存・伝送する目的ではほぼUTF-8しか使われていない。

Unicode=UTF-8と考えている人もけっこういそうですけどね。

UTF-8はUnicodeの1文字を1~4バイトのデータとして表現するもので、

ASCIIの範囲の文字であれば1バイトでASCIIコードと同じように表現され、

それ以外の文字はASCIIコードと被らない2~4バイトのデータで表現される。

2~4バイトとはいうが、通常の日本語の文字は3バイトである。


一方、ソフトウェアの内部処理では可変長のコードでは不都合なので、UTF-16またはUTF-32が使われることが多いという。

UTF-16は16bitデータとしてUnicodeの1文字を格納するもので、UTF-32は32bitデータとしてUnicodeの1文字を格納するものである。

しかし、現在はUnicodeの文字コードの最大値は 0x10FFFF となっている。

このためUTF-16は全てのUnicode文字を16bitで格納することはできない。

Unicodeの当初構想では65536種類の文字で全世界の文字を網羅しようと考えていて、16bitのUTF-16が使われているのはその時代の名残らしい。

UTF-16で0x10000以降の文字を表現する場合は、サロゲートペアという機能を使う。

0x10000以降の1文字を16bitデータ2つで表現するんですね。

固定長だからUTF-16を使ったはずなのに、サロゲートペアがあるために可変長になってしまった。

UTF-32を使用する場合はこのような問題はなく、必ず固定長である。


ちなみに一部環境のPythonではUCS-2を使っていることがあると書いたが、

UCS-2はUTF-16と同じで16bitデータとしてUnicodeの1文字を格納するものである。

しかし文字コードの最大値は0xFFFFに制限されている。

じゃあ、UCS-2環境のPythonではUnicodeで0x10000以降のコードになる文字は使えないのか?

実際にはサロゲートペアとして処理できる場合もあるらしい。


UTF-16を使っているプラットフォームとしては.NET Frameworkが有名である。

というかWindows自体がUTF-16を多用していて、それに合わせてるんですよね。

.NET FrameworkというとC#のプログラムで使われていることが知られているが、

Windows PowerShellも.NETを使っているので、そちらで実験。

Unicodeで0x10000以降の文字と言えば、使用頻度の低い漢字などもあるが、

やはりなんといっても絵文字でしょう。例えば0x1F44Dは「👍」である。

絵文字1文字を格納した文字列の長さを出すと「2」と出てきた。

{0xD83D,0xDC4D}という16bitデータ2つで1文字が表現されていることが確認できるが、これこそがサロゲートペアである。


用途にもよるが、そこまでサロゲートペアを意識する必要はない。

フォームに入力された文字列をファイルに保存する場合に、

0x10000以降の文字が入力されたら、1文字が2文字相当で格納されるが、

しかしそれをファイルに保存するときにはUTF-8などに変換するわけである。

これらの変換ではサロゲートペアのことは自動で処理される。

ただ、1字ずつ文字コードを取得しようと考えると、サロゲートペアを意識する必要がある。

文字列の1文字ずつを整数変換する方法ではサロゲートペアが処理されない。

System.Char.ConvertToUtf32関数で文字列中の指定位置でサロゲートペアを処理して文字コードを取得できる。

そしてサロゲートペアを使う0x10000以上の場合は、位置を2文字進める処理が必要であろう。


.NET FrameworkにしてもWindowsにしても過去のプログラムのバイナリは変えられないので、

UTF-16は固定長の文字コードではないという問題があるのは知っていても、互換性のために変えられないということなんだろうなと。

一方でPythonの場合、ソースコードを実行時に解釈するわけで、

ソースコードに記載された“ABC”という文字列が、実行時に{0x41, 0x42, 0x43}となっても、{0x0041, 0x0042, 0x0043}となっても、{0x00000041, 0x00000042, 0x00000043}となっても、

3文字のデータであること、格納されている値が0x41~0x43であることは同じである。

内部的な取扱が違ったとしても、結果的にその差を意識しない場合はある。

だからPythonではUTF-32が一般的に使われるようになったと言えるのでは?

数字だらけのQRコードにした意味

昨日、QRコードが印字されたワクチン接種証明書の話を書いた。

QRコード付き証明書のここが良い

国際標準というICAO VDS-NCはQRコードを読み取ってみると、半分ぐらいまでは普通に読めるデータが入っている。

途中からBase64でエンコードされた文字列が並んでいるが、これは電子署名ですね。

一方で国内用・海外用の両方に記載されたSMART Healthcare Cards(SHC)は読み取ると、shc:/56762909… という数字の羅列が続いている。

実はこの数字、Base64でエンコードされた文字列の文字コードを10進数表記したものである。

ハワイ州のワクチン接種証明書SMART Health Cardを読んでみる (Zenn)

しかもデータ本体はDeflate(ZIPなどで使われるアルゴリズム)で圧縮されているのだtろいう。


そもそもBase64というと、E-mailで8bitの文字コードやバイナリデータを扱うために使われていたものである。

これはかつてはSMTPプロトコルでは7bitの文字コードしか扱えなかった。

そのため8bitのデータ3byteを7bitのASCIIコード4文字に変換する処理を行い、これをBase64と呼んでいる。

日本では7bitのISO-2022-JPという文字コードを使うことが一般的だったが。

現在はSMTPでも8bit取り扱えるので、8bitのままUTF-8で送ることもあるけど。

文字コードはさておき、添付ファイルは現在もBase64に変換されて送られることが通常である。

現在はBase64はE-mailに限らず多目的に活用されている。

しかし、その原点は8bitのデータを7bitのASCIIコードに変換することにある。


一方のQRコードだがデータの格納方式には4方式がある。

数字モード、英数字モード、バイナリモード、漢字モードである。

数字モードは数字を3桁ずつに分けて10bitデータに変換する。

英数字モードは数字・大文字アルファベットなど45種類の文字を2文字ずつ分けて11bitデータに変換する。

バイナリモードは8bitのデータをそのまま8bitで格納する。

漢字モードはShift_JISの1字を13bitで格納する。

実際には漢字でもUTF-8などでバイナリで格納している場合が多いのではないか。


で、Base64の文字列は大文字小文字混在なので、英数字モードは使えない。

このためQRコードではバイナリモードを選択し、3文字→24bit となる。

一方で文字コードから45引いた数値を10進数にして2桁表記すると、

3文字は数字6桁になり、QRコードでは数字モードを選択すると、数字6桁は20bit。

というわけで、数字表記にする方がコンパクトなQRコードが作れるんですね。

もちろんバイナリデータをバイナリデータとして格納すればよりコンパクトだが、

JWT形式という既存のデータ形式が流用できるメリットも考慮したのではないか。


しかし、実は接種証明書アプリで表示されるQRコードはこのメリットが生きていない。

QRコードの情報量とバージョン (QRコードドットコム)

接種証明書アプリで国内向け証明書のQRコードのサイズはVersion30である。

誤り訂正レベルはLだったので、13880bit格納できるサイズである。

一方で格納されていたデータは1675文字、最初の5文字以外は数字である。

この場合、最初の5文字(小文字含む)はバイナリモード、残りの1670文字は数字モードと分ければ、

ヘッダー部分を合わせて5618bitで済むはずで、これはVersion18に相当する。

1675文字をバイナリデータで格納すると13408bit、Version30にピタリ一致する。

だから本来はだいぶ小さなQRコードで済むのである。

QRコード セル数/バージョンの指定 (CMAN)

ここで18と30を見比べてみればわかりますが。


というわけで、せっかくの圧縮も水の泡という感じはあるが、これはこれで正当なQRコードではある。

shc:/に続く数字を2桁ずつ10進数で解釈して文字列に変換する。

すると “eyJ6……J9.5Z……QQ.Ae……NQ” というようなピリオドで3節に区切られた長い文字列が出てきた。

これがJSON Web Tokens(JWT)形式というもので、Webサイトでの認証データのやりとりに使われているらしい。

特徴としては電子署名とデータの暗号化ができることで、特に電子署名ですよね。

で最初のeyJ6……J9の部分をBase64から戻すとこんなデータは入っている。

{"zip": "DEF", "alg": "ES256", "kid": "f1vhQP9oOZkityrguynQqB4aVh8u9xcf3wm4AFF4aVw"}

ここは特に秘密にするデータはないと思うので貼ってしまうが。

algとkidが電子署名のアルゴリズムと鍵の識別ID(フィンガースタンプ)である。

ここら辺は一般的だがzipという情報があるのは珍しく、これがデータ本体がDeflateで圧縮されていることを表しているのだという。

2つ目の5Z……QQの部分を展開して表示するのは、Pythonで書くとこんな感じ。

databin=zlib.decompress(base64.b64decode(data64,"-_"),-15)

すると、おー。なんか接種証明書っぽいデータが出てきた。

1198byteのデータがBase64で635文字(476byte相当)だからそこそこ圧縮効果あるね。

3つ目の電子署名は “eyJ6……J9.5Z……QQ” に対する電子署名が格納されているはず。


これを逆向きにすれば接種証明書のQRコードを作れる。

名前が”\\u4e09\\u5341\\u4e03”(「三十七」に相当、サンプルのQRコードから引っ張って来た)のようにUnicodeの文字コードで格納されている。

無駄な表記っぽいなぁと思うけど、圧縮するならあまり問題はないかと思いながら書き換える。

そして、データを圧縮して、最初と最後の節はそのままくっつけてQRコードを作成する。

Pythonのqrcodeライブラリは自動的に小さくなるようにモード切替ながら作ってくれるんだよね。ちゃんとVersion18で生成された。

そして接種証明書アプリで読むと、ちゃんと読めた。

氏名は変更されていたが、電子署名が一致しないので改ざんを検出して「有効性が確認できませんでした」と表示された。というわけで完璧ですね。


というわけで、実はSHCのデータ形式はよく考えられていたという話ですね。

問題は接種証明書アプリではその特徴をうまく生かせていないことだが。

でも読取りはできたから、他の発行者が意図通りコンパクトに作ったQRコードでも受入可能なのはとりあえずよかった。

今後、この巨大QRコードも改良されてちょっと小さくなるかも知れない。


しかしQRコードに数字だけ、英数字だけをコンパクトに格納できるのは知らなかったな。

一番小さなVersion1で誤り訂正レベルMだと、数字は34桁、英数字は20字、バイナリは14byte、漢字8字となる。

英数字モードの対象が数字・大文字アルファベットなのは、工業用バーコードとして今でも広く使われているCODE39に合わせたのではないか。

品名など格納する用途ではこれでいいわけですからね。

CODE39は誤読が少ない分、巨大になりがちで、これがQRコードで大幅にコンパクトになるのは確かにメリットが大きいわけである。

ただ、多目的に使われるようになると小文字が入るだけでバイナリモードになることが気になってしまう。

まして日本ローカルのShift_JISを比較的高密度に格納できる漢字モードなんて……


そういう事情を総合的に勘案した結果が、JWT形式のデータを1文字ずつ10進数2桁に置き換えたコードをSHCのデータとするという方法なんだろう。

しかしヘッダーの “shc:/” 部分はバイナリモード前提なので、あまり考えないと全体をバイナリモードにして余計に巨大なコードになってしまうと。

全体が英数字モードになるのがシンプルで良いけど、そのためにBase32(一応そういうものもある)を使うのも汎用性に欠けるだろう。

実際、45byteがBase32では英数72字→QRコードで396bit相当であるのに対し、

Base64→10進数だと数字120桁→400bit相当なので、そこまで差はない。

それならば従来のJWTの処理をできるだけ流用できる方がお得というのはなるほどと思った。