外部APIからの予期しない値から保護する必要がありますか?


51

外部APIから入力を受け取る関数をコーディングしているとしましょうMyAPI

その外部APIにMyAPIは、a stringまたはa を返すことを示す契約がありnumberます。

それはのようなものから保護することをお勧めしますnullundefinedbooleanそれはのAPIの一部ではないにもかかわらずなど、MyAPI?特に、そのAPIを制御することはできないため、静的型分析などの方法で保証することはできません。

私はロバストネスの原則に関連して考えています。


16
これらの予期しない値が返された場合、それらを処理しないことの影響は何ですか?これらの影響で生きることはできますか?これらの予期しない値を処理して、影響に対処する必要をなくすことは複雑な価値がありますか?
ビンセントサバード

55
あなたがそれらを期待しているなら、定義上、それらは予想外のものではありません。
メイソンウィーラー

28
APIには有効なJSONのみを返す義務はありません(これはJSONであると想定しています)。次のような返信を受け取ることもできます<!doctype html><html><head><title>504 Gateway Timeout</title></head><body>The server was unable to process your request. Make sure you have typed the address correctly. If the problem persists, please try again later.</body></html>
user253751

5
「外部API」とはどういう意味ですか?まだあなたの管理下にありますか?
デデュプリケーター

11
「優れたプログラマーとは、一方通行をする前に両方の見方をする人です。」
jeroen_de_schutter

回答:


103

ソースに関係なく、ソフトウェアへの入力を決して信頼しないでください。型の検証だけでなく、入力の範囲とビジネスロジックも重要です。コメントによると、これはOWASPによってよく説明されています

そうしないと、せいぜいクリーンアップする必要があるガベージデータしか残されませんが、最悪の場合、何らかの方法でその上流のサービスが危険にさらされると、悪意のある悪用の機会が残ります(qv the Targetハック)。その間の問題の範囲には、アプリケーションを回復不能な状態にすることが含まれます。


コメントから、おそらく私の答えが少し拡張される可能性があることがわかります。

「入力を決して信用しない」ということは、上流または下流のシステムから常に有効かつ信頼できる情報を常に受け​​取ると想定できないことを意味します。したがって、常に入力を可能な限り無害化するか、拒否しますそれ。

例として取り上げるコメントに、1つの議論が浮上しました。はい、OSをある程度信頼する必要があります。たとえば、1から10までの数字を要求し、「bob」で応答する場合、乱数ジェネレーターの結果を拒否するのは不合理ではありません。

同様に、OPの場合、アプリケーションがアップストリームサービスからの有効な入力のみを受け付けるようにする必要があります。うまくいかないときに何をするかはあなた次第であり、達成しようとしている実際のビジネス機能に大きく依存しますが、最低限、後でデバッグするためにログに記録し、そうでなければアプリケーションが動かないようにします回復不能または安全でない状態になります。

誰か/何かがあなたに与えるかもしれないすべての可能な入力を決して知ることはできませんが、ビジネス要件に基づいて許容できるものを制限し、それに基づいて何らかの形式の入力ホワイトリストを作成することは確かです。


20
qvスタンドとは何ですか?
JonH

15
@JonHは基本的に「参照」もしています... Targetハックは、彼がen.oxforddictionaries.com/definition/qvを参照している例です 。
アンドリューウェーバー

8
この答えは、それが意味をなさないということです。サードパーティのライブラリが誤動作する可能性のあるすべての方法を予測することは不可能です。ライブラリ関数のドキュメントで、結果に常にいくつかのプロパティが含まれることが明示的に保証されている場合、設計者がこのプロパティが実際に保持されることを信頼できるはずです。この種のことをチェックするテストスイートを用意し、そうでない場合にバグ修正を提出するのは彼らの責任です。あなた独自のコードでこれらのプロパティをチェックするには、DRY原則に違反しています。
1

23
@leftaroundabout no、ただし、アプリケーションが残りを受け入れたり拒否したりできるすべての有効なものを予測できるはずです。
ポール

