Unity SQLite:暗号化によるセキュリティ対策 – 詳細解説
Unity で SQLite データベースを利用する場合、データのセキュリティは重要な考慮事項です。特に、ローカルに保存される機密情報(ユーザーデータ、ゲーム進行状況、設定など)は、不正アクセスや改ざんから保護する必要があります。本稿では、Unity で SQLite データベースを暗号化することで、セキュリティを強化するための様々な方法を詳細に解説します。
1. はじめに:なぜ SQLite データベースの暗号化が必要なのか?
Unity ゲームは、様々なプラットフォーム(PC、モバイル、Web など)で動作します。SQLite データベースは、その軽量さと移植性の高さから、多くのゲームでローカルデータストレージとして採用されています。しかし、SQLite データベースファイルは、暗号化されていない場合、簡単にアクセスされ、内容を閲覧・編集される可能性があります。
以下に、SQLite データベースの暗号化が必要となる主な理由を挙げます。
- 不正アクセスからの保護: 暗号化によって、データベースファイルにアクセスするための鍵(パスワード)を知らないユーザーは、データを読み取ることができなくなります。これにより、個人情報やゲームの進行状況など、機密性の高い情報を保護できます。
- 改ざん防止: 暗号化されたデータベースファイルを改ざんした場合、データの整合性が損なわれ、ゲームが正常に動作しなくなる可能性があります。これにより、悪意のあるユーザーによる不正なデータ改変を防ぐことができます。
- チート対策: ゲームの進行状況や報酬などのデータを暗号化することで、チート行為を困難にすることができます。
- 知的財産保護: ゲームの設定情報やアセットデータをデータベースに保存し、暗号化することで、知的財産を保護することができます。
- コンプライアンス要件: 特定の業界や規制(例えば、個人情報保護法)では、データの暗号化が義務付けられている場合があります。
2. SQLite 暗号化の基本的な仕組み
SQLite データベースの暗号化は、データベースファイル全体または特定のテーブル・カラムを暗号化することで実現されます。暗号化されたデータベースにアクセスするには、正しい暗号化キー(パスワード)が必要です。
SQLite の暗号化には、主に以下の2つのアプローチがあります。
- SQLite 拡張機能を利用した暗号化: SQLite に暗号化機能を追加する拡張機能(例えば、SQLCipher)を利用する方法です。この方法は、比較的容易に導入でき、暗号化アルゴリズムや鍵管理をある程度制御できます。
- アプリケーション層での暗号化: SQLite データベースに格納するデータを、アプリケーション側で暗号化・復号化する方法です。この方法は、より柔軟な暗号化方式を選択でき、特定のカラムのみを暗号化するなどのカスタマイズが可能です。
3. SQLite 拡張機能を利用した暗号化:SQLCipher
SQLCipher は、SQLite データベースを暗号化するためのオープンソースの拡張機能です。AES 暗号化アルゴリズムを使用し、強力な暗号化強度を提供します。
3.1 SQLCipher の導入
Unity で SQLCipher を利用するには、まず SQLCipher のライブラリをプロジェクトにインポートする必要があります。
- SQLCipher ライブラリのダウンロード: SQLCipher の公式ウェブサイト (https://www.zetetic.net/sqlcipher/) から、必要なプラットフォームに対応したライブラリをダウンロードします。Unity で使用できるライブラリは、通常、C/C++ で記述されています。
- Unity プロジェクトへのインポート: ダウンロードしたライブラリを、Unity プロジェクトの Plugins フォルダに配置します。Plugins フォルダ内に、プラットフォーム別のフォルダ(例えば、Plugins/Android/libs/armeabi-v7a など)を作成し、対応するライブラリを配置する必要があります。
- C# スクリプトの作成: SQLCipher を操作するための C# スクリプトを作成します。このスクリプトでは、SQLite データベースへの接続、暗号化キーの設定、クエリの実行などの処理を行います。
3.2 C# スクリプトの実装例
以下に、SQLCipher を利用してデータベースを暗号化し、データを操作する C# スクリプトの例を示します。
“`csharp
using System;
using System.Data;
using Mono.Data.Sqlite;
using UnityEngine;
public class SQLiteEncryptionExample : MonoBehaviour
{
private string dbPath;
private string dbPassword = “YOUR_PASSWORD”; // 強固なパスワードに変更してください!
void Start()
{
dbPath = Application.persistentDataPath + "/encrypted_database.db";
// データベースの初期化と暗号化
InitializeDatabase();
// データの挿入
InsertData("John Doe", 30);
// データの取得
RetrieveData();
}
private void InitializeDatabase()
{
// データベースが存在しない場合は作成し、暗号化する
if (!System.IO.File.Exists(dbPath))
{
SqliteConnection.CreateFile(dbPath);
using (var connection = new SqliteConnection("Data Source=" + dbPath))
{
connection.Open();
// 暗号化キーの設定
using (var command = connection.CreateCommand())
{
command.CommandText = "PRAGMA key = '" + dbPassword + "'";
command.ExecuteNonQuery();
}
// テーブルの作成
using (var command = connection.CreateCommand())
{
command.CommandText = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)";
command.ExecuteNonQuery();
}
connection.Close();
}
}
}
private void InsertData(string name, int age)
{
using (var connection = new SqliteConnection("Data Source=" + dbPath))
{
connection.Open();
// 暗号化キーの設定
using (var command = connection.CreateCommand())
{
command.CommandText = "PRAGMA key = '" + dbPassword + "'";
command.ExecuteNonQuery();
}
// データの挿入
using (var command = connection.CreateCommand())
{
command.CommandText = "INSERT INTO users (name, age) VALUES ('" + name + "', " + age + ")";
command.ExecuteNonQuery();
}
connection.Close();
}
}
private void RetrieveData()
{
using (var connection = new SqliteConnection("Data Source=" + dbPath))
{
connection.Open();
// 暗号化キーの設定
using (var command = connection.CreateCommand())
{
command.CommandText = "PRAGMA key = '" + dbPassword + "'";
command.ExecuteNonQuery();
}
// データの取得
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM users";
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
int id = reader.GetInt32(0);
string name = reader.GetString(1);
int age = reader.GetInt32(2);
Debug.Log("ID: " + id + ", Name: " + name + ", Age: " + age);
}
}
}
connection.Close();
}
}
}
“`
スクリプトの説明:
dbPath
: データベースファイルのパスを定義します。Application.persistentDataPath
は、プラットフォームによって異なるデータの保存場所を提供します。dbPassword
: データベースを暗号化するためのパスワードを定義します。必ず、安全なパスワードに変更してください。InitializeDatabase()
: データベースが存在しない場合は作成し、PRAGMA key
コマンドを使用して暗号化キーを設定します。その後、CREATE TABLE
コマンドを使用してテーブルを作成します。InsertData()
:PRAGMA key
コマンドを使用して暗号化キーを設定した後、INSERT INTO
コマンドを使用してデータを挿入します。RetrieveData()
:PRAGMA key
コマンドを使用して暗号化キーを設定した後、SELECT * FROM users
コマンドを使用してデータを取得し、コンソールに表示します。
注意点:
- パスワードの管理: パスワードは、ソースコードに直接埋め込むのではなく、安全な場所に保存し、実行時に取得するようにしてください。環境変数や設定ファイルなどが考えられます。
- エラー処理: データベースへの接続やクエリの実行時にエラーが発生する可能性があるため、適切なエラー処理を追加してください。
- トランザクション: 複数のクエリをまとめて実行する場合は、トランザクションを使用することで、データの整合性を確保できます。
3.3 SQLCipher の利点と欠点
利点:
- 強力な暗号化: AES 暗号化アルゴリズムを使用しており、高いセキュリティ強度を提供します。
- 比較的容易な導入: SQLite 拡張機能として提供されており、比較的容易に導入できます。
- オープンソース: オープンソースであるため、自由に利用できます。
欠点:
- プラットフォーム依存: 各プラットフォームに対応したライブラリを別途用意する必要があります。
- パフォーマンス: 暗号化・復号化処理にはオーバーヘッドが発生するため、パフォーマンスに影響を与える可能性があります。
- 追加ライブラリの管理: SQLCipher のライブラリを別途管理する必要があります。
4. アプリケーション層での暗号化
SQLite 拡張機能を使用する代わりに、アプリケーション層でデータを暗号化・復号化することも可能です。この方法では、暗号化アルゴリズムや鍵管理をより柔軟に制御できます。
4.1 暗号化アルゴリズムの選択
アプリケーション層での暗号化では、様々な暗号化アルゴリズムを選択できます。代表的な暗号化アルゴリズムには、以下のようなものがあります。
- AES (Advanced Encryption Standard): 強力な暗号化強度を持ち、広く利用されている暗号化アルゴリズムです。
- DES (Data Encryption Standard): 古い暗号化アルゴリズムですが、比較的処理が高速です。
- Triple DES (3DES): DES を3回繰り返すことで、セキュリティ強度を高めた暗号化アルゴリズムです。
- RC4: ストリーム暗号であり、高速な処理が可能です。
どの暗号化アルゴリズムを選択するかは、セキュリティ要件とパフォーマンス要件に基づいて決定する必要があります。一般的には、AES が推奨されます。
4.2 鍵管理
暗号化アルゴリズムと同様に、鍵管理も重要な考慮事項です。安全な鍵管理システムを構築することで、暗号化されたデータのセキュリティを確保できます。
鍵管理には、主に以下の2つのアプローチがあります。
- 対称鍵暗号: 暗号化と復号化に同じ鍵を使用します。鍵の管理が容易ですが、鍵が漏洩した場合、すべてのデータが危険にさらされる可能性があります。
- 非対称鍵暗号: 暗号化と復号化に異なる鍵(公開鍵と秘密鍵)を使用します。秘密鍵は安全な場所に保管し、公開鍵は自由に配布できます。鍵の管理が複雑になりますが、セキュリティ強度は高くなります。
どちらのアプローチを選択するかは、セキュリティ要件と管理の複雑さに基づいて決定する必要があります。
4.3 C# スクリプトの実装例
以下に、AES 暗号化アルゴリズムを使用して、データベースに格納するデータを暗号化・復号化する C# スクリプトの例を示します。
“`csharp
using System;
using System.Data;
using Mono.Data.Sqlite;
using UnityEngine;
using System.Security.Cryptography;
using System.Text;
public class SQLiteEncryptionExampleApp : MonoBehaviour
{
private string dbPath;
private string encryptionKey = “YOUR_ENCRYPTION_KEY”; // 強固な暗号化キーに変更してください!
void Start()
{
dbPath = Application.persistentDataPath + "/encrypted_database_app.db";
// データベースの初期化
InitializeDatabase();
// データの挿入 (暗号化)
InsertData("Alice Smith", 25);
// データの取得 (復号化)
RetrieveData();
}
private void InitializeDatabase()
{
// データベースが存在しない場合は作成する
if (!System.IO.File.Exists(dbPath))
{
SqliteConnection.CreateFile(dbPath);
using (var connection = new SqliteConnection("Data Source=" + dbPath))
{
connection.Open();
// テーブルの作成
using (var command = connection.CreateCommand())
{
command.CommandText = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age TEXT)"; // ageをTEXT型に変更
command.ExecuteNonQuery();
}
connection.Close();
}
}
}
private void InsertData(string name, int age)
{
using (var connection = new SqliteConnection("Data Source=" + dbPath))
{
connection.Open();
// データの暗号化
string encryptedName = EncryptString(name, encryptionKey);
string encryptedAge = EncryptString(age.ToString(), encryptionKey); // ageを文字列に変換して暗号化
// データの挿入
using (var command = connection.CreateCommand())
{
command.CommandText = "INSERT INTO users (name, age) VALUES ('" + encryptedName + "', '" + encryptedAge + "')";
command.ExecuteNonQuery();
}
connection.Close();
}
}
private void RetrieveData()
{
using (var connection = new SqliteConnection("Data Source=" + dbPath))
{
connection.Open();
// データの取得
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM users";
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
int id = reader.GetInt32(0);
string encryptedName = reader.GetString(1);
string encryptedAge = reader.GetString(2);
// データの復号化
string name = DecryptString(encryptedName, encryptionKey);
string age = DecryptString(encryptedAge, encryptionKey);
Debug.Log("ID: " + id + ", Name: " + name + ", Age: " + age);
}
}
}
connection.Close();
}
}
// AES 暗号化
public static string EncryptString(string plainText, string key)
{
byte[] iv = new byte[16];
byte[] array;
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream))
{
streamWriter.Write(plainText);
}
array = memoryStream.ToArray();
}
}
}
return Convert.ToBase64String(array);
}
// AES 復号化
public static string DecryptString(string cipherText, string key)
{
byte[] iv = new byte[16];
byte[] buffer = Convert.FromBase64String(cipherText);
using (Aes aes = Aes.Create())
{
aes.Key = Encoding.UTF8.GetBytes(key);
aes.IV = iv;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, decryptor, CryptoStreamMode.Read))
{
using (StreamReader streamReader = new StreamReader((Stream)cryptoStream))
{
return streamReader.ReadToEnd();
}
}
}
}
}
}
“`
スクリプトの説明:
encryptionKey
: データを暗号化・復号化するための鍵を定義します。必ず、安全な暗号化キーに変更してください。EncryptString()
: AES 暗号化アルゴリズムを使用して、文字列を暗号化します。DecryptString()
: AES 暗号化アルゴリズムを使用して、暗号化された文字列を復号化します。InsertData()
:EncryptString()
関数を使用してデータを暗号化し、データベースに挿入します。RetrieveData()
: データベースからデータを取得し、DecryptString()
関数を使用して復号化します。
注意点:
- 暗号化キーの管理: 暗号化キーは、ソースコードに直接埋め込むのではなく、安全な場所に保存し、実行時に取得するようにしてください。
- エラー処理: 暗号化・復号化処理時にエラーが発生する可能性があるため、適切なエラー処理を追加してください。
- パフォーマンス: 暗号化・復号化処理にはオーバーヘッドが発生するため、パフォーマンスに影響を与える可能性があります。
4.4 アプリケーション層での暗号化の利点と欠点
利点:
- 柔軟性: 様々な暗号化アルゴリズムを選択でき、暗号化方式をカスタマイズできます。
- 部分的な暗号化: 特定のテーブルやカラムのみを暗号化できます。
- 鍵管理の自由度: 鍵管理システムを自由に構築できます。
欠点:
- 複雑性: 暗号化・復号化処理を実装する必要があるため、開発が複雑になります。
- パフォーマンス: 暗号化・復号化処理にはオーバーヘッドが発生するため、パフォーマンスに影響を与える可能性があります。
- セキュリティ: セキュリティ対策を適切に行わないと、暗号化キーが漏洩する可能性があります。
5. 暗号化以外のセキュリティ対策
SQLite データベースの暗号化は、セキュリティ対策の1つに過ぎません。より強固なセキュリティを確保するためには、以下の対策も併せて実施する必要があります。
- 入力検証: データベースに挿入するデータは、必ず入力検証を行い、不正なデータが挿入されないようにしてください。
- SQL インジェクション対策: SQL インジェクション攻撃を防ぐために、プレースホルダを使用してください。
- 権限管理: データベースへのアクセス権限を適切に管理し、必要最小限の権限のみを付与するようにしてください。
- バックアップ: 定期的にデータベースのバックアップを作成し、万が一の事態に備えてください。
- ログ: データベースへのアクセスログを記録し、不正アクセスを検知できるようにしてください。
6. まとめ:Unity SQLite 暗号化によるセキュリティ対策
本稿では、Unity で SQLite データベースを暗号化することで、セキュリティを強化するための様々な方法を詳細に解説しました。
- SQLite 拡張機能を利用した暗号化 (SQLCipher): 強力な暗号化強度を持ち、比較的容易に導入できます。
- アプリケーション層での暗号化: 柔軟な暗号化方式を選択でき、特定のカラムのみを暗号化するなどのカスタマイズが可能です。
どちらの方法を選択するかは、セキュリティ要件とパフォーマンス要件に基づいて決定する必要があります。
また、暗号化以外にも、入力検証、SQL インジェクション対策、権限管理、バックアップ、ログなどのセキュリティ対策を併せて実施することで、より強固なセキュリティを確保できます。
本稿が、Unity で SQLite データベースを利用する際のセキュリティ対策の参考になれば幸いです。