外部API MyAPI
から入力を受け取る関数をコーディングしているとしましょう。
その外部API MyAPI
は、string
またはnumber
を返すことを示す規約を持っています。
null
のAPIの一部ではありませんが、undefined
、boolean
、MyAPI
などのようなものから保護することをお勧めしますか?特に、あなたはそのAPIを制御できないので、静的型分析のようなものを通して保証をすることはできません。
堅牢性の原則 との関連で考えています。
ソースに関係なく、ソフトウェアへの入力を信頼してはなりません。タイプを検証するだけでなく、入力の範囲やビジネスロジックも重要です。コメントごとに、これは [〜#〜] owasp [〜#〜] によってよく説明されています
そうしないと、せいぜい後でクリーンアップする必要のあるガーベッジデータが残されますが、最悪の場合、そのアップストリームサービスが何らかの形で侵害された場合(q.v.ターゲットハック)に悪意のある悪用の機会が残されます。その間の問題の範囲には、アプリケーションを回復不可能な状態にすることが含まれます。
コメントから、おそらく私の答えは少し拡張を使用できることがわかります。
「入力を信頼しない」とは、上流または下流のシステムから常に有効で信頼できる情報を受け取るとは限らないため、その入力を可能な限り完全に無害化するか、拒否することを意味します。それ。
例として取り上げるコメントで、1つの議論が浮上しました。はい、ある程度はOSを信頼する必要があります。たとえば、1から10までの数値を要求し、「bob」で応答した場合に乱数ジェネレーターの結果を拒否することは不合理ではありません。
同様に、OPの場合、アプリケーションが上流のサービスからの有効な入力のみを受け入れるようにする必要があります。うまくいかないときに何をするかはあなた次第であり、達成しようとしている実際のビジネス機能に大きく依存しますが、後でデバッグするためにログに記録し、それ以外の場合はアプリケーションが実行されないようにします回復不能または安全でない状態に。
誰か/何かがあなたに与える可能性のあるすべての入力を知ることは決してできませんが、ビジネス要件に基づいて許容できるものを制限し、それに基づいて何らかの形式の入力ホワイトリストを作成することができます。
はいもちろんです。しかし、答えが異なる可能性があると思いますか?
契約が言っていることをAPIが返さない場合に備えて、プログラムが予期しない方法で動作することを望まないのではないでしょうか。したがって、少なくともあなたはそのような振る舞いをどうにかしなければなりません。最小限の形式のエラー処理は常に(非常に最小限の!)努力する価値があり、このようなものを実装しないことの言い訳は絶対にありません。
ただし、そのような場合に対処するためにどれだけの労力を費やすべきかは、ケースに大きく依存し、システムのコンテキストでのみ回答できます。多くの場合、短いログエントリとアプリケーションを正常に終了させるだけで十分です。場合によっては、詳細な例外処理を実装したり、「間違った」戻り値のさまざまな形式を処理したり、フォールバック戦略を実装したりするほうがよい場合があります。
しかし、社内のスプレッドシートフォーマットアプリケーションを10人未満で使用し、アプリケーションのクラッシュによる経済的影響が非常に少ない場合、または新しい自動運転車を作成している場合は、それは大きな違いになります。アプリケーションのクラッシュにより人命が犠牲になるシステム。
だからあなたがしていることを反映することへの近道はありません、あなたの常識を使うことは常に必須です。
ロバスト性の原則、具体的には、「受け入れるものを自由にする」の半分は、ソフトウェアでは非常に悪い考えです。もともとはハードウェアのコンテキストで開発されましたが、物理的な制約によりエンジニアリングの許容範囲が非常に重要になりますが、ソフトウェアでは、誰かが不正な形式または不適切な入力を送信したときに、2つの選択肢があります。それを拒否するか(できれば何が問題だったかについての説明を添えて)、それが何を意味するはずだったのかを理解することができます。
EDIT:上のステートメントで間違っていたことがわかりました。堅牢性の原則は、ハードウェアの世界からではなく、インターネットアーキテクチャ、具体的には RFC 1958 から来ています。それは述べています:
3.9送信時は厳格にし、受信時は許容する。実装は、ネットワークに送信するときに仕様に正確に従う必要があり、ネットワークからの誤った入力を許容する必要があります。疑わしい場合は、仕様で必要とされない限り、エラーメッセージを返さずに、誤った入力を静かに破棄してください。
これは、はっきり言って、最初から最後まで単純に間違っています。この投稿で説明されている理由により、「エラーメッセージを返さずにエラーのある入力を静かに破棄する」よりも、エラー処理の誤った考え方を理解することは困難です。
この点の詳細については、IETFペーパー ロバストネス原則の有害な結果 も参照してください。
決して、決して、neverスローするGoogleの検索チームと同等のリソースがない限り、2番目のオプションを選択しますそれは、その特定の問題領域でまともな仕事に近いことを何でも行うコンピュータプログラムを思いつくために必要なことだからです。 (そして、それでも、Googleの提案は、約半分の時間で直接左のフィールドから出てきているように感じます。)そうしようとすると、プログラムが頻繁に解釈しようとする大きな頭痛が生じます。送信者が実際に意味したのがYである場合、Xとしての入力が正しくありません。
これには2つの理由があります。明らかなのは、システムに不良データがあるためです。それほど明白ではないものは、多くの場合、あなたや送信者のいずれも、何かがあなたの顔で爆破したときにずっと後で道のりで問題が起こったことに気付かず、突然あなたが修正する大きな高価な混乱を抱えていて、何もわからないということです目立った影響が根本的な原因から遠く離れているため、何が問題でしたか。
これが、Fail Fast原則が存在する理由です。それをAPIに適用することで、頭痛の種に関係するすべての人を救います。
一般に、コードは、少なくとも次の制約を実現できるように構成する必要があります。
正しい入力が与えられると、正しい出力が生成されます。
有効な入力が与えられると(正しい場合もそうでない場合もあります)、有効な出力を生成します(同様に)。
無効な入力が与えられた場合、通常の入力によって引き起こされたものや、エラーを通知するものとして定義されたものを超える副作用なしに処理します。
多くの状況で、プログラムは本質的に、それらが有効であるかどうかを特に気にすることなく、データのさまざまなチャンクを通過します。そのようなチャンクにたまたま無効なデータが含まれている場合、結果としてプログラムの出力に無効なデータが含まれる可能性があります。すべてのデータを検証し、無効な出力が生成されないことをプログラムが特別に設計していない限り、無効な入力が与えられても、その出力を処理するプログラムは可能性を考慮に入れるべきですその中の無効なデータの。
多くの場合、データを早期に検証することは望ましいことですが、常に実用的であるとは限りません。特に、1つのデータチャンクの有効性が他のチャンクの内容に依存する場合、および一連のステップに供給されたデータの大部分が途中でフィルターで除外され、検証がデータに限定される場合すべての段階で、すべてを検証するよりもはるかに優れたパフォーマンスが得られる場合があります。
さらに、プログラムに事前検証済みのデータが与えられることだけが期待されている場合でも、実際には、上記の制約anywayを維持することをお勧めします。多くの場合、すべての処理ステップで完全な検証を繰り返すとパフォーマンスが大幅に低下しますが、上記の制約を維持するために必要な検証の量は限られているため、はるかに安価になる場合があります。
2つのシナリオを比較して、結論を出してみましょう。
シナリオ1このアプリケーションは、外部APIが契約どおりに動作することを前提としています。
シナリオ2このアプリケーションでは、外部APIが誤動作する可能性があると想定しているため、予防策を追加します。
一般に、APIまたはソフトウェアが契約に違反する可能性があります。バグまたは予期しない状況が原因である可能性があります。 APIでさえ、内部システムに問題があり、予期しない結果になる可能性があります。
私たちのプログラムが、外部APIが契約に準拠し、予防策を追加しないことを前提に書かれている場合。問題に直面している当事者は誰ですか?統合コードを書いたのは私たちです。
たとえば、選択したnull値。たとえば、API契約に従って、レスポンスにnull以外の値を含める必要があります。しかし、それが突然違反された場合、プログラムはNPEになります。
したがって、予期しないシナリオに対処するために、アプリケーションにいくつかの追加コードがあることを確認することをお勧めします。
一般に、はい、常に欠陥のある入力から保護する必要がありますが、APIの種類によっては、「保護」は異なる意味を持ちます。
サーバーへの外部APIの場合、サーバーの状態をクラッシュまたは危険にさらすコマンドを誤って作成したくないので、それを防ぐ必要があります。
たとえば、次のようなAPIの場合コンテナクラス(リスト、ベクトルなど)、例外のスローは完全に問題のない結果であり、クラスインスタンスの状態をある程度損なうことは許容できる場合があります(たとえば、比較演算子が正しくない場合にソートされたコンテナはソートされません)。アプリケーションのクラッシュは許容できるかもしれませんが、アプリケーションの状態を損なう-例クラスインスタンスに関係のないランダムなメモリ位置への書き込み-ほとんどの場合そうではありません。
ユーザーが入力したデータなど、常に受信データを検証する必要があるため、この外部APIから取得したデータが無効な場合に処理するプロセスを用意する必要があります。
一般的に言って、組織外システムが出会う継ぎ目には、認証、承認(認証だけで定義されていない場合)、および検証が必要です。
少し異なる意見を述べると、契約に違反していても、与えられたデータをそのまま使用することは許容できると思います。これは使用方法によって異なります。文字列である必要があるものか、単に表示している/使用していないものなどです。後者の場合は、そのまま受け入れます。別のAPIによって配信されるデータの1%を必要とするAPIがあります。 99%はどのようなデータなのか気にならなかったのでチェックしません。
「入力を十分にチェックしないためエラーが発生する」と「厳しすぎるため有効なデータを拒否する」の間にはバランスが必要です。
これに対する私の見方は、常に、システムへのすべての入力を常にチェックすることです。つまり、プログラムで使用されていない場合でも、APIから返されるすべてのパラメーターを確認する必要があります。 APIに送信するすべてのパラメーターが正しいかどうかもチェックする傾向があります。このルールには2つの例外があります。以下を参照してください。
テストの理由は、何らかの理由でAPI /入力が正しくない場合、プログラムは何にも依存できないためです。多分私のプログラムは、私が信じているものとは異なる何かをする古いバージョンのAPIにリンクされていましたか?多分、私のプログラムは、これまでになかった外部プログラムのバグに遭遇しました。またはさらに悪いことに、常に発生しますが、誰も気にしません!たぶん、外部プログラムがハッカーに騙されて、私のプログラムやシステムに害を与える可能性のあるものを返しているのでしょうか?
私の世界のすべてをテストする2つの例外は次のとおりです。
パフォーマンスを注意深く測定した後のパフォーマンス:
エラーの対処法がわからない場合
入力/戻り値を正確にどれだけ注意深くチェックするかは重要な問題です。例として、APIが文字列を返すと言われている場合は、次のことを確認します。
データ型は実際には文字列です
その長さは最小値と最大値の間です。プログラムが処理できると予想される最大サイズの文字列を常にチェックします(大きすぎる文字列を返すことは、ネットワーク化されたシステムでの古典的なセキュリティ問題です)。
一部の文字列は、関連がある場合に「不正な」文字またはコンテンツをチェックする必要があります。プログラムが文字列を送信して後でデータベースを通知する可能性がある場合は、データベース攻撃をチェックすることをお勧めします(SQLインジェクションを検索します)。これらのテストは私のシステムの境界で行うのが最適です。攻撃がどこから来たのかを特定でき、早期に失敗する可能性があります。文字列が後で結合されるときに完全なSQLインジェクションテストを実行することは難しい場合があるため、データベースを呼び出す前にテストを実行する必要がありますが、いくつかの問題を早期に発見できる場合は便利です。
APIに送信するパラメーターをテストする理由は、正しい結果が返されることを確認するためです。繰り返しになりますが、APIを呼び出す前にこれらのテストを行うことは不要に思えるかもしれませんが、パフォーマンスはほとんど必要なく、プログラムでエラーをキャッチする可能性があります。したがって、テストはシステムを開発するときに最も価値があります(しかし、今日ではすべてのシステムが継続的に開発されているようです)。パラメータに応じて、テストは多かれ少なかれ完全になる可能性がありますが、私のプログラムが作成できるほとんどのパラメータに許容最小値と最大値を設定できることがよくあります。おそらく、文字列は常に2文字以上で、最大2000文字にする必要がありますか?プログラムが一部のパラメーターの全範囲を使用することはないことを知っているので、最小値と最大値はAPIが許可する範囲内にある必要があります。