10
@leftaroundaboutそれはすべてを信用することではなく、外部の信頼できないソースを信用することです。これは、脅威のモデリングに関するものです。ソフトウェアが安全でないことをまだ実行していない場合(アプリケーションを保護するためにどんな種類の攻撃者や脅威に対しても考えたことがない場合はどうでしょうか)。工場のビジネスソフトウェアを実行する場合、発信者が悪意がある可能性があると想定するのが合理的なデフォルトですが、OSが脅威であると想定することはめったにありません。
Voo

33

はい、もちろん。しかし、答えが異なると思われるのはなぜですか?

確かに、APIがコントラクトの内容を返さない場合に、プログラムを何らかの予測不可能な方法で動作させたくはありませんか?だから、少なくともあなたは何とかそのような行動に対処する必要があります。エラー処理の最小限の形式は常に(非常に最小限の!)努力の価値があり、このようなものを実装しないことの弁解は絶対にありません。

ただし、このようなケースに対処するためにどれだけの労力を費やす必要があるかは、ケースに大きく依存しており、システムのコンテキストでのみ回答できます。多くの場合、短いログエントリとアプリケーションの正常な終了で十分です。場合によっては、詳細な例外処理を実装し、さまざまな形式の「誤った」戻り値を処理したほうがよい場合があり、場合によってはフォールバック戦略を実装する必要があります。

ただし、10人未満で使用する社内スプレッドシートの書式設定アプリケーションをいくつか作成し、アプリケーションのクラッシュによる経済的影響が非常に低い場合、または新しい自動運転車を作成する場合は、大きな違いになります。システム、アプリケーションのクラッシュが命を奪う可能性があります。

そのため、あなたがしていることを反映することに対する近道はありません。常識を使用することは常に必須です。


何をすべきかは別の決定です。フェールオーバーソリューションがあります。非同期ログは、例外ログ(またはデッドレター)を作成する前に再試行できます。問題が解決しない場合は、ベンダーまたはプロバイダーへのアクティブなアラートがオプションになる場合があります。
mckenzm

@mckenzm:文字通りの答えが明らかに「はい」だけである可能性があるOPが質問をするという事実は、文字通りの答えに興味がないだけの兆候です。彼らは「APIからの予期しない値のさまざまな形式から保護し、それらを異なる方法で処理する必要がある」と彼らは尋ねているように見えますか?
Doc Brown

1
うーん、がらくた/コイ/ダイのアプローチ。悪い(しかし合法的な)リクエストを渡すのは私たちのせいですか?応答は可能ですが、特に私たちには使用できませんか?または応答が壊れていますか?さまざまなシナリオ、今では宿題のように聞こえます。
mckenzm

21

ロバストネスの原則、具体的には、「受け入れるものに寛容であること」の半分は、ソフトウェアでは非常に悪い考えです。もともとは、物理的な制約によりエンジニアリングの許容範囲が非常に重要になるハードウェアのコンテキストで開発されましたが、ソフトウェアでは、誰かが不正な入力や不適切な入力を送信した場合、2つの選択肢があります。それを拒否するか(できれば何が悪いのかを説明して)、それが何を意味するのかを理解することができます。

編集:私は上記のステートメントで間違っていたことが判明。Robustness Principleは、ハードウェアの世界からではなく、インターネットアーキテクチャ、特にRFC 1958から来ています。状態:

3.9送信時は厳しく、受信時は寛容にします。実装は、ネットワークに送信するときに仕様に正確に準拠し、ネットワークからの誤った入力を許容する必要があります。疑わしい場合は、仕様で要求されない限り、エラーメッセージを返さずに、エラーのある入力を静かに破棄します。

はっきり言って、これは最初から最後まで単に間違っています。この投稿で示されている理由により、「エラーメッセージを返さずにエラーのある入力を静かに破棄する」よりも、エラー処理の誤った概念を考えることは困難です。

