オンライン登録でプラグインのロックを解除する
認証されるまでアクセスをロックすることで、アプリやプラグインのセキュリティを向上させましょう。オンラインでキーを登録することで、プラグインのロックを解除する仕組みをユーザーに提供する方法をご紹介します。
レベル:上級
プラットフォーム:Windows, macOS, Linux
クラス: OnlineUnlockStatus,OnlineUnlockForm,KeyGeneration,RSAKey,TracktionMarketplaceStatus
このチュートリアルでは、RSA暗号、証明書、ネットワークプロトコル、PHP構文についての初歩的な知識を前提としています。
スタート
このチュートリアルのデモ・プロジェクトのダウンロードはこちらから:PIP|ZIP.プロジェクトを解凍し、最初のヘッダーファイルをProjucerで開く。
このステップでヘルプが必要な場合は、以下を参照してください。Tutorial: Projucer Part 1: Getting started with the Projucer.
デモ・プロジェクト
デモ・プロジェクトは、2つのボタンを持つ非常にシンプルなUIを示しており、そのうちの1つは、登録によってもう1つへのアクセスをロック解除するために使用されます。Unlock "ボタンが押されると、ユーザーが認証情報を入力するための登録フォームが開きます。現時点では、バックエンドサーバーの実装を開始するまで、登録プロセスは失敗します。

アプリケーションの実装と認証に成功すると、このチュートリアルの最後には、以下のようにアプリのロックが解除された状態が表示されるはずです。

