database java maven security

H2 Databaseで生成したSHA256値をJavaで生成したSHA256値と比較してみる

投稿日:2020年8月15日

ファイル、サーバ、メモリ、様々な動作形態がとれてプロトタイピングや、配布アプリの組み込みDBとして便利に使えるPure Javaデータベースの「H2」。

  1. zipファイルでWin10にインストール。
  2. H2 Databaseコンソール(Webブラウザ)起動。
  3. H2にユーザ情報テーブル作成。
  4. データ追加。(パスワードはH2のSHA256ハッシュ関数で保存)
  5. JDBCでH2に接続、平文パスワードをJavaでSHA256化した値と比較。

こんな感じのことを試してみようと思います。

環境

  • Windows10
  • Java 8(Eclipse同梱ではなくOSにインストール)
  • Eclipse 202003(Java11)
  • H2 Database ver 1.4.199

H2はJavaで作られていて、コマンドプロンプトから実行します。
コマンドプロンプトからJVMを起動できるよう、OSにJDKをインストールしてjavaコマンドを実行出来るようにしておきます。

(*)2020/08時点でH2の最新は1.4.200ですが、私の環境だとDBファイルを作る際に以下のエラーに遭遇しました。

一般エラー: "java.lang.IllegalStateException: Unable to read the page at position 1967576057930752 [1.4.200/6]"
General error: "java.lang.IllegalStateException: Unable to read the page at position 1967576057930752 [1.4.200/6]" [50000-200] HY000/50000 (ヘルプ)

githubのissueを見るとOpenなままです。

この為、一つ下のバージョン1.4.199を使用します。(調査するのがめんどくさい)

H2 Databaseの構築

インストールからデータ追加まで。

Windows10にインストール

インストーラ形式とzipファイル形式があります。
今回はAll Platform用のzipファイルを解凍してC:\opt\h2に置きました。

C:\opt\h2>dir
2020/08/14  23:45    <DIR>          bin
2019/03/13  14:58               385 build.bat
2019/03/13  14:58               546 build.sh
2020/08/14  23:45    <DIR>          docs
2020/08/14  23:45    <DIR>          service
2020/08/14  23:45    <DIR>          src

コンソール(Webブラウザ)起動

binディレクトリに入り、h2.batを実行。

C:\opt\h2>cd bin
C:\opt\h2\bin>h2.bat

ブラウザが起動し、8082ポートでコンソール開始画面が開きます。
今回はHDDに作成されるDBファイルにTCP接続するサーバモードで使います。

「接続」するとホームディレクトリ直下にtest.mv.dbファイルが出来上がり、SQLを実行できる画面に遷移します。

SQL編集画面からテーブルを作成

表示されたSQL編集画面で以下のテーブルを作成してみます。

  • users:ユーザ情報(メール、パスワードハッシュ、権限)
  • role:権限マスタ
/* 権限マスタ */
create table role(
    id int primary key,
    name varchar(50)
);

/* ユーザ情報 */
create table users(
    mail varchar(255) primary key,
    name varchar(255),
    password varchar(255),
    role int,
    foreign key(role) references role(id)
);

作成したテーブルにデータを追加

roleテーブル:1:user(一般利用者)、2:admin(管理者)
usersテーブル:パスワードはH2のhash関数でSHA256ハッシュ化

H2の関数詳細は公式を参照。

insert into role values(1, 'user');
insert into role values(2, 'admin');

insert into users
values(
    'yamada@hoge.com',
    '山田太郎',
    hash('sha256', stringtoutf8('password1')),
    1
);

insert into users
values(
    'suzuki@foo.com',
    '鈴木一郎',
    hash('sha256', stringtoutf8('password2')),
    2
)
;

selectして追加されたか確認。
一般利用者の山田さん、管理者の鈴木さんが登録されました。

山田さんのパスワードは「password1」、鈴木さんのパスワードは「password2」でしたがハッシュ化されてシステム側では元のパスワードがなんだったのか分からなくなっています。

EclipseからJDBCでH2に接続

H2側のデータが整ったのでJavaプログラムから接続してみます。

mavenプロジェクトを作ってH2の依存を追加

(gradleでもいいですが)EclipseでMavenプロジェクトを作成。
mvnrepositoryからH2のドライバを含むjarを探し、pom.xmlに追加します。

(ついでにJava11でビルドされるようにmaven-compiler-pluginも設定してます)

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

        (snip)

    <!-- java11でコンパイル -->
    <build>
        <finalName>h2</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <inherited>true</inherited>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!-- H2のJDBCドライバorg.h2.Driverクラスを含むjarファイル -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.199</version>
        </dependency>

        (snip)

    </dependencies>