この点の詳細については、IETFの論文「ロバストネス原則の有害な結果」も参照してください。

決して、決して、決してあなたのプロジェクトで投げるために、Googleの検索チームと同等のリソースを持っていない限り、それはそれは、その特定の問題領域でまともな仕事には何の近くを行うコンピュータプログラムを思い付くために必要なものだから、その2番目のオプションを選択していません。(それでも、Googleの提案は、左半分のフィールドから約半分の時間でまっすぐに出てきているように感じます。)そうしようとすると、プログラムが頻繁に解釈しようとする大きな頭痛です。送信者が実際に意味するのがYであった場合のXとしての不適切な入力

これには2つの理由があります。明らかなのは、システムに不正なデータがあるためです。あまり明らかではないのは、多くの場合、あなたも送信者も、何かがあなたの顔に爆発したときにずっと先に何かがうまくいかなかったことに気づかないということです。そして、突然、あなたは修正するための大きな、高価な混乱を持っています目立った効果が根本的な原因からこれまでのところ削除されたため、何が間違っていたのか

これが、Fail Fast原則が存在する理由です。APIに適用することで、頭痛にかかわるすべての人を救うことができます。


7
私はあなたが言っていることの原則に同意しますが、あなたはWRTをロバストネス原則の意図と間違えていると思います。「悪いデータを受け入れる」、「良いデータを過度にいじるな」という意味であることを意図したのを見たことがない。たとえば、入力がCSVファイルである場合、ロバストネスの原理は、予期しない形式の日付を解析しようとする有効な引数ではありませんが、ヘッダー行から列の順序を推測することは良い考えであるという引数をサポートします。
モルゲン

9
@Morgen:堅牢性の原則を使用して、ブラウザーはややゆるいHTMLを受け入れ、ブラウザーが適切なHTMLを要求した場合に比べて展開されたWebサイトが非常にゆるやかになります。ただし、そこでの問題の大きな部分は、人間が生成したコンテンツと機械が生成したコンテンツに共通の形式を使用することでした。
スーパーキャット

9
@supercat:それにもかかわらず-またはそれだけ-HTMLとWWWは非常に成功しました;-)
Doc Brown

11
@DocBrown:本当に恐ろしいものの多くは、多くの影響力を持つ人が特定の最小限の基準を満たすものを採用する必要があるときに利用可能になった最初のアプローチであったため、標準になりました。より良いものを選択するには遅すぎます。
スーパーキャット

5
@supercatまさに。たとえば、JavaScriptがすぐに頭に浮かびます...
Mason Wheeler

13

一般に、実用的な場合は、少なくとも次の制約を維持するようにコードを構築する必要があります。

  1. 正しい入力が与えられると、正しい出力が生成されます。

  2. 有効な入力(正しい場合も正しくない場合もある)が与えられると、有効な出力を生成します(同様に)。

  3. 無効な入力が与えられた場合、通常の入力によって引き起こされる副作用またはエラーのシグナルとして定義されている副作用を超える副作用なしにそれを処理します。

多くの状況で、プログラムは、有効かどうかを特に気にすることなく、本質的にさまざまなデータの塊を通過します。そのようなチャンクに無効なデータが含まれている場合、結果としてプログラムの出力に無効なデータが含まれている可能性があります。プログラムがすべてのデータを検証するように特別に設計されており、無効な入力が与えられても無効な出力が生成されないことを保証しない限り、その出力を処理するプログラムはその中に無効なデータが含まれる可能性を考慮すべきです。

データを早期に検証することが望ましい場合がよくありますが、常に特に実用的とは限りません。とりわけ、あるデータチャンクの有効性が他のチャンクの内容に依存しており、ステップのシーケンスに入力されたデータの大部分が途中でフィルターされ、検証がデータを通過する場合に検証を制限する場合すべてのステージで、すべてを検証するよりもはるかに優れたパフォーマンスが得られる場合があります。

