close search bar

Sorry, not available in this language yet

close language selection
 

アプリケーションを保護するためのJavaScriptセキュリティのベスト・プラクティス

JavaScript, like any programming language, is not without security challenges. Use these JavaScript security best practices to write more secure code.

JavaScript is one of the most popular programming languages, largely because it is an easy language for beginners. It’s easy to set up, has a large and active community, and allows users to create web, mobile, and desktop applications using only JavaScript.

However, as with any programming language, malicious actors find vulnerabilities in JavaScript applications and attempt to exploit them. Common vulnerabilities include cross-site scripting, sensitive data disclosure, Broken Access Control, session hijacking, CSRF, and man-in-the-middle attacks. In this blog post, I’ll share JavaScript security best practices to protect your applications.

Browse the JavaScript Security course

1. Write quality code

There are many different programming languages, and a good understanding of them is crucial to writing quality code. The U.S. Department of Homeland Security reports that “up to 90% of computer security incidents result from software vulnerabilities exploited by attackers.” It is therefore clear that developers need to improve their code quality to reduce the number of bugs that lead to security breaches. Tips to help reduce bugs include:

  • Learn important concepts of the JavaScript language.
    Familiarity with global context, scoping, loose equality operators, strict equality operators, hoists, callbacks, and more is critical to writing quality code.
  • Avoid using functions that evaluate strings as codes.
    JavaScript functions such as eval , Function , setTimeout , and setInterval are not recommended as they can lead to cross-site scripting (XSS) attacks when used with untrusted data .
  • Use linters to identify problems early.
    A linter is a tool that analyzes your source code for typos, logic errors, and code smells. In short, it helps improve your code. We recommend the ESLint linter as it is extensible and easy to get started with.
  • 静的アプリケーション・セキュリティ・テスト(SAST)ツールを使用して、品質およびセキュリティ上の問題を検出する。
    Synopsys Coverity®のようなSASTツールをお勧めします。このツールは、セキュリティ上の問題とコンプライアンス標準に対してより深いサポートを提供します。Synopsys Code Sight™ SEの機能であるRapid Scan Staticを使用することもできます。これを使用すると、チームは開発者のワークフローを妨げることなく、IDEベースのAppSecテストをリアルタイムで実施することができます。VS CodeおよびIntelliJで使用可能なCode Sightプラグインにより、開発者はコードを記述しながら修正を確認できるため、後行程の作業を回避することができます。
  • サイレント障害を明白な障害に変える。
    ECMAScript 5では、厳密モードがオプション機能として導入されています。この機能を使用して、サイレント障害を明白な障害に変えます。厳密モードの使用時に生じる変化の詳細なリストについては、このページを参照してください。
  • 考え方の不具合を検出するテストを作成する。
    あなたの考え方、あなたのコード、および期待がすべて一致することを裏付けるテストを作成します。これは、文書化ツールとしても役立ちます。

2. サードパーティー製ライブラリの必要性を評価する

コードの再利用は優れた実践方法と見なされており、JavaScriptの開発者は、この考えを極端に推し進め、きわめて簡単なタスクにもパッケージを作成しています。しかし、管理されていない方法でパッケージを再利用することは、JavaScriptアプリケーションが問題やセキュリティ上の脅威にさらされることになります。依存関係を、プロジェクトの実行に必要なコードとして扱うことを検討してください。必要性が高まるほど、障害の発生個所が増える可能性があります。

次の事例は、そのことをよく表しています。2016年3月22日、基本的なleft-pad string関数(12行のみのコード)を実装する、一般に使用されているパッケージが削除されました。この一見無害に見えるシンプルなパッケージが削除されたことによる依存関係の連鎖反応は、React、Babel、その他の知名度の高いパッケージなどのWeb開発エコシステムに大打撃を与えました。

