情報セキュリティ

電子メールでパスワードを送るべからず

情報セキュリティ意識が高まってきていることは歓迎すべきことであるが,誤った対策が広まってしまっているケースもある。メールの添付ファイルを暗号化するまではよいのだが,問題はそのパスワードの受け渡し方法がかなり杜撰なのである。後から同じメールで送られてくるケースが非常に多い。これはむしろ危険なのでやめてもらいたい。

 まず,添付ファイルが暗号化されていると,PCのセキュリティソフトが機能しなくなる。メールサーバ上で稼働しているセキュリティソフトの場合は,マルウェア等を検知するとサーバ上でそのメールを削除してくれるので,PCの危険を軽減できる。暗号化されたファイルがメールに添付されていた場合,これらをすり抜けてしまう。そして,運がよければ,復号した瞬間に検知されることになる。運が悪ければ,マルウェアの餌食となる。

 もう一つは,パスワードが盗まれる危険があることだ。暗号化されたファイルを盗まれ,かつ,パスワードも盗まれては暗号化した意味がまったくない。同じメールアドレス同士であれば,ずっとその間のやり取りを盗聴していれば,暗号化されたファイルと復号のためのパスワードを両方とも窃取できる。

 安全なのは,メール以外の方法でパスワードを受け渡すことである。SMS,チャット,電話,FAX,郵便など,他の手段でパスワードを通知すれば,かなりリスクを低減できる。公開鍵暗号方式を用いるのがよりよい方法ではあるが,そこまでするほど秘匿性が高くない場合には有効な方法だろう。

新型コロナウイルス関連

有名ホテル前に群衆

 今日の夕方,都内の有名ホテル前に大勢の人が群がっていました。皆,そのホテルの名前の入った紙袋を持っていたので,ホテルでのイベントにでも参加していた人達でしょう。中高年が多かった印象です。

 イベントの開催制限が緩和されたとはいえ,新型コロナウイルスの感染拡大防止対策を徹底することが前提であったはずです。このような群衆を発生させてしまうことは,感染拡大防止対策を十分に行っているとは言えません。ホテルの中ではしっかり対策を行ったのかもしれませんが,イベントが終わったら周辺の駅に人が集中してしまうことは容易に想像できます。重大な過失,否,未必の故意です。しっかりした感染拡大防止対策を講じることができないならば,イベントは自粛すべきでしょう。自分達の都合しか考えず,社会全体の利益に反する行為と言わざるをえません。

 ホテルの名前は公開しませんが,私がそのホテルチェーンを利用することはないでしょう。内閣官房にも報告しておきました。

雑記, 新型コロナウイルス関連

Webへのスマホ以外でのアクセスが排除される

Webページへスマートフォン以外からのアクセスが排除される例が増えてきている。中には通信キャリアによる認証や課金といったサービスを利用する都合上,そうすることに合理性があるケースもある。しかし,広く一般に見せたいWebページでさえも,スマホ以外からの「動線」がないサイトが増えているのだ。

 下の画像は私の住んでいる東京都板橋区の新型コロナウイルス対策に関するクラウドファンディングのリーフレットである。QRコードは印刷されているのだが,人間が読める形ではURLが書かれていない。スマートフォンにはカメラも搭載されているし,QRコードを読み取るアプリも多く配布されている。しかし,PCを使っているユーザはわざわざQRコードなど読み取らないだろう。ブラウザを起動して,キーワードで検索する方が一般的だし,フィッシングサイトを避けたい場合は,URLをタイプする。短縮URLなら打ち込んでも苦にならないだろう。QRコードのみでの記述は,事実上「スマホ専用」になってしまい,それ以外のユーザを排除してしまう。

新型コロナウイルス対策に関するクラウドファンディングのリーフレット
JDK

Java 15の新機能(8)-Edwards-Curveデジタル署名アルゴリズム

Java 15では電子署名のアルゴリズムとして,Edwards-Curveデジタル署名アルゴリズム(EdDSA)が追加されている。これは,RFC8032で定義されているものだ。

 さっそく,EdDSAアルゴリズムを用いてデジタル署名を作成するプログラムを作成してみる。

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class EdDSAExample
{
    public static void main(final String[] args) throws Exception
    {
       KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("Ed25519");
       KeyPair keyPair = keyPairGen.generateKeyPair();
        String message = "Hello World";
        Signature signature = Signature.getInstance("Ed25519");
        signature.initSign(keyPair.getPrivate());
        signature.update(message.getBytes());
        byte[] sign = signature.sign();
        System.out.println(byteToHex(sign));
    }
    private static String byteToHex(byte[] bytes)
    {
        return IntStream.range(0, bytes.length)
                        .mapToObj(i->String.format("%02x", bytes[i]))
                        .collect(Collectors.joining(" "));
    }
}

コンパイルして,実行すると次のようなデジタル署名が出力される。

45 7a 1f 2e 40 0a 82 1a 56 e3 96 6e 0c b5 b5 f5 30 7f 28 f2 a1 64 49 5d 62 f6 74 46 6a f4 11 7e 8e dc d9 a7 ce 78 0e 65 ee 71 24 79 89 4f aa 8c 38 a2 98 55 10 4b 79 e1 76 6b d7 de 32 df 40 08

JDK

Java 15の新機能(6)-Foreign-Memoryアクセス

Javaヒープ外にメモリを割り当てたり,読み込んだりする手段が提供された。MemorySegmentインタフェースを用いて,メモリセグメントを取得する。asByteBuffer()メソッドを使えば,ByteBufferのメソッドを使って操作できるため,これまでと同じように扱うことができる。以下のプログラムでは4バイトのメモリを割り当て,全バイトに0xFFを書き込んでいる。そして,その領域をint型で読み込んでintでの値を出力する。

