OpenDolphin は『真正性』すら満たしていないかもしれない
以前にもサーバの API を通して偽造カルテをデータベースに入れ込む、みたいなことをやったのだが(この記事の『医療向けオープンソースソフトウェアのセキュリティに関する問題点』の項参照)、
今度はもうちょっと巧妙なカルテの改竄(対策)を考えてみる。
OpenDolphin では、hibernate というORMを使って、「カルテを修正したときは、元のバージョンは消去せず、必ず新しいバージョンを作る」ということで改竄を防止している。
なのだが、これは「データベースにアクセスするときは hibernate を使う」というのが大前提になっている。
実際のカルテ実体のデータは、データベースの blob 領域というところに保存されている。当たり前だが blob オブジェクトには hibernate を使わずともアクセスできる。
最も単純な例を考えてみよう。
id=11 のレコードに blob で test と書き込む。
ところが、このときの oid(lob への一種のポインタみたいなものです)は、データベースに直接問い合わせることで知ることができる。
このときは 47410。
この値を使って、blob を書き換えることは可能だ。
‘test’ は16進数表記だと 0x74657374 になるのだが、これを 0x74747474 に変更する。
hibernate を使って id=11 のレコードを再度復元すると該当する blob は ‘tttt’ (0x74747474)となっている。
実際は、blob を書き換えた時に oid も変更されてしまうので(47410 → 47412 に変化している)、よくできた管理者であれば、この異常に気がつくと思うが、そうでない人ならば、改竄されたところで気がつきもしないだろう。
OpenDolphin はソースコードが公開された時点では「データ構造が複雑なので、真正性は担保されている」みたいなことが言われていたが、データ構造が複雑だからといって、それは解読不能ということでも、想定していた方法以外での操作が不可能ということでもない。
思うのだが、データベースなどの何らかのストレージに医療記録を平文で「普通」に記録する方式はもうそろそろ限界なのかもしれない。
かといって、ブロックチェーンみたいな話をいきなりされてもなあ。。。という感じで、ここら辺は難しいところですね。
なお、紹介状などをスキャナで電子化して、システムに入れ込むような場合は、既にガイドラインは示されていて
・管理者の電子署名
・タイムスタンプ
を施すことでOKとされています。
逆にノーマルPDFを単にシステムに取り込んだだけでは、正式な医療記録とはみなされません。
政府の医療DXの各種政策を見るに、HPKI 基盤を使って真正性を担保するようなトレンドになっていくんでしょうか。
ところで先ほど openEHR というオープンソースの EHR のサイトを覗いてきたが、ここら辺は配慮がされているようで、記録は persistence layer という一種の抽象化されたレイヤーを通して行うようだ。
要するに永続化の手段まで具体的に決めてしまうと、そのセキュリティホールを突いて記録を改竄する輩が出てくるため、特定のストレージに限定しないということなのだろう。
ところで、openEHR はけっこう間違った情報が流れているみたいですね。よく PHP で書かれて、みたいな紹介のされ方しているが、普通に Java コードも確認できたけど?
猪股弘明
Java hibernate を利用した PotsgreSQL LOB の取り扱い
以前に OpenDolphin-2.7m という電子カルテのプロジェクトをやっていたとき、ユーザー(大半は医師の方々)の理解がアヤしいと感じた箇所は LOB のあたり。
一般的に『データベース=巨大なエクセル』みたいな説明がなされているので、「電子カルテの実体は LOB 領域に永続化されています」と言って即座に理解できる医師はそう多くなかった。
OpenDolphin 自体のデータ構造がかなりわかりにくく、コーディングもかなり「正統派」スタイルだったため、敷居が高かったせいもあるのかもしれない。
PostgreSQL の LOB に関しては、sql ・jdbc などのドライバ・ORM (Java であれば hibernate)などを介して操作できる。
プログラミング言語を介して LOB を操作するのであれば、やはり ORM を使うのが一番楽だと思う。
最もシンプルな書き方は以下の通り。
@Entity
public class Lobs implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String content;
@Lob
private byte[] binarylob;
@Lob
private String charlob;
(以下、セッター・ゲッターなどを定義)
適当な名前の String と byte[] を用意して @Lob アノテーションを付ければ、hibernate はこれを LOB (それぞれ clob, blob になる)として取り扱ってくれる。
プロジェクトが適切に構成されていれば、アプリ起動時に hibernate は class Lobs に対応したテーブル lobs を自動で作成してくれる。
実際に得られたテーブルは以下の通り。
この書き方だと
・blob, clob とも oid 型になる
・String は character varying(255) となる
のが「へえ」という感じだ。
次回は、lob の書き込み/読み込みの具体的な方法あたり。
猪股弘明