この事例から得た教訓は、プロジェクトはその最も脆弱な依存関係と同じぐらい脆弱だということです。よく知られているセキュアなライブラリとフレームワークを使用することが、自分自身を守ることになります。よく知られていないサードパーティー製パッケージを使用する際は、誰が開発したのか、パッケージはメンテナンスされているか、非アクティブか、十分にテストされているか、パッチが適用されていない脆弱性が含まれていないかなどを調べます。必ず、適切なパッケージをインストールしているかを確認してください。よく知られているパッケージと似た名前の悪意のあるパッケージが実際のアプリケーションに侵入する「タイポスクワッティング攻撃」はよく発生します。

ソフトウェア・コンポジション解析(SCA)ツールを使用して、オープンソース・ソフトウェアのライセンス違反、脆弱性、および古い依存関係を検出することもできます。Black Duck®は、これらの問題の検出に役立つソフトウェア・コンポジション解析ツールです。

3. ユーザー入力を信頼しない

ほとんどのWebアプリケーションでは、ユーザーがテキスト入力でデータを挿入できます。データが挿入されると、Webアプリケーションのどこかに反映されます。しかし、ユーザーからの入力を受け入れて表示することは、サイバー犯罪者が特別な文字を使用してブラウザを欺き、テキストをHTMLマークアップやJavaScriptコードとして解釈させる、クロスサイト・スクリプティング攻撃を可能にすることになります。

出力エンコーディングは、潜在的に危険な文字を安全な形に変換します。使用するエンコーディングのメカニズムは、信頼できない入力データが置かれる場所によって異なります。Open Web Application Security Project(OWASP)が認めているエンコーディングには、HTMLエンティティ・エンコーディング、HTML属性エンコーディング、URLエンコーディング、JavaScript文字列エンコーディング、CSS Hexがあります。使用するエンコーディングのタイプは、入力データが置かれる場所によって異なるため、これらのコンテキストに応じたエンコーディングを、使用しているフレームワークに合わせることをお勧めします。フレームワークを使用していない場合、OWASPは、エンコーディングが適切に実装されるように、セキュリティに重点を置いたエンコーディング・ライブラリの使用を推奨しています。

ユーザーが挿入したHTMLをWebアプリケーションで受け入れる必要がある場合は、それをページで表示したり、他のシステムに送信したりする前に、入力をサニタイズします。HTMLのサニタイズには、入力妥当性の検証と予期しない文字の消去が必要です。その結果、HTML入力の安全なHTMLバージョンになります。HTMLをサニタイズするための優れたツールはDOMPurifyです。

クロスサイト・スクリプティング攻撃を減らすために、出力エンコーディングの適用に加えて、入力をサニタイズします。

4. JSONインジェクションから身を守る

JSONは、アプリケーション間で情報を交換するためによく使われる構文です。これはシンプルかつコンパクトで、習得、読み取り、理解が容易であり、階層構造になっています。

インジェクション攻撃は、攻撃者が、妥当性検証やサニタイズを行わずに、信頼できない入力をプログラムやアプリケーションに供給する場合に発生します。JSONインジェクション攻撃は、サーバー側またはクライアント側に影響を与える可能性があります。たとえば、サーバー側のコードによって文字列からJSONオブジェクトが作成される場合、その文字列は、ユーザーが指定した入力を連結することで作成されます。妥当性検証やサニタイズが行われない、単純な文字列の連結は、機密情報の漏えいや不正行為を招く可能性があります。

クライアント側では、連結された文字列が、evalFunctionsetTimeoutなどのコードを評価する関数内で実行される場合、このような攻撃はクロスサイト・スクリプティングにつながる可能性があります。以下に例を示します。

