Skip to main content

Javaで対称暗号アルゴリズムを安全に使う

blog feature pypi spoof

2023年10月18日

0 分で読めます

デジタルにつながる現代社会では、デジタルデータの保護が最優先事項となっています。銀行から医療まで、さまざまな分野でJavaアプリケーションが広く使われていることから、暗号化の重要性を強調する必要があります。暗号化とは、読み取り可能なデータ(平文)を読み取り不能なデータ(暗号文)に変換することです。これにより、暗号化されたデータが傍受されても、権限のない人がアクセスできないようにします。

開発者としてまず判断すべきなのは、暗号化が必要かどうかです。奇妙な質問に聞こえるかもしれませんが、暗号化の重要な原則は、暗号文を元のテキストに戻せることです。たとえばパスワードの場合、元のテキストを「復号」できるようにしたくはありません。そのため、暗号化ではなく、パスワードのハッシュ化を使用します。

Javaでの暗号化

Javaアプリケーションは、顧客情報、金融情報、取引記録などの機密データを扱うことがよくあります。Java開発者にとって、堅牢な暗号アルゴリズムを使用することは、機密データの完全性と機密性を維持するうえで欠かせません。これにより、ユーザーとシステム内の重要なデータを保護できます。

対称暗号と非対称暗号

一般に、暗号化には対称暗号と非対称暗号の2種類があります。それぞれ仕組みが根本的に異なり、用途も異なります。

対称暗号では暗号化と復号に同じ鍵を使うため処理が速い一方、鍵を共有する必要がある場合は安全性が低くなります。これに対して非対称暗号では、公開鍵と秘密鍵のペアを使います。公開鍵は暗号化に、秘密鍵は復号に使用します。大量のデータを暗号化する場合は対称暗号が適しており、ネットワーク経由で鍵を安全に共有する場合は非対称暗号が最適です。暗号化には2種類ありますが、この記事ではJavaアプリケーションにおける対称暗号にのみ焦点を当てます。

対称暗号を使う場合は、鍵管理にハードウェアセキュリティモジュール(HSM)を活用し、エンベロープ暗号化のパターンを採用するサービスを利用するのが最善です。暗号化ソリューションで最も難しい鍵管理を簡素化できます。ただし、Amazon Key Management Service(KMS)のようなサービスを利用できず、それでも自分で実装したい場合は、以下の情報を参考に安全に実装してください。この記事の以降の部分では、javax.cryptoパッケージを使ってJavaアプリケーションに対称暗号を実装する方法を説明します。

古い暗号アルゴリズムとそのリスクを理解する

暗号化は重要ですが、すべての暗号アルゴリズムが同等に安全というわけではありません。DES(Data Encryption Standard)や3DESなど、一部の古い、あるいは「時代遅れの」暗号アルゴリズムには、攻撃者に悪用される可能性のある脆弱性が見つかっています。かつては安全と考えられていたこれらのアルゴリズムも、コンピューターの処理能力や暗号解読技術の進歩により、安全ではなくなりました。

Javaアプリケーションで安全でない暗号アルゴリズムを使用すると、リスクにつながる可能性があります。攻撃者が暗号化データにアクセスした場合、暗号アルゴリズムが古く、既知の脆弱性があれば、データを復号されるおそれがあります。データ侵害が発生すれば、ユーザーや組織に壊滅的な影響を及ぼしかねません。

アルゴリズムと暗号モードに関する推奨事項

対称暗号アルゴリズムについて、OWASP foundationは現在、鍵長が最低128ビット、できれば256ビットのAES(Advanced Encryption Standard)を推奨しています。また、安全なモードと組み合わせて使用してください。さらに、NISTの暗号標準とガイドラインでも、ブロック暗号方式としてAES暗号化が承認されています。

AESなどの暗号方式では、複数のモードを利用できます。モードごとにセキュリティとパフォーマンスの特性が異なります。モードを選ぶ際は、メッセージの機密性を十分に確保できないECB(Electronic Codebook)モードを避けてください。代わりに、CCM(Counter with CBC-MAC)やGCM(Galois/Counter Mode)などを使用します。これらはいずれも認証付きモードで、データの完全性、機密性、真正性を保証します。

Javaで脆弱な暗号アルゴリズムを特定する

Javaで小規模な暗号化サービスを実装するには、javax.crypto APIを簡単に利用できます。以下は、古いDESアルゴリズムをECBモードで使用するJavaのEncryptionServiceの例です。

public class EncryptionServiceDes {

    private SecretKey secretKey;
    private Cipher cipher;

    public EncryptionServiceDes(String key) throws GeneralSecurityException {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        DESKeySpec desKeySpec = new DESKeySpec(keyBytes);
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        secretKey = keyFactory.generateSecret(desKeySpec);
        cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
    }

    public String encrypt(String original) throws GeneralSecurityException {
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        // Encrypt the original data
        byte[] encryptedData = cipher.doFinal(original.getBytes(StandardCharsets.UTF_8));
        // Encode the encrypted data in base64 for better handling
        return Base64.getEncoder().encodeToString(encryptedData);
    }
    public String decrypt(String cypher) throws GeneralSecurityException{
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        // Decode the base64-encoded ciphertext
        byte[] encryptedData = Base64.getDecoder().decode(cypher);
        // Decrypt the data
        byte[] decryptedData = cipher.doFinal(encryptedData);
        return new String(decryptedData, StandardCharsets.UTF_8);
    }
}

既知の安全でない暗号アルゴリズムをプロジェクトに組み込んだので、無料で利用できるSnykのサービスを使って、安全でない使い方を検出し、より良い解決策を見つけてみましょう。

