はじめに
みなさん、こんにちは。捡田螺的小男孩です。
私たちが日常的に開発を行う中で、インターフェースのデータセキュリティをどのように確保するか。個人的には、インターフェースデータのセキュリティ確保のプロセスは、主に以下の点に現れていると考えています。1つ目はデータ転送プロセスにおけるセキュリティ、次はデータがサーバーサイドに到着した際に、どのようにデータを識別するか、最後の1つはデータ保存のセキュリティです。今日は、インターフェースデータのセキュリティを確保するための10の方法についてお話しします。

1. データ暗号化、平文でのメッセージ送信を防ぐ
ご存知のように、ネットワーク転送中にデータは簡単にパケットキャプチャされてしまいます。httpプロトコルを使用している場合、平文で転送されるため、ユーザーのデータは簡単に他人に取得されてしまいます。そのため、データを暗号化する必要があります。
1.1 データはどのように暗号化するのか?
一般的な実装方法は、重要なフィールドを暗号化することです。例えば、ログイン用のインターフェースでは、パスワードを暗号化することができます。一般的に使用される暗号化アルゴリズムは何でしょうか?簡単な方法としては、共通鍵暗号方式(AESなど)を使用して暗号化/復号化するか、ハッシュアルゴリズム(MD5など)で処理します。
共通鍵暗号方式とは、暗号化と復号化に同じ鍵を使用する暗号化アルゴリズムのことです。

公開鍵暗号方式(非対称暗号方式):非対称暗号アルゴリズムには2つの鍵(公開鍵と秘密鍵)が必要です。公開鍵と秘密鍵はペアで存在し、公開鍵でデータを暗号化した場合、対応する秘密鍵でのみ復号化できます。
より安全な方法は、公開鍵暗号方式(非対称暗号方式)(RSAやSM2など)を使用し、公開鍵で暗号化し、秘密鍵で復号化することです。

すべてのフィールドを暗号化したい場合は、一般的にhttpsプロトコルを使用することを推奨します。httpsは、実際にはhttpとtcpの間に暗号化レイヤーであるSSLを追加したものです。
1.2 皆さん、httpsの原理を覚えていますか?
面接でもよく聞かれるので、以下の通りです:

- クライアントがHttpsリクエストを発行し、サーバーの443ポートに接続します。
- サーバーはデジタル証明書(証明書には公開鍵、証明書発行機関、有効期限などが含まれます)を持っている必要があります。
- サーバーは自身のデジタル証明書をクライアントに送信します(公開鍵は証明書内にあり、秘密鍵はサーバーが保持します)。
- クライアントはデジタル証明書を受信した後、証明書の正当性を検証します。証明書の検証が成功すると、ランダムな共通鍵(対称鍵)を生成し、証明書の公開鍵で暗号化します。
- クライアントは公開鍵で暗号化された鍵をサーバーに送信します。
- サーバーはクライアントから送信された暗号化された鍵を受信し、自身が保持していた秘密鍵を使用して非対称復号化を行い、復号化後、クライアントの鍵を取得します。そして、クライアントの鍵を使用して返却データを共通鍵暗号化し、これにより転送データはすべて暗号文となります。
- サーバーは暗号化されたデータをクライアントに返却します。
- クライアントは受信後、自身の鍵を使用して共通鍵復号化を行い、サーバーから返されたデータを取得します。
日常業務では、データ転送の暗号化に関しては、httpsを使用すれば十分です。しかし、ログインや登録など、パスワードを転送する必要がありセキュリティ要件が高い場合は、パスワードにRSAなどの非対称暗号方式を使用して暗号化することができます。業務のセキュリティ要件が非常に高い場合は、httpsのこのプロセスを模倣して、メッセージに対してさらに暗号化/復号化を1回追加することも可能です。
2. データの署名と署名検証
データメッセージの署名と署名検証は、データ転送のセキュリティを確保するための一般的な手段であり、データが転送中に改ざんされないことを保証します。以前私が担当した企業送金システムでは、署名と署名検証を使用していました。
2.1 署名と署名検証とは?
- データ署名(加签):Hashアルゴリズム(
MD5やSHA-256など)を使用して、元のリクエストパラメータからメッセージダイジェストを生成し、秘密鍵でこのダイジェストを暗号化して、メッセージに対応するデジタル署名signを取得します(このプロセスが署名です)。通常、リクエスト側はデジタル署名とメッセージ原文を一緒に受信側に送信します。

- 署名検証(验签):受信側は元のメッセージとデジタル署名(
sign)を受け取った後、同じHashアルゴリズム(例えば両方ともMD5)を使用してメッセージからダイジェストAを生成します。また、相手から提供された公開鍵を使用してデジタル署名を復号化し、ダイジェストBを取得します。AとBを比較することで、メッセージが改ざんされたかどうかを確認できます。