```
//場所: http://my-website.com/view?username=ENTER_USERNAME
const params = new URLSearchParams(document.location.search);
 
//検索パラメータから入力を取得します 入力の妥当性検証やサニタイズは行われません 
const usernameFromQueryParams=params.get('username');
 
// ユーザー入力からJSONオブジェクト文字列を作成します
const bannerJSONAsString = `{ "greetings": "Hello ${usernameFromQueryParams}"}`
 
//文字列をコードとして評価する関数を使用する
const result = eval("(" + bannerJSONAsString + ")");
document.getElementById("#banner").innerText = result. greetings;

この例では、攻撃者は以下を挿入することによって、ユーザーcookieにアラートを生成できます。

`test"});alert(document.cookie);({"a":"b`を

この攻撃ベクトルを防御するには、信頼できない入力の妥当性検証とサニタイズを行い、文字列をコードとして評価する関数の使用を避けます。さらに、eval()の代わりにJSON.parse()関数を使用してJSON文字列を解析し、コンテンツ・セキュリティ・ポリシーを設定して文字列をコードとして評価する関数の使用を制限します。

5. cookieを保護する

HTTP cookieは、認証またはセッション・トークン、ユーザー設定など、サーバーがリクエストの間に記憶しておくべき情報を保存するために使用されます。HTTP cookieは、Webサーバーが、レスポンスのSet-Cookie HTTPヘッダーを使用して、ブラウザに送信する小さなデータ文字列です。その後、ブラウザは、Cookie HTTPヘッダーを使用して(同じドメインへの)ほぼすべてのリクエストに対して自動的にcookieを送り返します。

フラグを設定しない場合は、document.cookieを使用してプログラムによって、cookieの内容にアクセスできます。これは必ずしも望ましいものではありません。攻撃者がWebアプリケーション内にJavaScriptを挿入できれば、このスクリプトでdocument.cookieの内容を読み取ることができるため、悪意のある人物がcookie内の機密情報にアクセスできるようになる可能性があります。

cookieを保護するには、さまざまな方法があります。cookieがWebサーバーによってのみ使用される場合は、httpOnlyフラグを使用して、cookieの内容へのプログラムによるアクセスを制限することができます。

HTTPはメッセージを暗号化しないため、中間者攻撃が成功し、メッセージが傍受される可能性があります。cookieに機密情報が含まれている場合は、ブラウザが暗号化されていないHTTP接続でcookieを送信しないよう制限します。secureフラグを使用して、HTTPのプロトコル拡張であるHTTPSを介してのみcookieを送信するようにブラウザに指示します。このフラグを使用しない場合、ブラウザは、セキュアな接続(https://my-secure-site.com)とセキュアでない接続(http://my-secure-site.com)の両方を使用してサイトにcookieを送信することになります。

クロスサイト・リクエスト・フォージェリ攻撃の場合、Webアプリケーションは有効なリクエストと偽のリクエストを区別できないため、攻撃者は成功する可能性があります。たとえば、ユーザーのパスワードを更新するためのフォームがあり、そのWebサイトがセッションの処理のためにcookieを使用しているとします。攻撃者は、パスワード更新のリクエストを自動的に送信するページを作成することができます。cookiesが保護されておらず、有効なセッションcookieがある場合、このサイトにアクセスすると、パスワード更新のリクエストがサイトから送信されます。セッションcookieが有効になっているため、ブラウザは自動的にcookieをリクエストに含めます。この情報により、Webサーバーは有効なセッションcookieを含むリクエストを受信し、パスワードを更新します。このようにして、攻撃者はあなたのパスワードを自分で選んだ値にリセットすることができます。クロスサイト・リクエスト・フォージェリ攻撃からcookieを保護するには、cookieフラグsamesite=strictを使用して、cookieを設定したサイトとは別のドメインからリクエストが届いた場合はcookieが送信されないようにします。

6. プロトタイプ汚染を防御する

JavaScriptはプロトタイプベースの言語です。オブジェクトは、生成されると、いわゆるプロトタイプ・チェーンに従ってすべてのプロパティとメソッドを継承します。それはチェーンであるため、プロトタイプは他のプロトタイプへの参照を使用します。このチェーンは、nullに到達するまで続きます。このプロトタイプ・チェーンにおいて、Objectプロトタイプはnullのすぐ下に位置します。JavaScriptのほとんどすべてのオブジェクトは、Objectのインスタンスです。

Objectを標的にして、JavaScriptのほとんどのオブジェクトがプロトタイプチェーンを通じて継承するメソッドを変更することをプロトタイプ汚染攻撃と呼びます。この攻撃はサーバー側でもクライアント側でも発生する可能性があります。その結果、リモート・コード実行、クロスサイト・スクリプティング、DoS攻撃などが行われる可能性があります。

クライアント側では、攻撃者がObject.prototypeを直接変更できるコードを挿入します。サーバー側では、アプリケーションがオブジェクトのプロパティを再帰的に複製したり、特定のパスを通じてオブジェクトのプロパティを設定したりします。HTTPリクエストを受け入れるHTTPサーバーを使用している場合、攻撃者は_proto_constructorprototypeプロパティを悪用しようとするJSONオブジェクトを含むHTTPリクエスト・ペイロードを送信する可能性があります。

プロトタイプ汚染攻撃を軽減するにはさまざまな方法があります。

  • `Object.prototype`をフリーズして、デフォルトのプロトタイプが汚染されるのを防ぎます。
    ```
    // これは1回だけ実行されるはずです
    Object.freeze(Object.prototype);
     
    clone(source, target);
    ```
  • Object.create(null)を使用してプロトタイプのないオブジェクトを作成します。これらのタイプのオブジェクトにはプロトタイプがありません。このようにすると、Object.prototypeからメソッドを継承することはありません。
    ```
    const target = Object.create(null);
    clone(source, target);
    ```
  • Objectの代わりにMapを使用します。
  • 安全でない再帰的なマージ関数の使用は避けます。

7. ウィンドウ・オブジェクト間のブラウザ通信を保護する

Webページでは、他のウィンドウやiframeを開いてユーザーに情報を提示することがよくあります。これらのウィンドウとiframeが相互の通信を必要とすることがあります。しかし、両ページでdocument.domainに同じ値を設定して、異なるサブドメインのページ間の通信を可能にすることは、元が同一のポリシーによって提供されるセキュリティ保護が弱体化するため避けなければなりません。代わりに、window.postMessage()メソッドを使用する必要があります。

window.postMessage()メソッドは、ページやポップアップまたはiframeのようなウィンドウ・オブジェクト間での安全な通信を可能にします。これにより、2つのウィンドウは、一方がもう一方を直接管理することなくメッセージを共有できます。より安全な方法ですが、使い方には注意を払う必要があります。たとえば、window.postMessage()targetOriginパラメータを受け入れますが、targetOriginとして*を使用することは避け、提供するURLがHTTPSを使用していることを確認します。メッセージを受信するウィンドウは、受信したメッセージを(メッセージの送信元を確認して)検証するだけでなく、常に送信者の身元を確認する必要があります。window.postMessage()メソッドを通じて届けられる文字列をコードとして実行しないでください。また、メッセージ・イベントを予期していない場合はリッスンしないでください。

8. ネットワーク・デバイス間のセキュアな通信

ユーザーがブラウザにURLを入力してEnterボタンを押すと、HTML、CSS、画像、フォント、その他いくつかのビットが混在した結果がユーザーの画面に表示されます。これらの要素はすべて、他のシステムからコンテンツをダウンロードすることでブラウザに届きます。場合によっては、最初のリクエストですべてが完了することもあります。また、CSS、画像、フォント、JavaScriptライブラリは、その後のリクエストでダウンロードされることもあります。

悪質なユーザーは、機密データへのアクセス、通信の傍受、通信中のデータの改竄を目的として、通信チャネルに侵入しようとします。これらの攻撃は、HTTPの代わりにHTTPSを使用して通信を保護することで、最小限に抑えることができます。HTTPを使用する場合、送信データは暗号化されません。つまり、攻撃者が転送データを傍受する可能性があります。HTTPSを使用する場合、通信はTLSまたはSSLプロトコルで暗号化されます。このプロトコルは、サーバーのIDを確認するためにデジタル証明書を使用し、ネットワーク・デバイス間の通信を暗号化します。SSL/TLS証明書を使用することで、通信におけるプライバシーとデータのインテグリティが確保されます。暗号化されたデータが傍受されたとしても、秘密鍵にアクセスしなければ復号化できません。ネットワーク・デバイス間の通信を保護するために、すべてのリクエストにHTTPSを使用します。

9. 外部リソースを安全に読み込む

Web開発では、静的リソースをコンテンツ・デリバリ・ネットワーク(CDN)にオフロードすることは一般的なプラクティスです。CDNは、ロード時間の短縮、インフラストラクチャ・コストの削減、サイト・パフォーマンスの向上を実現します。しかし、WebサイトでCDNがホストする静的ファイルを使用すれば、そのサイトはサードパーティーのセキュリティを信頼します。もし、攻撃者がCDNを制御して、悪意のあるコンテンツを注入したらどうなるでしょうか。セキュリティ対策を講じなければ、この悪意のあるコンテンツを利用しているWebサイトはそのことに気づかないでしょう。それを示す良い例がNBA.comのWebサイト攻撃で、静的ファイルを保管するために使用していたAmazon S3バケットの1つが侵害されました。MalwareBytesは、この攻撃を2019年6月4日に公表しました。

この攻撃ベクトルを軽減するために、ブラウザはサブリソースのインテグリティ・チェック機能を備えています。この機能により、ブラウザは、外部リソースを参照する要素の一部としてbase64でエンコードされた暗号化ハッシュを含めることによって、ファイルのインテグリティを検証できます。ソース・コードの「既知の良好な」バージョンからハッシュを作成するのは、開発者が担当します。サブリソースのインテグリティ・チェックが行われた外部リソースがダウンロードされると、受信したデータに基づいてbase64でエンコードされたハッシュが生成されます。生成されたこのハッシュ値が開発者によって提供されたものと異なる場合、そのリソースは読み込まれません。

外部リソースをコードに含める際は、サブリソースのインテグリティ・チェックを活用して、安全に読み込んでください。

```
<script src="https://code.jquery.com/jquery-2.1.4.min.js"
integrity="sha384-R4/ztc4ZlRqWjqIuvf6RX5yb/v90qNGx6fS48N0tRxiGkqveZETq72KgDVJCp2TC"
crossorigin="anonymous"></script>
```

10. コンテンツ・セキュリティ・ポリシーを設定する

ブラウザがHTMLコンテンツをダウンロードする際に、CDNからの画像、音声、動画などの他のコンテンツが他のドメインからダウンロードされることがあります。ブラウザは、これらのリクエストが予期せぬものであるかどうかを判断できません。

コンテンツ・セキュリティ・ポリシー(CSP)は、クロスサイト・スクリプティングやその他の攻撃を軽減するのに役立つ追加のセキュリティ層です。CSPは、コンテンツのダウンロード元となるドメインや、使用するプロトコルを制限できます。また、インライン・スクリプト、インライン・イベント・ハンドラーの実行、evalやFunctionなどの文字列をコードとして評価する関数も制限することができます。

CSPのルールはコンテンツ・ソースを制限するため、ルールが誤って構成されていないことを確認する必要があります。構成を誤ると、資産のダウンロード/実行がブロックされるため、Webサイトが適切にレンダリングされない可能性があります。CSPはレポート専用モードで展開することもでき、ポリシーは適用されませんが、違反は指定されたURLにレポートされます。

CSPには多くのポリシーがあるため、その意味を理解し、どのポリシーを使用すべきかを評価し、有効にして、違反に関するレポートを監視する必要があります。クロスサイト・スクリプティングに対抗する多重防御に興味をお持ちの方は、Googleの次の記事をお読みください。

始める

In today’s ever-evolving threat landscape, managing vulnerabilities to keep pace with development can be a daunting task. Adopting good coding practices enables your DevSecOps team to address vulnerabilities early in the software development lifecycle (SDLC) . In addition to JavaScript security best practices, Synopsys e-learning materials provide deeper insight into application security topics.

 

Application Security Best Practices | Synopsys

 
Alberto Fernández Reyes

投稿者

Alberto Fernández Reyes


More from セキュアなソフトウェアの構築