import java.nio.ByteBuffer;
import jdk.incubator.foreign.MemorySegment;

public class ForeignMemoryExample
{
	public static void main(final String[] args) throws Exception
	{
		try(MemorySegment segment = MemorySegment.allocateNative(4L))
		{
			ByteBuffer buffer = segment.asByteBuffer();
			buffer.put(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF});
			int intValue = buffer.getInt(0);
			System.out.println(String.format("int型での値は:%d", intValue));
		}
	}
}

これをコンパイルする。その際,module-info.javaを作成して,jdk.incubator.foreignモジュールを利用できるように記述しておく必要がある。

実行するには,次のコマンドを実行する。

java –add-modules jdk.incubator.foreign –enable-preview ForeignMemoryExample

実行すると,次のように出力されて,2の補数表現でintが取得された。

int型での値は:-1

JDK

Java 15の新機能(5)-Record型

Record型はJava 14にプレビュー機能として取り入れられて,引き続きプレビューとなっている。新たにシールインタフェースを実装できるように拡張されている。次のようにAnimalインタフェースを実装するレコードPersonを定義できる。

public class RecordExample
{
    public static void main(String[] args)
    {
        Person person = new Person("はらだ たかひこ", 38);
        System.out.printf("名前: %s\n", person.getName());
        System.out.printf("年齢: %d歳\n", person.getAge());
    }
}
sealed interface Animal permits Person
{
    public String getName();
}
record Person(String name, int age) implements Animal
{
    @Override
    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
}

レコード型については,次の記事を参照していただきたい。

https://jy-info-sys.com/2020/03/23/java-14-%e6%96%b0%e6%a9%9f%e8%83%bd-4/

JDK

Java 15の新機能(3)-テキストブロック

Java 14でプレビューとなっていたテキストブロック機能が正式リリースとなった。ヒアドキュメントのように複数行にわたって文字列リテラルを記述できる機能である。次のように「”」3つでリテラルを囲むだけでよい。

public class TextBlock
{
    private static final String text = """
            <html>
                <head>
                </head>
                <body>
                </body>
            </html>""";
    public static void main(String[] args)
    {
        System.out.println(text);
    }
}

上のコードをコンパイルして実行すると,以下のように出力される。

<html>
    <head>
    </head>
    <body>
    </body>
</html>

JDK

Java 15の新機能(2)ーHiddenクラス

Hiddenクラスは、かなり特殊な用途でしか使われないと思われる。Javaバイトコードが直接アクセスできないクラスを動的に作り出すことを可能にする機能である。フレームワークなどが動的に作り出すクラスが、リフレクションによって検出されてしまうのを避ける意図があるという。

 このクラスはその特性ゆえに、実験するには少々手間がかかる。java. lang. invoke. MethodHandles. Lookup # defineHiddenClass()メソッドにbyte[]型でクラス定義を与えること以外にはインスタンスを作成する方法がないようだ。そこで、下準備として、次のようなHello Worldプログラムを作成して、コンパイルしclassファイルを用意しておく。このclassファイルを読み込んで利用することにする。

public class Hello
{
    public final String message = "Hello World!";
    public static void main(String[] args)
    {
        Hello instance = new Hello();
        instance.greet();
    }
    public void greet()
    {
        System.out.println(message);
    }
}

続いて、これをコンパイルしたバイトコードを使って、Hiddenクラスのインスタンスを使用するプログラムを作成する。FilesクラスのstaticメソッドreadAllBytes()メソッドを使って、上のHelloクラスをコンパイルしたHello.classファイルをbyte[]型の変数bytesに読み込む。続いて、MethodHandles.lookup()によって、ルックアップオブジェクトlookupを取得する。lookupのdefineHiddenClass()メソッドに、Helloクラスのバイトコードを読み込んだ配列bytesを渡して、Hiddenクラスを定義する。

lookupのfindClass()メソッドを使って、Class<?>オブジェクトを取得する。このオブジェクトを取得できれば、あとは従来のようなリフレクションを使ってインスタンスを作成したり、メソッドを呼び出したりできる。

import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.*;
import java.io.*;
import java.nio.file.*;

public class HiddenExample
{
    public static void main(final String[] args) throws Exception
    {
        byte[] bytes = Files.readAllBytes(Paths.get("Hello.class"));
        Lookup lookup = MethodHandles.lookup();
        lookup.defineHiddenClass(bytes, false);

        Class<?> clazz = lookup.findClass("Hello");

        Constructor constuctor = clazz.getDeclaredConstructor();
        Object hiddenClassInstance = constuctor.newInstance();
        
        Method method = clazz.getDeclaredMethod("greet");
        method.invoke(hiddenClassInstance);
        try
        {
            Field field = clazz.getField("message");
            field.set(hiddenClassInstance, "Goodbye!");
        }
        catch(IllegalAccessException e)
        {
            System.out.println("    finalフィールドは変更できません "+ e.getMessage());
        }
    }
}

このプログラムをコンパイルして実行すると、次のような出力が得られる。Hello World!と出力されていて、Helloクラスのgreet()メソッドが実行されていることがわかる。また、finalフィールドの値を変更しようとして、例外も発生している。

Hello World!
    finalフィールドは変更できません Can not set final java.lang.String field Hello.message to java.lang.String

普通にクラスを定義して、それを利用することを考えると、面倒なことこの上ない。それでも、フレームワークを作っている人には役に立つのであろう。