私の理解では、署名とは、リクエストパラメータを一定のルールに従い、hashアルゴリズムと暗号化アルゴリズムを使用して一意のタグsignを生成することです。署名検証とは、リクエストパラメータを同じルールで処理し、同じhashアルゴリズムと対応する鍵を使用して復号化し、署名が一致するかどうかを比較することです。
もう一つ例を挙げると、一部の開発者は以下のように実装しています。すべての空でないパラメータ(
AccessKeyという一意の開発者識別子を含む)を昇順に並べ、さらにSecretKey(これはローカルでの暗号化のみに使用され、ネットワーク転送には関与せず、署名の中でのみ使用されます)を連結して、stringSignTempの値を取得し、最後にMD5演算を行い、signを取得します。サーバーサイドはメッセージを受信した後、正当な
AccessKeyと署名Signが正しい場合のみ検証をパスさせます。これにより、本人確認とパラメータ改ざんの問題が解決されます。リクエストパラメータが傍受された場合でも、傍受者はSecretKeyを取得できないため(ローカル暗号化のみでネットワーク転送には関与しない)、正当なリクエストを偽造することはできません。
2.2 httpsなどでデータを暗号化しているのに、なぜ署名と署名検証が必要なのか?
一部の開発者は疑問に思うかもしれません。署名と署名検証の主な目的はデータの転送中の改ざん防止ですが、httpsプロトコルでデータを暗号化しているなら、なぜ改ざんされる可能性があるのか?なぜ署名と署名検証が必要なのか?
データは転送中に暗号化されているため、理論的にはパケットキャプチャされてもデータは改ざんされません。しかし、httpsは絶対的に安全ではありません。こちらの記事をご参照ください:恐ろしい、実はHTTPSも役に立たない。もう一つのポイント:
httpsの暗号化部分は外部ネットワークのみであり、多くのサービスは内部ネットワークで相互にジャンプします。署名はここでも中間者による改ざんを防ぐことができるため、一般的に送金などセキュリティ要件の高いインターフェース開発には署名と署名検証が必要です。
3. Token認可認証メカニズム
日常開発では、ウェブサイトやアプリは通常ユーザーログインが必要です。では、非ログインインターフェースの場合、どのようにセキュリティを確保し、ユーザー身份を確認するのでしょうか?token認可メカニズムを使用することができます。
3.1 Token認可認証の方式
Token認可認証の方式:ユーザーがクライアントでユーザー名とパスワードを入力し、ログインボタンをクリックすると、サーバーはパスワードが正しいことを検証し、成功するとクライアントに一意の値tokenを返します。そしてtokenをキーと値のペアでキャッシュ(通常はRedis)に保存します。その後のクライアントの認可が必要なモジュールへのすべての操作にはこのtokenを含める必要があり、サーバー側はリクエストを受け取った後、まずtokenの検証を行い、tokenが存在すれば正当なリクエストとみなします。
Tokenログイン認可フローチャート:

- ユーザーがユーザー名とパスワードを入力し、ログインリクエストを開始します。
- サーバーサイドがパスワードを検証し、検証が成功すると、グローバルに一意のtokenを生成します。
tokenをredisに保存します。keyはtoken、valueはuserIdまたはユーザー情報で、有効期限を設定します。- この
tokenをクライアントに返します。 - ユーザーが他の業務リクエストを開始する際には、この
tokenを含める必要があります。 - バックエンドサービスはインターフェースリクエストを一律にインターセプトし、
tokenの有効性を検証し、そこからユーザー情報を取得して、後続の業務ロジックに使用します。tokenが存在しない場合は、リクエストが無効であると見なします。
3.2 Tokenの安全性をどのように確保するか?Tokenが傍受された場合は?
Tokenの安全性をどのように確保するのでしょうか?
例えば、tokenを取得した場合、サーバーサイドの任意のインターフェースを呼び出せるのでしょうか?以下の観点から検討できます:
- Tokenに適切な有効期限を設定する
- httpsプロトコルを使用する
- Tokenをさらに暗号化する
- 機密情報にアクセスする場合、単にTokenを追加するだけでは不十分であり、通常はさらにホワイトリストを設定します。
Tokenと言えば、一部の開発者はjwt(JSON Web Token)を思い浮かべるかもしれませんが、実際にはTokenの一種です。興味のある方は調べてみてください。
4. タイムスタンプ(timestamp)によるタイムアウトメカニズム
データは簡単にパケットキャプチャされます。仮にhttpsと署名検証を使用していたとしても、中間者がデータメッセージをキャプチャしても、実際のデータを見ることはできません。しかし、中には実際のデータに関心を持たず、キャプチャしたデータパケットをそのまま使用して悪意のあるリクエスト(DOS攻撃など)を行い、システムをダウンさせようとする不正者もいます。
そこで、タイムスタンプによるタイムアウトメカニズムを導入することで、インターフェースのセキュリティを確保できます。すなわち、ユーザーは毎回のリクエストに現在の時刻のタイムスタンプtimestampを含め、サーバーサイドはtimestampを受信し、復号化し、署名検証を通過した後、サーバーの現在時刻と比較します。時間差が一定時間(例えば3分)を超えた場合、そのリクエストは無効とみなされます。
5. timestamp+nonce方式によるリプレイ攻撃防止
タイムスタンプによるタイムアウトメカニズムにも脆弱性があります。時間差内にハッカーがリプレイ攻撃を行った場合、この仕組みは機能しません。そこでtimestamp+nonce方式を使用します。
nonceは一意のランダム文字列で、署名された各リクエストを識別するために使用されます。毎回のリクエストのnonceパラメータを「setコレクション」に保存するか、JSON形式でデータベースやキャッシュに保存することができます。HTTPリクエストを処理するたびに、まずそのリクエストのnonceパラメータがその「コレクション」に存在するかどうかを判定し、存在する場合は不正なリクエストとみなします。
しかし、サーバーにとってnonceを永続的に保存するコストは非常に大きいです。timestampと組み合わせて最適化できます。timestampパラメータが3分を超えるリクエストはすべて不正とみなされるため、3分分のnonceパラメータの「コレクション」のみを保存すれば十分です。
6. レート制限(レートリミット)メカニズム
もしユーザーが実際に正当なユーザーでありながら、悪意を持って頻繁にインターフェースを呼び出し、システムをダウンさせようとした場合はどうでしょうか?そのような場合には、レート制限を導入する必要があります。
一般的に使用されるレート制限アルゴリズムには、トークンバケットアルゴリズムとリーキーバケットアルゴリズムがあります。こちらの記事をご参照ください:面接必須:4種類の古典的レート制限アルゴリズム解説
GuavaのRateLimiterを使用したスタンドアロン版レート制限、Redisを使用した分散レート制限、あるいはAlibabaのオープンソースコンポーネントsentinelを使用したレート制限が可能です。例えば、1分間に何回までリクエストを受け付けるか、といった設定ができます。
7. ブラックリストメカニズム
正当なユーザーによる悪意のあるリクエストが発見された場合、ブラックリストメカニズムを導入して、そのユーザーをブラックリストに登録することができます。一般的に、競合他社や悪意のあるユーザーがシステムを攻撃しようとすることがあります。そのため、セキュリティを確保するために、通常は業務システムにブラックリストメカニズムを持たせる必要があります。ブラックリストからのリクエストには、直接エラーコードを返すようにします。
8. ホワイトリストメカニズム
ブラックリストメカニズムがあれば、ホワイトリストメカニズムも設定できます。以前私が担当していた企業送金システムでは、外部の加盟店がシステムに接続する場合、事前にネットワークホワイトリストを申請する必要がありました。その際、運用チームがIPネットワークホワイトリストを申請し、ホワイトリスト内のリクエストのみが送金システムにアクセスできるようにしていました。
9. データマスキング
パスワード、電話番号、身分証番号などの機密情報については、通常、表示する際にマスキング(一部を伏せ字にする)が必要です。パスワードの場合は、さらに暗号化してデータベースに保存する必要があります。
電話番号や身分証番号などの情報については、日常の開発において、ログ調査時にマスキングされた状態で表示されるべきです。目的はこれらのユーザー情報をできる限り漏洩させないことであり、ログを見ることができるのは開発者や運用担当者だけですが、それでも対策としてマスキング処理を行う必要があります。
パスワードをデータベースに保存する場合、平文のまま保存することは絶対に避けるべきです。最低限でもMD5で処理してから保存する必要があります。Spring SecurityのBCryptPasswordEncoderも使用できます。このエンコーダーは内部でSHA-256 + ランダムソルト + 鍵を使用してパスワードを暗号化しており、SHAとMDシリーズは同じhashダイジェスト系のアルゴリズムです。
10. データパラメータの正当性チェック
インターフェースデータのセキュリティを確保するためには、システムにデータの正当性チェック、簡単に言うとパラメータバリデーションを実装する必要もあります。例えば、身分証番号の長さ、電話番号の長さ、数値かどうかなどです。
まとめ
本記事では、インターフェースデータのセキュリティを確保するための10の方法をご紹介しました。他にも方法があれば、コメント欄でご議論いただければと思います。一緒に学び合いましょう。