</project>

DBハッシュ値とJavaでSHA256化した値を比較

単純なJDBCプログラムでH2から山田さんのハッシュされたパスワードを取得、Javaでハッシュしたパスワードと比較してみます。

package sample.h2;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * Hello world!
 *
 */
public class App {
	public static void main(String[] args) throws SQLException, NoSuchAlgorithmException {
		String jdbcUrl = "jdbc:h2:tcp://localhost/~/test";
		String jdbcUser = "sa";
		String jdbcPassword = "";

		// DB値と比較するyamadaの平文パスワード
		String userPassword = "password1";

		// 1.usersテーブルからyamadaのレコードを取得
		try (Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword)) {
			conn.setAutoCommit(false);

			String sql = "select * from users, role where users.role = role.id and users.mail = 'yamada@hoge.com'";

			Statement stmt = conn.createStatement();
			ResultSet rs = stmt.executeQuery(sql);

			String mail = null;
			String name = null;
			String password = null;
			while (rs.next()) {
				mail = rs.getString(1);
				name = rs.getString(2);
				password = rs.getString(3);
				System.out.println(mail + " | " + name + " | " + password);
			}

			// 2.yamadaの平文パスワードをsha256ハッシュ、16進文字列化。
			MessageDigest md = MessageDigest.getInstance("SHA-256");
			md.update(userPassword.getBytes());
			byte[] sha256 = md.digest();
			StringBuilder sb = new StringBuilder(2 * sha256.length);
			for (byte b : sha256) {
				sb.append(String.format("%02x", b & 0xff));
			}
			System.out.println("yamadaのパスワードハッシュ = " + sb);

			// 1と2が等しいか調べる
			if (sb.toString().equals(password)) {
				System.out.println("パスワードは等しい");
			} else {
				System.out.println("パスワードは等しくない");
			}

			stmt.close();
		}
	}
}

実行結果

平文「password1」をH2でハッシュ化、Javaでハッシュ化した結果が等しいと分かりました。

yamada@hoge.com | 山田太郎 | 0b14d501a594442a01c6859541bcb3e8164d183d32937b851835442f69d5c94e
yamadaのパスワードハッシュ = 0b14d501a594442a01c6859541bcb3e8164d183d32937b851835442f69d5c94e
パスワードは等しい

まとめ

AP、DBどちらでハッシュ化しても同じ結果になることが分かりました。

お仕事で実戦投入する際はハッシュする担当はAPかDBに統一しますし、より強度を高める為にソルトを使ったり、暗号化したり、keycloakやopenstack-keystone他外部の認証トークン生成ツールを使うなどすると思います。

実践向きの検証ではないですが、どちらでハッシュ化しても同じ結果になることを知っておくのは運用面を考えると良いことかな、と。

可搬性にも優れていますし、試作の時点ではH2でも十分代替が効きそうです。

-database, java, maven, security
-, ,

執筆者:

関連記事

IVS、サロゲートペアが混じった文字列をJavaのWebアプリでワードカウントする

(結論:java.text.BreakIteratorは「サロゲートペア文字 + IVS」の8バイト文字も1文字としてカウントしてくれる) 先日、IPAmj明朝フォントを使ってIVSを含んだUnico …

IPAmj明朝のttfファイルをJVMに読み込ませてSwingで表示(要Java11)

目次1 はじめに2 Java11でSwingがIVSを認識するようになった3 Java Swingソース4 動作確認4.1 Java 8(1.8.0_202)で実行4.2 Java 11(11.0.2 …

SpringBootアプリにBootstrap4を追加(WebJars使用)

SpringBootにCSSやJSを追加する場合は概ね以下のパターンがあるんじゃないかと思います。(bowerはもう使わない方向で) CDNで外部から読み込む<script src=&#8221 …

ブラウザでRSA暗号化したデータをサーバで復号する(Angular + JSEncrypt、Spring MVC)【前編】

セキュリティ的にクリティカルなデータをクライアントブラウザで暗号化保存するようにしてみます。 通信経路はHTTPSで暗号化されていてもスマホに重要なデータが平文で残っていたら珠に傷です。 目次1 環境 …

CentOS7にOpenJDK11をインストール、alternatives後の再ログインでJAVA_HOMEも自動変更

OpenJDKはCentOS-Baseリポジトリのupdatesに登録されているので新規yumリポジトリ追加は不要です。 [root@spock tmp]# yumdb search from_rep …

 

shingo.nakanishi
 

東京在勤、1977年生まれ、IT職歴2n年、生涯技術者として楽しく生きることを目指しています。デスマに負けず健康第一。