さらに、プログラムに事前検証済みのデータのみが提供されると予想される場合でも、実用的な場合常に上記の制約を維持することをお勧めします。すべての処理ステップで完全な検証を繰り返すと、多くの場合、パフォーマンスが大幅に低下しますが、上記の制約を維持するために必要な限られた量の検証ははるかに安価です。


次に、API呼び出しの結果が「入力」であるかどうかを決定します。
マストフ

@mastov:多くの質問に対する答えは、「入力」と「観測可能な動作」/「出力」をどのように定義するかに依存します。プログラムの目的がファイルに保存された数字を処理することである場合、その入力は数字のシーケンス(この場合は数字ではないものは入力できない)、またはファイル(その場合は何かファイルに表示される可能性があります)。
supercat

3

2つのシナリオを比較して、結論に達しましょう。

シナリオ1 アプリケーションは、外部APIが契約どおりに動作することを前提としています。

シナリオ2この アプリケーションでは、外部APIが誤動作する可能性があると想定しているため、予防措置を追加します。

一般に、APIまたはソフトウェアが契約に違反する可能性があります。バグまたは予期しない状況が原因である可能性があります。APIでさえ、内部システムに問題があり、予期しない結果が生じる場合があります。

外部APIが契約に準拠し、予防策の追加を回避することを想定してプログラムが作成されている場合; 問題に直面しているのは誰ですか?統合コードを作成したのは私たちです。

たとえば、選択したヌル値。API契約に従って、応答にnull以外の値を含める必要があるとします。しかし、突然違反すると、プログラムはNPEになります。

そのため、予期しないシナリオに対処するために、アプリケーションに追加のコードを追加することをお勧めします。


1

ユーザーが入力したデータやその他のデータを常に検証する必要があるため、この外部APIから取得したデータが無効な場合に処理するプロセスを用意する必要があります。

一般的に言えば、組織外システムが出会う継ぎ目は、認証、許可(認証だけで定義されていない場合)、および検証が必要です。


1

一般的に、はい、あなたは常に欠陥のある入力に対してガードする必要がありますが、APIの種類によって「ガード」は異なることを意味します。

サーバーへの外部APIの場合、サーバーの状態をクラッシュまたは危険にさらすコマンドを誤って作成したくないので、それを防ぐ必要があります。

コンテナクラス(リスト、ベクターなど)のようなAPIの場合、例外をスローすることは完全に素晴らしい結果であり、クラスインスタンスの状態の妥協はある程度許容される場合があります(たとえば、障害のある比較演算子を備えたソートされたコンテナはソートされます)、アプリケーションのクラッシュも許容される場合がありますが、アプリケーションの状態を損なうこと-クラスインスタンスに関係のないランダムなメモリロケーションへの書き込み-はほとんどの場合そうではありません。


0

わずかに異なる意見を述べるために:契約に違反していても、与えられたデータをそのまま使用することは許容できると思います。これは使用法に依存します:それはあなたにとって文字列でなければならないものであるか、あなたが単に表示している/使用しないなどです。後者の場合、単にそれを受け入れます。別のAPIによって配信されるデータの1%だけを必要とするAPIがあります。99%に含まれるデータの種類をあまり気にすることはできなかったので、決してチェックしません。

「入力を十分にチェックしていないためエラーが発生している」と「厳しすぎるため有効なデータを拒否する」とのバランスを取る必要があります。


2
「別のAPIによって配信されるデータの1%だけを必要とするAPIがあります。」これにより、APIが実際に必要なデータの100倍のデータを必要とする理由が明らかになります。渡すために不透明なデータを保存する必要がある場合、それが何であるかを特定する必要はなく、特定の形式で宣言する必要もありません。その場合、呼び出し元は契約に違反しません。 。
Voo

1
@Voo-私は、彼らが何らかの外部API(「都市Xの天気の詳細を取得」など)を呼び出し、必要なデータ(「現在の温度」)をチェリーピッキングし、返される残りのデータ(「降雨」 「、「風」、「予測温度」、「風の寒さ」など)
1