まず、Snykの無料アカウントに登録してください。GitHub、Bitbucket、Azure AD、またはDockerのアカウントを使って、すぐに利用を始められます。

次に、GitHubリポジトリをSnykに接続しました。Snyk Codeの静的解析により、アプリケーションで「Broken or Risky Cryptographic Algorithm(破損またはリスクのある暗号アルゴリズム)」を使用しているという警告がすぐに表示されます。上記のサービス内でDESを複数回参照しているため、まさにそのとおりです。Snyk CodeエンジンはAESの使用も検討するよう推奨しており、OWASPの推奨事項とも一致しています。

セキュリティコードスキャンにより、DES/ECB/PKCS5Paddingが脆弱な暗号アルゴリズムとして検出されました。CWE-327、スコアは828です。

以下の短いコード例ではAESを使用していますが、安全とは見なされていないCBCモードを使っています。

…
    public EncryptionServiceAES(String key) throws GeneralSecurityException {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        secretKey = new SecretKeySpec(keyBytes, "AES");
        cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    }
…

Snykは、安全でないモードなども検出します。以下のスクリーンショットは、InteliJ IDEA内でコードをスキャンし、外部の修正例などの有用な情報を提供するSnykプラグインのものです。

AES/CBCに関する脆弱な暗号アルゴリズムの警告と、AES/GCMを使用したコード修正を示すSnykの脆弱性レポート

EncryptionServiceを、以下のようにAES/GCM/NoPaddingを使用するバージョンに変更すれば、以前よりはるかに優れた解決策となり、OWASPの現在の推奨事項にも沿ったものになります。

public class EncryptionServiceAESGCM {
    private SecretKey secretKey;
    private Cipher cipher;

    public EncryptionServiceAESGCM(String key) throws GeneralSecurityException {
        byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
        secretKey = new SecretKeySpec(keyBytes, "AES");
        cipher = Cipher.getInstance("AES/GCM/NoPadding");
    }

    public String encrypt(String original) throws GeneralSecurityException{
        byte[] iv = new byte[16]; // Initialization Vector
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv); // Generate a random IV
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(128, iv));
        byte[] encryptedData = cipher.doFinal(original.getBytes());
        var encoder = Base64.getEncoder();
        var encrypt64 = encoder.encode(encryptedData);
        var iv64 = encoder.encode(iv);
        return new String(encrypt64) + "#" + new String(iv64);
    }

    public String decrypt(String cypher) throws GeneralSecurityException{
        var split = cypher.split("#");
        var decoder = Base64.getDecoder();
        var cypherText = decoder.decode(split[0]);
        var iv = decoder.decode(split[1]);
        var paraSpec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, paraSpec);
        byte[] decryptedData = cipher.doFinal(cypherText);
        return new String(decryptedData);
    }
}

Javaアプリケーションの暗号アルゴリズムを継続的に見直す

古い暗号アルゴリズムを更新することは重要ですが、コードベースに新たなセキュリティ上の問題がないか継続的に監視することも同じくらい重要です。執筆時点で安全とされている暗号アルゴリズムも、数年もすれば間違いなく時代遅れになります。

そのため、問題を事前にスキャンして特定する作業を継続的に行う必要があります。対象は暗号アルゴリズムだけではなく、保守するコードに含まれるほかの問題も同様です。Snyk Codeのような静的解析セキュリティテスト(SAST)ツールを導入して活用すれば、実害が発生する前に問題を簡単に認識、特定し、軽減できます。

Java向けSnyk Codeを無料で活用

Snyk Codeは、コードベース内のセキュリティ脆弱性を特定できる最先端の静的アプリケーションセキュリティテスト(SAST)ツールです。Javaを含む複数のプログラミング言語に対応し、コードを分析して開発者にリアルタイムでフィードバックを提供します。Snyk CodeをCI/CDパイプラインに統合すれば、コミットごとにコードを自動スキャンできるため、開発サイクルの早い段階でセキュリティ上の問題を発見し、修正できます。また、Gitリポジトリへの接続、CLIを使ったコマンドラインでの利用、Java開発で広く使われているIDEとの統合も可能です。

これにより、開発ライフサイクルのあらゆる段階で、コード内の問題を早期かつ頻繁に検出できます。何より、以下からSnykに登録すれば、無料で利用できます。

Capture the Flagを始めよう

オンデマンドのバーチャル入門ワークショップを見て、Capture the Flagの課題の解き方を学びましょう。

この記事で使用したコード例はGitHubで公開しているので、ぜひ試してみてください。Java開発者向けのセキュリティに関する情報をもっと知りたい方は、以下もご覧ください。

続きを読む

blog feature snyk container custom base recommendations
Blog

Evo Agentic AppSecのご紹介:AI開発のスピードに追随するセキュリティ

AIを活用した開発は、従来のセキュリティワークフローを上回るスピードで進んでいます。SnykのAgentic AppSecは、セキュリティエージェントが脆弱性を継続的に検出、修正、検証し、人が基準を定められるようにします。

feature insights context
Blog

自律型攻撃はすでに始まっている。防御もそのスピードに追いつかなければならない。

自律型攻撃者によって、防御に使える時間は短くなっています。継続的な検出、修復、検証、予防で、セキュリティチームが攻撃に歩調を合わせる方法をご紹介します。

illustration hero ai
Blog

AIハリケーンが到来

AIはソフトウェア開発とサイバー攻撃の双方を加速させています。リーダーは、エージェントとコードを開発の初期段階から保護し、実行時に制御を徹底するとともに、防御策を独立して検証しなければなりません。