JavaScriptを使用してウェブアプリケーションを開発する際、データの保存方法の一つとしてローカルストレージが広く利用されています。ローカルストレージは、ブラウザにデータを永続的に保存できる便利な機能ですが、その利便性の反面、セキュリティリスクも伴います。保存されたデータは、暗号化されていない限り、悪意のある第三者によって簡単にアクセスされる可能性があります。特に、ユーザーの個人情報や機密データを扱うアプリケーションにおいては、これらのデータが悪用されるリスクを最小限に抑えるために、適切な暗号化技術とセキュリティ対策を実装することが不可欠です。本記事では、JavaScriptでローカルストレージを安全に使用するための方法を解説し、暗号化技術やセキュリティ強化のためのベストプラクティスを紹介します。
ローカルストレージとは何か
ローカルストレージは、ウェブブラウザにデータを保存するための仕組みであり、ユーザーがページを再読み込みしたりブラウザを閉じたりしてもデータが保持される永続的なストレージです。通常、キーと値のペアでデータを保存し、最大5MB程度のデータを保存できるため、小規模なデータを保持するのに適しています。これにより、ユーザーの設定情報や、簡単なデータキャッシング、セッション情報などをサーバーに依存せずにローカルに保存することが可能になります。ローカルストレージは、セッションストレージと異なり、ブラウザを閉じてもデータが保持されるという特性を持ち、特にシングルページアプリケーションやオフライン対応のアプリケーションにおいて、その有用性が高いと言えます。
ローカルストレージのセキュリティリスク
ローカルストレージは便利な機能である一方で、セキュリティ面でのリスクが存在します。最大のリスクは、保存されたデータが暗号化されていない場合、悪意のある攻撃者が容易にアクセスできるという点です。ローカルストレージに保存されるデータは、JavaScriptコードやブラウザの開発者ツールを通じて簡単に閲覧、編集が可能です。これにより、ユーザーの個人情報や認証トークン、その他の機密データが盗まれる危険性が高まります。
また、クロスサイトスクリプティング(XSS)攻撃により、悪意のあるスクリプトがウェブページに挿入され、ローカルストレージのデータに不正アクセスする可能性もあります。さらに、ローカルストレージに保存されたデータは、同じドメインで動作するすべてのページからアクセス可能であるため、同一オリジンポリシーを悪用した攻撃によってデータが漏洩するリスクも考慮しなければなりません。
これらのリスクを理解し、適切な対策を講じることが、ローカルストレージを安全に利用するための第一歩となります。
暗号化の必要性
ローカルストレージに保存されるデータは、非常にアクセスしやすい場所にあるため、暗号化が重要なセキュリティ対策となります。暗号化とは、平文のデータを変換して、特定の鍵を持つ者だけがそのデータを元に戻せるようにする技術です。これにより、データが盗まれたり、第三者に不正にアクセスされた場合でも、暗号化されていない限りその内容を解読することができません。
特に、認証情報、APIトークン、個人情報などの機密データをローカルストレージに保存する場合、これらの情報が平文のまま保存されていると、セキュリティ侵害が発生した際に甚大な被害を招く恐れがあります。暗号化を施すことで、万が一不正アクセスがあった場合でも、データの内容が保護され、悪用を防ぐことが可能です。
さらに、暗号化はセキュリティの向上だけでなく、コンプライアンスの観点からも重要です。多くのプライバシー保護規制では、ユーザーの個人情報を適切に保護することが求められており、暗号化はその一環として非常に有効です。データを安全に保つために、ローカルストレージへの保存前に必ず暗号化を行うことが推奨されます。
JavaScriptでのデータ暗号化方法
JavaScriptでローカルストレージに保存するデータを暗号化するためには、暗号化アルゴリズムを使用してデータを安全に変換する必要があります。以下では、一般的な暗号化手法とその実装例を紹介します。
CryptoJSを使った暗号化
JavaScriptで手軽に暗号化を実装するために、CryptoJSというライブラリが広く使われています。このライブラリは、AES(Advanced Encryption Standard)などの強力な暗号化アルゴリズムをサポートしており、簡単にデータの暗号化と復号化が行えます。
AESによるデータの暗号化
以下は、CryptoJSを使ってローカルストレージに保存するデータをAESで暗号化するコード例です。
// ライブラリの読み込み(CDNを使用)
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<script>
// 暗号化するデータとキー
const data = "ユーザーの機密情報";
const key = "シークレットキー";
// AESでデータを暗号化
const encryptedData = CryptoJS.AES.encrypt(data, key).toString();
// 暗号化されたデータをローカルストレージに保存
localStorage.setItem("encryptedData", encryptedData);
</script>
このコードでは、data
に格納された機密情報がkey
を使ってAESで暗号化され、その暗号化された文字列がローカルストレージに保存されます。
暗号化されたデータの復号化
保存されたデータを再利用する際には、復号化が必要です。以下に、暗号化されたデータを復号化する方法を示します。
// ローカルストレージから暗号化されたデータを取得
const encryptedData = localStorage.getItem("encryptedData");
// AESでデータを復号化
const decryptedData = CryptoJS.AES.decrypt(encryptedData, key).toString(CryptoJS.enc.Utf8);
// 復号化されたデータを表示
console.log(decryptedData); // "ユーザーの機密情報"
このコードを使用することで、暗号化されたデータを元の平文に戻すことができます。復号化には暗号化時と同じキーが必要であり、これによりデータのセキュリティが保たれます。
Web Crypto APIを使った暗号化
最新のブラウザ環境では、Web Crypto APIを使用することでネイティブな暗号化処理を行うことも可能です。Web Crypto APIは標準化された暗号化ライブラリで、より高いセキュリティとパフォーマンスを提供します。
以下は、Web Crypto APIを使った暗号化の基本例です。
// AESキーの生成
async function generateKey() {
return await crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256,
},
true,
["encrypt", "decrypt"]
);
}
// データの暗号化
async function encryptData(key, data) {
const encodedData = new TextEncoder().encode(data);
const iv = crypto.getRandomValues(new Uint8Array(12)); // 初期化ベクター
const encryptedData = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv,
},
key,
encodedData
);
return { iv, encryptedData };
}
// データの復号化
async function decryptData(key, encryptedData, iv) {
const decryptedData = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv,
},
key,
encryptedData
);
return new TextDecoder().decode(decryptedData);
}
Web Crypto APIを使うことで、より強力かつ効率的な暗号化が可能です。ただし、非同期処理を伴うため、ブラウザの互換性やパフォーマンスを考慮しながら使用する必要があります。
これらの方法を使用することで、ローカルストレージに保存されるデータを安全に保護し、セキュリティリスクを大幅に低減することができます。
セキュリティ強化のための追加対策
暗号化はローカルストレージのデータ保護において重要な役割を果たしますが、これだけでは完全なセキュリティを確保できません。暗号化に加えて、さらなるセキュリティ強化のための対策を講じることが重要です。以下に、追加のセキュリティ対策をいくつか紹介します。
1. セキュアな鍵管理
暗号化で使用する鍵(キー)は、最も機密性が高く、適切に管理されるべき要素です。暗号化鍵が漏洩すれば、暗号化されたデータも簡単に復号されてしまいます。以下のような方法で鍵を安全に管理しましょう。
- 環境変数の利用:暗号化キーをコード内にハードコーディングするのは避け、環境変数を使用して管理します。
- サーバーサイドでのキー管理:暗号化キーはできるだけサーバーサイドで管理し、フロントエンドに直接渡さないようにします。必要に応じて、APIを通じて暗号化・復号化の処理をサーバー側で行うことも検討してください。
2. セッションタイムアウトとデータの自動削除
長時間アクセスがない場合に、ローカルストレージのデータを自動的に削除する機能を実装すると、セキュリティリスクを低減できます。これにより、セッションが終了した後もデータが保持され続けることを防ぎます。
// ローカルストレージにデータを保存するときに、タイムスタンプも一緒に保存
localStorage.setItem("data", encryptedData);
localStorage.setItem("timestamp", Date.now());
// データを取得するときに、時間をチェックして期限切れかどうかを確認
const savedTimestamp = localStorage.getItem("timestamp");
const currentTime = Date.now();
const timeout = 60 * 60 * 1000; // 1時間
if (currentTime - savedTimestamp > timeout) {
// 期限切れの場合、データを削除
localStorage.removeItem("data");
localStorage.removeItem("timestamp");
}
3. クロスサイトスクリプティング(XSS)対策
XSS攻撃は、悪意のあるスクリプトがウェブページに挿入されることで発生し、ローカルストレージ内のデータに不正アクセスされる可能性があります。XSS対策として、以下の方法が効果的です。
- コンテンツセキュリティポリシー(CSP)の設定:ウェブサーバーのヘッダーにCSPを設定し、スクリプトの実行元を制限します。
- 入力の検証とエスケープ:ユーザーからの入力データは必ずサニタイズし、不正なスクリプトが混入しないようにします。
4. 定期的なセキュリティテスト
アプリケーションのセキュリティは、定期的に見直し、テストする必要があります。セキュリティツールを使用して脆弱性を検出し、潜在的なリスクを事前に特定して対処します。ペネトレーションテストやコードレビューも効果的な手段です。
5. ユーザー認証と認可の強化
ローカルストレージに保存されたデータが適切に保護されるよう、ユーザー認証と認可のプロセスを強化します。強力なパスワードポリシー、多要素認証(MFA)、ユーザーロールに基づいたアクセス制御などを実装することで、データの不正アクセスを防ぎます。
これらの追加対策を組み合わせることで、暗号化されたローカルストレージデータの安全性をさらに向上させることができます。暗号化とこれらのセキュリティ対策を併用することで、より安全なウェブアプリケーションを構築することが可能となります。
実際の使用例とベストプラクティス
暗号化とセキュリティ対策を施したローカルストレージの使用は、実際のウェブアプリケーションにおいてどのように実践されるべきかが重要です。ここでは、具体的な使用例と、それを実現するためのベストプラクティスを紹介します。
ユーザー設定の保存と暗号化
たとえば、ユーザーのダークモード設定や言語設定などの個人設定をローカルストレージに保存する場合、これらの情報が他者に覗かれることを防ぐために暗号化を施します。以下は、その実装例です。
// 暗号化キーの生成
const key = "user-settings-key";
// ユーザー設定の保存
function saveUserSettings(settings) {
const encryptedSettings = CryptoJS.AES.encrypt(JSON.stringify(settings), key).toString();
localStorage.setItem("userSettings", encryptedSettings);
}
// ユーザー設定の取得
function loadUserSettings() {
const encryptedSettings = localStorage.getItem("userSettings");
if (encryptedSettings) {
const decryptedSettings = CryptoJS.AES.decrypt(encryptedSettings, key).toString(CryptoJS.enc.Utf8);
return JSON.parse(decryptedSettings);
}
return null;
}
// 例:設定の保存と取得
saveUserSettings({ theme: "dark", language: "ja" });
const settings = loadUserSettings();
console.log(settings); // { theme: "dark", language: "ja" }
この例では、ユーザー設定が暗号化されてローカルストレージに保存されるため、悪意のあるユーザーによる直接アクセスを防ぐことができます。
セキュリティトークンの管理
セキュリティトークン(例:JWT)の保存は、特に慎重に行う必要があります。トークンが盗まれると、ユーザーになりすますことが可能になるため、トークンを暗号化して保存することが重要です。また、トークンの有効期限やセッション管理にも注意が必要です。
// トークンの保存
function saveToken(token) {
const encryptedToken = CryptoJS.AES.encrypt(token, key).toString();
localStorage.setItem("authToken", encryptedToken);
}
// トークンの取得
function loadToken() {
const encryptedToken = localStorage.getItem("authToken");
if (encryptedToken) {
const decryptedToken = CryptoJS.AES.decrypt(encryptedToken, key).toString(CryptoJS.enc.Utf8);
return decryptedToken;
}
return null;
}
// 例:トークンの保存と取得
saveToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");
const token = loadToken();
console.log(token); // "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
ベストプラクティス
- 必要最小限のデータ保存:ローカルストレージには必要最小限のデータだけを保存し、重要なデータはサーバーサイドで管理するようにします。
- 短期間でのデータ保持:データの保存期間はできるだけ短くし、不要になったデータは速やかに削除します。
- ユーザーセッションの適切な管理:セッション管理はサーバーサイドで行い、セッションタイムアウト時に自動的にローカルストレージのデータも削除するようにします。
- 開発者ツールによるアクセスの制限:可能であれば、ブラウザの開発者ツールでローカルストレージにアクセスできる範囲を制限し、重要なデータの操作を防ぎます。
- 定期的なセキュリティ監査:アプリケーションのセキュリティは常に見直し、定期的にセキュリティ監査を実施することで、潜在的な脆弱性を早期に発見し修正します。
これらのベストプラクティスを適用することで、暗号化されたデータの保護をさらに強化し、ローカルストレージの安全な運用を実現することができます。
暗号化ライブラリの紹介
JavaScriptでの暗号化を効率的に行うためには、信頼性の高い暗号化ライブラリを使用することが推奨されます。暗号化ライブラリは、セキュリティの高い暗号化アルゴリズムを簡単に実装できるように設計されており、開発者が独自の暗号化ロジックを構築するリスクを避けることができます。ここでは、代表的な暗号化ライブラリをいくつか紹介します。
1. CryptoJS
CryptoJSは、JavaScriptでの暗号化に最も広く使用されているライブラリの一つです。AES、DES、RC4、SHA-1、SHA-256など、多様な暗号化アルゴリズムをサポートしています。シンプルなAPIで簡単に暗号化と復号化を実装でき、特にAES(Advanced Encryption Standard)を利用した暗号化が人気です。
特徴:
- 使いやすいAPI
- 幅広い暗号化アルゴリズムのサポート
- シンプルなコードで実装可能
例:
const encrypted = CryptoJS.AES.encrypt("データ", "シークレットキー").toString();
const decrypted = CryptoJS.AES.decrypt(encrypted, "シークレットキー").toString(CryptoJS.enc.Utf8);
2. Web Crypto API
Web Crypto APIは、ブラウザに組み込まれているネイティブの暗号化APIです。これにより、高いパフォーマンスとセキュリティを提供し、標準化された方法で暗号化を行うことができます。特に、サーバーレベルのセキュリティを要求する場合に適しており、AES-GCMなどの高度な暗号化モードをサポートしています。
特徴:
- ブラウザネイティブで動作
- 高パフォーマンスとセキュリティ
- 非同期処理をサポート
例:
async function encryptData(key, data) {
const encodedData = new TextEncoder().encode(data);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encryptedData = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv: iv },
key,
encodedData
);
return { iv, encryptedData };
}
3. SJCL(Stanford JavaScript Crypto Library)
SJCLは、スタンフォード大学が開発した暗号化ライブラリで、軽量かつセキュリティに重点を置いて設計されています。AESやHMAC、PBKDF2などの暗号化メソッドをサポートし、セキュアな暗号化操作をブラウザ上で実行できます。特に、サイズが小さいため、モバイルアプリケーションにも適しています。
特徴:
- 軽量でモバイルフレンドリー
- セキュリティ重視の設計
- 豊富な暗号化機能
例:
const encrypted = sjcl.encrypt("シークレットキー", "データ");
const decrypted = sjcl.decrypt("シークレットキー", encrypted);
4. Forge
Forgeは、広範な機能を持つJavaScriptの暗号化ライブラリで、暗号化だけでなく、SSL/TLS通信、RSA暗号化、証明書の生成と検証など、セキュリティ関連の機能を包括的にサポートしています。より高度なセキュリティ要件を持つアプリケーションに適しています。
特徴:
- SSL/TLSなどの高度なセキュリティ機能
- RSAなどの公開鍵暗号化サポート
- 全面的なセキュリティソリューション
例:
const md = forge.md.sha256.create();
md.update('データ', 'utf8');
const hash = md.digest().toHex();
ライブラリ選定のポイント
暗号化ライブラリを選定する際には、以下のポイントを考慮することが重要です。
- セキュリティ要件: アプリケーションが必要とするセキュリティレベルに応じて、適切な暗号化アルゴリズムとライブラリを選定します。
- パフォーマンス: デバイスや環境に応じたパフォーマンスを考慮し、軽量で高速なライブラリを選びます。
- 互換性: ターゲットブラウザや環境に対応しているか確認します。特に、モバイルデバイスでの動作を考慮する場合は、ライブラリのサイズも重要です。
- メンテナンスとサポート: 定期的にメンテナンスされ、コミュニティや開発者からのサポートが充実しているライブラリを選ぶと良いでしょう。
これらの暗号化ライブラリを活用することで、JavaScriptでのデータ暗号化を効果的に実装し、アプリケーションのセキュリティを強化することができます。
セキュリティテストと評価
暗号化とセキュリティ対策を施したアプリケーションは、実装後に適切なセキュリティテストを行い、その有効性を評価する必要があります。セキュリティテストは、潜在的な脆弱性を発見し、対策の効果を確認するための重要なプロセスです。ここでは、セキュリティテストの基本的な方法と評価基準について解説します。
1. ペネトレーションテスト(Penetration Testing)
ペネトレーションテストは、実際に攻撃をシミュレートしてシステムの脆弱性を検出する手法です。専門のセキュリティチームまたはツールを使用して、外部からの攻撃に対する防御力をテストします。ローカルストレージの暗号化に関しては、以下のポイントをテストします。
- 暗号化データの解析: 保存されたデータが正しく暗号化されているか、暗号化の強度を確認します。
- 鍵管理の脆弱性: 暗号化鍵が安全に管理されているか、鍵漏洩のリスクをテストします。
- XSS攻撃の脆弱性: クロスサイトスクリプティング攻撃によって、ローカルストレージ内のデータに不正アクセスが可能かどうかをテストします。
2. セキュリティスキャンと脆弱性診断
セキュリティスキャンは、自動化されたツールを使用して、システム全体をスキャンし、既知の脆弱性やセキュリティリスクを検出する方法です。これにより、コードや設定に潜む問題を素早く特定できます。
- 静的解析ツール: ソースコードを分析して、暗号化の実装ミスやセキュリティホールを検出します。
- 動的解析ツール: 実行中のアプリケーションを解析し、リアルタイムの脆弱性を検出します。これには、暗号化されたデータの取り扱いに関するチェックも含まれます。
3. ログレビューと監査
セキュリティテストの一環として、アプリケーションのログを定期的にレビューし、異常なアクセスや挙動がないかを確認します。特に、ローカルストレージに保存されたデータに対するアクセスや操作履歴は、監査の対象となるべき重要な情報です。
- アクセスログの監視: ローカルストレージにアクセスする操作が正常かどうか、異常なパターンがないかを監視します。
- 異常検出のトリガー設定: 特定の条件(例:頻繁なデータ復号化試行)が発生した場合に警告を発するように設定します。
4. セキュリティ評価基準
セキュリティテストの結果は、事前に定めた評価基準に基づいて判断します。評価基準は、アプリケーションの特性や業界標準に応じて設定されます。
- 暗号化の強度: 使用している暗号化アルゴリズムが業界標準に準拠しているか、キーの長さや暗号化モードが適切かを評価します。
- データ保護の確実性: ローカルストレージに保存されたデータが適切に保護されているか、漏洩リスクが最小化されているかを評価します。
- リスク管理の徹底度: 脆弱性が発見された場合、その対応策が迅速に実行されているか、リスク管理プロセスが徹底されているかを確認します。
5. 継続的なテストとモニタリング
セキュリティは一度テストして終わりではなく、継続的なモニタリングとテストが求められます。セキュリティ環境は日々変化しており、新たな脅威が出現する可能性があるため、定期的なテストと更新が不可欠です。
- 定期的なセキュリティテストの実施: 定期的にペネトレーションテストや脆弱性診断を実施し、セキュリティ状態を維持します。
- リアルタイムモニタリング: 監視ツールを使用して、アプリケーションのセキュリティ状態をリアルタイムでモニタリングします。
これらのテストと評価を通じて、アプリケーションのセキュリティが適切に確保されていることを確認し、ユーザーデータの保護を徹底することが可能になります。
よくある問題とその対処法
ローカルストレージの暗号化とセキュリティ対策を実装する際には、さまざまな問題が発生する可能性があります。これらの問題に対処するための方法を事前に理解しておくことで、トラブルシューティングを迅速に行い、アプリケーションの信頼性を維持することができます。以下に、よくある問題とその対処法を紹介します。
1. 暗号化キーの管理ミス
問題: 暗号化キーが不適切に管理されていると、キーが漏洩した場合にデータの安全性が損なわれます。例えば、キーがコード内にハードコーディングされていると、ソースコードが外部に流出した際に大きなリスクとなります。
対処法:
- キーの安全な保管: 暗号化キーは、環境変数やセキュアなキー管理システム(KMS)を使用して安全に管理します。
- 定期的なキーのローテーション: セキュリティを強化するため、暗号化キーは定期的に変更し、古いキーは廃棄します。
- サーバーサイドでのキー処理: 可能な限り、暗号化キーの生成や管理をサーバーサイドで行い、クライアント側にはキーを渡さないようにします。
2. 暗号化のパフォーマンス低下
問題: 大量のデータを暗号化する際、暗号化処理がボトルネックとなり、アプリケーションのパフォーマンスが低下することがあります。
対処法:
- 効率的なアルゴリズムの選択: AES-GCMなどの効率的な暗号化アルゴリズムを使用し、暗号化処理の負荷を軽減します。
- 非同期処理の活用: 暗号化処理を非同期で実行し、メインスレッドのパフォーマンスへの影響を最小限に抑えます。
- データサイズの最適化: 暗号化するデータのサイズを最小限に抑え、必要な部分のみを暗号化することで処理時間を短縮します。
3. 復号化時のデータ破損
問題: 暗号化されたデータが復号化される際、キーの不一致やデータの損傷により、正しく復号できない場合があります。これにより、ユーザーは正しいデータにアクセスできなくなることがあります。
対処法:
- 復号化前のデータ検証: 復号化の前に、データが完全かつ正確に取得されていることを検証します。例えば、データのハッシュ値をチェックして整合性を確認します。
- エラーハンドリングの強化: 復号化に失敗した場合に適切なエラーメッセージを表示し、ユーザーに再試行を促すなどのエラーハンドリングを実装します。
- 暗号化プロセスのログ記録: 暗号化および復号化の各ステップでログを記録し、問題が発生した際にその原因を迅速に特定できるようにします。
4. ユーザーが複数デバイスを使用する場合のデータ同期問題
問題: ユーザーが複数のデバイスを使用している場合、ローカルストレージのデータがデバイス間で同期されないことがあります。これにより、一部のデバイスで古いデータが表示されることがあります。
対処法:
- クラウド同期の利用: ローカルストレージとクラウドストレージを組み合わせて使用し、デバイス間でデータの一貫性を保つようにします。
- 定期的なデータの更新チェック: ローカルストレージのデータが古くなった場合に自動的に更新されるように、バックグラウンドで定期的にデータの最新状態をチェックします。
5. XSS攻撃によるデータ流出
問題: クロスサイトスクリプティング(XSS)攻撃により、ローカルストレージに保存されたデータが不正にアクセスされるリスクがあります。攻撃者が悪意のあるスクリプトを挿入し、保存されたデータを盗み出す可能性があります。
対処法:
- 入力データのサニタイズ: ユーザーからの入力データを徹底的にサニタイズし、不正なスクリプトが混入しないようにします。
- コンテンツセキュリティポリシー(CSP)の導入: CSPを設定し、信頼できるスクリプトのみが実行されるように制限します。
- 定期的なセキュリティレビュー: アプリケーションのセキュリティ設定を定期的にレビューし、XSS攻撃の脆弱性をチェックします。
これらの問題とその対処法を理解し、適切な対応を行うことで、ローカルストレージのセキュリティを強化し、アプリケーションの信頼性を向上させることができます。
まとめ
本記事では、JavaScriptでローカルストレージを安全に利用するための暗号化技術とセキュリティ対策について詳しく解説しました。ローカルストレージは便利な機能ですが、適切なセキュリティ対策が施されていない場合、データの漏洩や不正アクセスのリスクが高まります。暗号化によるデータ保護、キー管理の徹底、追加のセキュリティ対策、そして定期的なセキュリティテストが、ローカルストレージを安全に運用するために不可欠です。これらの手法を適用することで、ユーザーの信頼を得る安全なウェブアプリケーションを構築することができます。
コメント