@ChristianSauer-私はあなたがより広いコンセンサスからそれほど遠くないと思います-あなたが使用するデータの1%はチェックする意味がありますが、あなたが必ずしも99%チェックする必要はありません。コードをトリップする可能性があるものだけをチェックする必要があります。
ストボー

0

これに関する私の見解は、システムへのすべての入力を常に、常にチェックすることです。つまり、私のプログラムが使用しない場合でも、APIから返されるすべてのパラメーターをチェックする必要があります。APIに送信するすべてのパラメーターの正確性も確認する傾向があります。このルールには2つの例外しかありません。以下を参照してください。

テストの理由は、何らかの理由でAPI /入力が正しくない場合、プログラムは何にも依存できないためです。多分私のプログラムは、私が信じているものとは異なる何かをする古いバージョンのAPIにリンクされていたのでしょうか?たぶん私のプログラムは、これまでにない外部プログラムのバグにつまずいたかもしれません。さらに悪いことに、常に発生しますが、誰も気にしません!外部プログラムがハッカーにだまされて、自分のプログラムやシステムを傷つける可能性のあるものを返すのではないでしょうか?

私の世界ですべてをテストするための2つの例外は次のとおりです。

  1. パフォーマンスを慎重に測定した後のパフォーマンス:

    • 測定する前に最適化しないでください。すべての入力/返されたデータのテストは、実際の呼び出しと比較して非常に短い時間で行われることがほとんどであるため、削除してもほとんど、またはまったく節約されません。エラー検出コードはそのままにしておきますが、おそらくマクロによって、または単にコメント化してコメントアウトします。
  2. エラーが発生した場合の対処法がわからない場合

    • あなたのデザインがあなたが見つけたであろう種類のエラーのハンドリングを単に許可しない時があります。たぶんあなたがすべきことはエラーを記録することですが、システムにはエラーの記録はありません。ほとんどの場合、少なくとも開発者として後でエラーをチェックできるように、エラーを「記憶」する方法を見つけることができます。エラーカウンタは、ログを記録しないことを選択した場合でも、システムに保持するのに適しています。

入力/戻り値を正確にどのくらい注意深くチェックするかは重要な問題です。たとえば、APIが文字列を返すと言われている場合、次のことを確認します。

  • 実際にデータ型は文字列です

  • その長さは最小値と最大値の間です。私のプログラムが処理できる最大サイズの文字列を常に確認してください(ネットワークシステムでは、大きすぎる文字列を返すことは古典的なセキュリティ問題です)。

  • 関連する場合は、一部の文字列で「不正な」文字またはコンテンツをチェックする必要があります。プログラムが文字列を送信して後でデータベースを言う場合、データベース攻撃をチェックすることをお勧めします(SQLインジェクションを検索)。これらのテストは、システムの境界で最もよく行われます。攻撃がどこから来たかを特定でき、早期に失敗する可能性があります。後で文字列を結合する場合、完全なSQLインジェクションテストを実行するのは難しい可能性があるため、データベースを呼び出す前にテストを実行する必要がありますが、問題を早期に発見できる場合は便利です。

APIに送信するパラメーターをテストする理由は、正しい結果が返されることを確認するためです。繰り返しますが、APIを呼び出す前にこれらのテストを実行することは不要に思えるかもしれませんが、パフォーマンスはほとんど必要なく、プログラムでエラーをキャッチする可能性があります。したがって、テストはシステムを開発するときに最も価値があります(しかし、今日ではすべてのシステムが継続的に開発されているようです)。パラメーターに応じて、テストは多かれ少なかれ徹底的に行われますが、私のプログラムで作成できるほとんどのパラメーターに許容可能な最小値と最大値を設定できることがよくあります。おそらく、文字列は常に少なくとも2文字で、最大2000文字でなければなりませんか?私のプログラムはいくつかのパラメーターの全範囲を決して使用しないことを知っているので、最小値と最大値はAPIが許可する範囲内でなければなりません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.