はじめに
プラグイン製造者として、ハッカーによってあなたのプラグインがクラックされたり、あなたの同意なしに無料で配布されたりすることは、時にフラストレーションがたまるものです。あなたのアプリケーションをそのような脅威に対して完全に防御することは難しいかもしれませんが、ハッカーにとってこのプロセスをより面倒なものにする手段はあります。時には、製品を購入するかどうかを決める前に、ユーザーに試用期間を提供したり、無料層のユーザーに対して特定の機能を制限したりすることもできます。
PACEのiLokのようなサードパーティライセンスソリューションを使用する場合もあるが、これはライセンスを携帯するための物理的なUSBスティックを必要とする。
このチュートリアルでは、ウェブサーバー認証によってプラグインを登録する2番目の方法について説明します。この方法の利点は、オンラインでもオフラインでも機能し、登録に物理的なデバイスを必要としないことです。しかし、プラグインは一定期間マシンに登録されるため、プラグインを起動するたびに認証が必要なPACEのソリューションと比較すると、この手法はハッキングに対してより脆弱です。
では、サーバー認証はどのように行われるのでしょうか? マシンがインターネットに接続されている場合、プラグインが安全な接続を通じてユーザー認証情報を送信し、サーバーにライセンスキーを問い合わせる、いわゆるオンライン認証を行うことができます。ユーザーが製品を 購入したことが確認されれば、サーバーはキーファイルをマシンに送り返し、プラグインはロック解除されます。一方、マシンがオフラインの場合でも、ユーザーはインターネットに接続された別のコンピューターでウェブサーバーからキーファイルをダウンロードし、そのキーファイルをコピーしてプラグインにロードすることで、オフラインのマシンに適用できる。
RSA暗号化
ここで暗号について少し話をしよう。二者間の伝送中に情報が改ざんされないようにするにはどうすればいいのでしょうか? これを確実にするために、RSAと呼ばれる広く使われている安全な暗号化アルゴリズムを使います。RSAは、素数を用いて一対の鍵を作成し、一方を公開鍵、もう一方を秘密鍵として機能します。公開鍵は公に共有でき、メッセージの暗号化に使われる。秘密鍵は受信側で秘密にされ、メッセージの復号化に使われる。
RSAの設計上、一致するペアの対応する公開鍵で暗号化されたメッセージを復号できるのは秘密鍵の所有者のみである。このシナリオは、ボブは公開鍵を知っており、アリスは秘密鍵を所有し、イブはメッセージを傍受しようとする第三者である、という次の図で説明できる:
このブラウザではSVGを表示できません: 代わりにFirefox、Chrome、Safari、またはOperaをお試しください。
上記の場合、Eveはメッセージを復号するための秘密鍵を持っていないので、 Bobからのメッセージを読むことはできない。彼女は公開鍵を使って暗号化されたメッセージをアリスに送ることはできるが、 ボブのメッセージを傍受したいイヴには使えない。これは通常、RSAの最も一般的な使用例で、あるパーティが秘密のメッセージを送りたい場合に使われるが、プラグイン登録プロセスでRSAが使われるのはこの方法ではない。
プラグインの認証に使われるRSAには、秘密鍵でメッセージを暗号化し、公開鍵でそれを復号化するというシナリオもある。ボブはプラグインがインストールされたマシン、アリスは登録のための認証ウェブサーバ、イブはプラグインをハックしようとするサードパーティである:
このブラウザではSVGを表示できません: 代わりにFirefox、Chrome、Safari、またはOperaをお試しください。
この場合、BobとEveを含むAliceが秘密鍵で暗号化したメッセージは、 公開鍵を持っていれば誰でも復号できる。しかし、もしEveが暗号化されたメッセージを、マッチするペアの対応する秘密鍵なしでBobに送ろうとすると、Bobは公開鍵で復号することでメッセージの真正性をチェックできる。その結果、データにはゴミが含まれるため、EveはAliceになりすますことができない。
これと同様に、プラグイン認証は、サーバから送信されたメッセージが有効かどうかをチェックするだけで、実行できます。ありがたいことに、この暗号化と復号化の手続きは、JUCEライブラリが以下のようなクラスによって処理してくれます。KeyGenerationそしてRSAKey.
RSA暗号化の仕組みを理解したところで、デモ・プロジェクトに登録メカニズムを実装してみよう。
市場ステータスの導入
あるマシンでアプリケーションを使用するクライアントと、アプリケーションを認可する登録サーバとの間の通信を容易にするために、JUCEライブラリはOnlineUnlockStatusクラスで定義されています。このクラスは、アプリケーションがインストールされている現在のマシンの登録ステータスを管理する基盤を提供します。
店舗固有のマーケットプレイスを実装するための最初のステップは、次のものを継承することである。OnlineUnlockStatusクラスと、このサブクラスの実装方法の例はTracktionMarketplaceStatusクラスを作成します。この実装はTracktionマーケットプレイス専用に設計されたものなので、独自のサブクラスを作 成する必要がありますが、サーバからのレスポンスを処理するコードはそのまま流用します。
まずはTutorialMarketplaceStatus
クラスから派生したOnlineUnlockStatusクラスである。クラスはreadReplyFromWebserver()
そしてuserCancelled()
の関数をコピーした。TracktionMarketplaceStatusを実装した。
あとは、基底クラスの残りの仮想関数を埋めて、独自のマーケットプレイス実装を完成させる必要がある。
アプリケーションのプロダクトIDをgetProductID()
関数を使用して、このIDが後でサーバー上の製品IDと一致することを確認する:
juce::String getProductID() override
{
return "TestApp";
}
以下の通りである。doesProductIDMatch()
関数は、サーバーから送信されたIDが、前のステップで挿入された製品IDと一致するかどうかを検証しなければならない:
bool doesProductIDMatch (const juce::String& returnedIDFromServer) override
{
return getProductID() == returnedIDFromServer;
}
次に、ウェブサイトの名前をgetWebsiteName()
関数を使用します。この情報は、サーバーに問い合わせる際にユーザーにドメイン名を表示するためだけに使用されます。
juce::String getWebsiteName() override
{
return "juce.com";
}
次の関数が重要で、これはアプリの認証を試みる際にコンタクトされるアドレスであり、マーケットプレイスサーバーを指す必要がある。このチュートリアルの目的では、テストサーバーが私たちのマシン上でローカルに実行されるため、"localhost "または "127.0.0.1 "のIPアドレスを指します。サーバーは単純化のためにPHP言語を使用するため、指すページはPHP拡張子で終わります。
juce::URL getServerAuthenticationURL() override
{
return juce::URL ("https://localhost:8443/auth.php");
}
サーバとの通信に使用するプロトコルはHTTPSでなければならない。8443ポートはこの選択を反映し、デフォルトのSSLポートを示します。
登録プロセスが開始すると、クライアントアプリはユーザーから提供された認証情報を使って認証を要求し、サーバーは暗号化されたメッセージで応答します。アプリのロックを解除するための鍵ファイルを含むこのメッセージは、サーバー側でメッセージの暗号化に使用されたRSA秘密鍵に対応するRSA公開鍵を使用してのみ復号化できます。
についてgetPublicKey()
関数はこの公開鍵を返す必要があり、後でこのチュートリアルの鍵生成のステップで入力します。以下のように関数を準備し、フィールドはとりあえず空白にしておきます:
juce::RSAKey getPublicKey() override
{
return juce::RSAKey ("INSERT_PUBLIC_KEY_HERE");
}
についてTutorialMarketplaceStatus
クラスはこれで、マーケットプレイス・サーバーと通信してキーを取得するために必要な情報をすべて手に入れたことになる。
登録フォームの提供
ユーザーがアプリやプラグインに入力するための登録フォームを表示するには、いくつかの方法があります。通常のComponentをポップアップ表示させることもできます。DialogWindowをメインウィンドウの上に追加します。後者のオプションは、プラグインを検証する際に単純なスキャンルーチンをフリーズさせる可能性があり、また登録ウィンドウがまだアクティブな状態でDAWがプラグインをシャットダウンした場合に問題が発生する可能性があるため、プラグインを開発する際には非常にお勧めできません。
したがって、このセクションでは、アプリとプラグインの両方との互換性を確保するために、前者のオプションを選択します。登録ユーザインタフェースの実装プロセスを単純化するために、JUCEライブラリはOnlineUnlockFormこのクラスは、電子メールとパスワードのための2つのフィールドを表示し、登録のためのシームレスな実装を提供します。OnlineUnlockStatusクラスである。
という名前のクラスを作成する。TutorialUnlockForm
に由来する。OnlineUnlockForm以下の通りである:
class TutorialUnlockForm : public juce::OnlineUnlockForm
{
public:
TutorialUnlockForm (TutorialMarketplaceStatus& status)
: OnlineUnlockForm (status, "Please provide your email and password.")
{}
ここではTutorialMarketplaceStatus
クラスを実装し、クラス・コンストラクタでユーザーに指示するための説明メッセージを提供する。
の中でMainContentComponent
のインスタンスである。TutorialMarketplaceStatus
そしてTutorialUnlockForm
オブジェクトと、アプリケーションのロックが解除されたかどうかを保存するブール値。
TutorialMarketplaceStatus marketplaceStatus;
TutorialUnlockForm unlockForm;
bool isUnlocked = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};
コンストラクタを修正してTutorialUnlockForm
を渡すことでTutorialMarketplaceStatus
オブジェクトを作成し、フォームを子として追加します。Componentこの通りである:
class MainContentComponent : public juce::Component,
private juce::Timer
{
public:
//==============================================================================
MainContentComponent()
: unlockForm (marketplaceStatus)
{
addChildComponent (unlockForm);
setSize (400, 250);
startTimer (100);
}
からも継承している。Timerクラスを呼び出しstartTimer()
この関数は、アプリがロック解除されているかどうかをチェックするために後で使われる。
の中でresized()
メソッドを呼び出して、インタフェースの大部分をカバーするフォームのサイズを設定する。centreWithSize()
関数は次のようになる:
void resized() override
{
unlockForm .centreWithSize (300, 200);
についてshowForm()
ヘルパー関数は、ユーザーによって「ロック解除」ボタンが押されたときに呼び出されます。そのためComponentを追加しただけである。addChildComponent()
関数の代わりにaddAndMakeVisible()
を呼び出すだけで、フォームはデフォルトで非表示になります。setVisible()
ここにフォームを表示する:
void showForm()
{
unlockForm.setVisible (true);
}
登録がキャンセルされた場合、または登録が成功した場合は、フォームを破棄してOnlineUnlockFormクラスは自動的にdismiss()
コールバックで行う。コールバックはTutorialUnlockForm
宣言をオーバーライドするにはsetVisible()
を隠す。Component:
void dismiss() override
{
setVisible (false);
}
};
のコンストラクタで見たとおりである。MainContentComponent
でアプリケーションが正常にロック解除されたかどうかを定期的にチェックするタイマーが起動される。timerCallback()
関数である:
void timerCallback() override
{
if (! isUnlocked && marketplaceStatus.isUnlocked())
{
isUnlocked = true;
unlockApp();
}
}
ここでは、アプリがまだロックされているかどうかをisUnlocked
ローカル変数に尋ねる。OnlineUnlockStatusインスタンスを使って、アプリがまだロックされているかどうかを調べます。isUnlocked()
関数を呼び出す。アプリが認証された場合、ローカル変数を切り替えてその後の繰り返しを防ぎ、以下を呼び出す。unlockApp()
ヘルパー関数:
void unlockApp()
{
secretButton.setEnabled (true);
unlockButton.setEnabled (false);
unlockLabel.setText ("Status: Unlocked", juce::dontSendNotification);
unlockLabel.setColour (juce::Label::textColourId, juce::Colours::green);
}
この機能は、単にシークレット機能にアクセスするためのボタンを有効にし、新しい認証ステータスを表示しながらアプリのロックを解除するためのボタンを無効にします。
最後にcheckFeature()
関数は、ユーザーが秘密機能ボタンをクリックしたときに呼び出され、ロック解除されたアプリケーションの動作をシミュレートします:
void checkFeature()
{
if (marketplaceStatus.isUnlocked())
DBG ("App unlocked!");
else
DBG ("Beware of hackers!");
}
この関数は、アプリケーションにもう1つのセキュリティ・レイヤーを提供するものとして重要である。ここで、この関数が再びOnlineUnlockStatusインスタンスで、アプリがまだアンロックされているかどうかを確認します。この背景には、ハッカーがアプリのロックを解除するためにこの関数をクラックする必要があるだけでなく、アプリの制限された機能を完全に突破するためには、アプリケーションコード内のこの関数のインスタンスをすべて置き換える必要があるからです。
したがって、より強固なセキュリティ・メカニズムを提供するために、コード内のできるだけ多くの場所でアプリケーションの認証状態をチェックすることをお勧めします。
アプリケーションを実行すると、登録フォームを表示できるようになりました。

しかし、任意のメールアドレスとパスワードの組み合わせでアプリを登録しようとすると、以下のようなエラーメッセージが表示される:

これは、アプリケーショ ンが認証サーバーに接続できないためです。
この修正版のソースコードはOnlineUnlockStatusTutorial_02.h
ファイルにある。
バックエンドサーバーのセットアップ
このセクションでは、PHPを使用したmacOSとWindowsのローカル・テスト・サーバーのセットアップ手順を説明しますが、Java、Ruby、Pythonなど、どのようなタイプのバックエンド・サーバーでもご自由にお使いください。
以来OnlineUnlockStatusクラスはサーバーからの応答をXMLフォーマットで扱うので、 PHPを使ってXMLレスポンスを提供しなければなりません。アプリケーションは認証情報をURLで供給される。getServerAuthenticationURL()
の機能である。TutorialMarketplaceStatus
チュートリアルの最初のセクションで "https://localhost:8443/auth.php "と定義された実装である。
従って、次のことを見てみよう。auth.php
デモ・プロジェクトに付属しているResources
フォルダに移動し、PHPでどのように応答が作成されたかを調べます:
'; // [2]
if($_POST["email"] === "test@example.com" and $_POST["pw"] === "test") // [3]
$response .= 'INSERT_KEY_HERE';
else
$response .= '';
return $response;
}
header("Content-type: text/xml; charset=utf-8"); // [4]
echo sendResponse();
?>
- [1]という関数を定義する。
sendResponse()
これは、XMLコンテンツを表示するために後で呼び出される。 - [2]次に、メタデータヘッダをPHPのローカル変数に代入してXMLドキュメントを準備します。
- [3]次に、POSTで受け取ったテスト用の電子メールとパスワードの文字列と比較することで、認証情報がテス ト・ユーザーに対応しているかどうかをチェックする。認証情報が一致した場合、成功メッセージと、次のセクションで挿入する暗号化鍵ファイルを追加します。そうでなければ、認証に失敗した理由を説明するエラーメッセージを追加します。
- [4]最後に、コンテンツがXMLフォーマットであることを示すレスポンス・ヘッダを設定し、レスポンスを
echo
関数である。
この時点で、ローカルでサーバーを実行し、アプリから認証サーバーに接続しようとすると、セキュリティ警告が表示され、接続がプライベートまたはセキュアでないことを知らされる。この場合OnlineUnlockStatusインスタ ンスに認証情報を安全に送信するために、HTTPSプロトコルを介してSSLを使用したセキュアな接続を作成する必要があります。
テスト目的でローカルでこれを行うには、自己署名証明書を作成し、トンネリングを使用して、基盤となる安全でないHTTPプロトコルにHTTPSでアクセスすればよい。
バックエンドサーバーをテストするために、OpenSSL、PHP、"stunnel "がコンピューターにインストールされていることを確認してください。OpenSSLは自己署名証明書を生成するために必要で、PHPはあなたのマシンでローカルに実行できる組み込みのウェブサーバーを提供し、"stunnel "は安全な接続をトンネル化できます。
に移動する。Resources
フォルダを開き、ターミナルから以下のコマンドを実行して自己署名証明書を作成する:
// Creates an RSA key and certificate pair.
// macOS
openssl req -new -newkey rsa:4096 -x509 -nodes -sha256 -days 365 -keyout stunnel.key -out stunnel.cert
// Windows
C:\path-to-openssl\openssl req -new -newkey rsa:4096 -x509 -nodes -sha256 -days 365 -keyout stunnel.key -out stunnel.cert
証明書の情報を入力するプロンプトが表示されたら、「localhost」と入力するコモン・ネーム(CN)項目以外のすべてのフィールドをスキップする。
ドメイン名のスペルが間違っていると、サーバーに接続する際に問題が発生するので、間違えないように注意すること。
これにより、SHA-256ハッシュ・アルゴリズムと1年の有効期限を使用した、RSA 4096ビットの鍵と証明書のペアを形成する2つのファイルが作成される。X.509標準は、われわれが関心を持っている自己署名ポリシーを示す。単純化するために、生成された鍵ファイルと証明書ファイルを次のように1つのPEMファイルにまとめる:
// Combines the key and certificate into a PEM file.
// macOS
cat stunnel.key stunnel.cert > stunnel.pem
// Windows
type stunnel.key stunnel.cert > stunnel.pem
macOSでは、PEMファイルをダブルクリックします。Finder
をクリックして、証明書をキーチェインに追加します。これでKeychain Access
アプリケーションはApplications/Utilities
フォルダに追加します。リスト内の新しく追加された証明書をダブルクリックして、詳細ビューを開き信頼ビューを表示します。AlwaysTrust(常に信頼する)]オプションを選択し、以下のように、証明書をmacの現在のユーザーアカウントで信頼できるようにします:

Windowsの場合はMicrosoft Management Console
に「MMC」と入力してください。Run
プロンプトを表示しファイル > スナップインの追加...をクリックし、"Certificates "を選択する。をクリックしてください。追加をクリックして、現在のユーザーの証明書を追加しOKを終了する。

次に、自己署名証明書を信頼できるルート認証局 > 証明書左のパネルで対応するフォルダに移動し、次の項目を選択します。**アクション > すべてのタスク > インポート...**を選択します。証明書を探す手順が表示されますので、その時点で.cert
拡張子がMMCで受け入れられるフォーマットである。

自己署名証明書やトンネリングは、CA署名証明書ほど安全でないとみなされるため、実際のアプリケーションでは使用しないでください。この方法はあくまでデモンストレーションのために使用するものです。
次に、トンネリングプロトコルを設定するためにstunnel.conf
デモ・プロジェクトに付属しているResources
フォルダー
[https]
accept = 8443
connect = 8080
cert = stunnel.pem
TIMEOUTclose = 0
ここでは、接続元と接続先の入出ポート、およびリンクをセキュアにするために前のステップで作成した証明書ファイルを定義します。アプリケーションはポート8443への接続を試み、トンネリングはPHPサーバーが動作しているポート8080にリンクします。
サーバーを起動するには、最後にResources
ディレクトリに移動し、ターミナルから以下のコマンドを実行する:
// Starts the built in PHP web server and tunnels a secure connection.
// macOS
php -S localhost:8080 & stunnel stunnel.conf
// Windows
C:\path-to-php\php -S 127.0.0.1:8080 & C:\path-to-stunnel\stunnel stunnel.conf
これにより、ローカルサーバーが8080番ポートで起動し、アプリケーションが安全にアクセスできるように、接続が8443番ポートにトンネルされます。認証を開く