localStorageのオンライン実装

Safariのプライベートモードが、バックアップ、特定のドメインからのデータへのアクセス、およびストアの書き込みとクリーニングからのパスワード保護を備えたNode.js上の単純なキーと値のストアの開発につながった方法を共有したいと思います。



それはすべて、人気のあるリソースのiframeを介して組み込まれたWebアプリケーションにテストオーダーを実装するタスクを私に与えたという事実から始まりました。

問題は解決され、次のように機能しました。

  1. 権限のないユーザーがストアをクリックする(リンク "_blank");
  2. テスト商品は新しいウィンドウに表示され、iframeでユーザーをテストユーザープロファイルにリダイレクトし、localStorageに購入データが表示されるのを待ちます。
  3. 購入後、そのデータはlocalStorageに保存されます(金額、数量、店舗、購入時間、ボーナスの数)
  4. iframeでは、テスト購入データがlocalStorageに表示されると、「購入履歴」ブロックに情報が表示されます。

ほとんどがすべてのブラウザーで動作し、IE11でも動作しましたが、Safariでは動作しませんでした。Safariのセキュリティポリシー(ポルノモードとして知られています)では、iframe内および外部(同じウィンドウ内)の同じドメインのlocalStorageデータへのアクセスが許可されていませんでした。

中間データをどこかに格納する必要があります。開発者のバックエンドを引き付けてこのタスクのデータを格納するためのAPIを作成するには、許可を受け取りませんでした。必要なのは、各ユーザー用のトークンを作成できるオンラインストレージを見つけることだけでした。

検索により、私はサービスkeyvalue.xyzに移動しました。これにより、キーを作成し、データを読み書きできます。 そのため、テストオーダーを試行してトークンを作成し、urlパラメーターでトークンを新しいウィンドウに渡すことを決定した各ユーザーに対して開始し、テストオーダーが成功すると、データをリポジトリに書き込み、iframeで既にデータが表示されるまで定期的にデータを要求しました。

すべてはうまくいきましたが、ここではテスターからメッセージが来ました。今回は、adblockをオンにした状態でテストオーダーが機能しないと言いました。 それで、コンソールでadblockがリソースへのリクエストがブロックされていると書きました。 私はサービスの開発者にミラーを作成するよう依頼しましたが、彼らは答えませんでした。nginx(proxy_pass)を介してミラーを作成しようとしましたが、おそらくcloudflareフィルターが原因で解決しませんでした。

それは快適ではありませんでした、状況から抜け出すことが必要でした。

単純なキーを記述することにしました。ストレージの値はlo​​calStorageに似ており、バックアップ、特定のドメインからのアクセス、書き込みに対するパスワード保護、およびそれを操作するための便利なライブラリがあります。

開発


Node.jsでexpressを使用して簡単なREST APIを記述することは難しくありません。データを保存するためにMongoDBを選択しました。サイズ(100〜200GB)。

開発について詳しく説明するのは意味がありません。非常に簡単で、私たちのほとんどは既にエクスプレスフレームワークを使用しています。

基本的なストレージ要件から始めましょう。

  1. トークン作成
  2. トークンの更新
  3. ストレージから価値を引き出す
  4. ストレージ全体を取得する
  5. データ記録
  6. アイテムを削除
  7. ストレージのクリーンアップ
  8. バックアップのリストを取得する
  9. バックアップからストレージを復元する

トークンスキームは、次のように非常に単純です。

const TokenSchema = new db.mongoose.Schema({ token: { type: String, required: [true, "tokenRequired"] }, connect: { type: String, required: [true, "connectRequired"] }, refreshToken: { type: String, required: [true, "refreshTokenRequired"] }, domains: { type: Array, default: [] }, backup: { type: Boolean, default: false }, password: { type: String }, }) 

追加オプション:
トークンストレージへのアクセスに使用
つなぐストレージをトークンに関連付けるためのプロパティ
refreshTokenケースのトークン更新
トークンまたは点灯しているトークンを更新する必要がある場合、
例:git commit
ドメインドメインの配列、許可されているリポジトリへのアクセス。
Origin HTTPヘッダーは検証に使用されます
バックアップtrueに設定すると、2時間ごとに実行されます
ストレージ全体をバックアップし、
つまり、日中は常にいくつかのバックアップが利用可能です。
ロールバックできる場所
パスワード書き込みおよび削除用のパスワードの設定

POSTリクエストハンドラ/作成
  app.post('/create', async (req, res) => { try { // Additional storage protection data const { domains, backup, password } = req.body // New unique uuid token const token = uuid.v4() // A unique identifier for connecting the token to the storage // as well as using it you can update the token const connect = uuid.v1() // Default const tokenParam = { token: token, connect: connect, refreshToken: connect } // The list of domains for accessing the repository if (domains) { // If an array is passed, store it as it is if (Array.isArray(domains)) tokenParam.domains = domains // If a string is passed, wrap it in an array if (typeof domains === 'string') tokenParam.domains = [domains] // If passed boolean true, save the host if (typeof domains === 'boolean') tokenParam.domains = [req.hostname] } // Availability of backup if (backup) tokenParam.backup = true // If a password is sent, we save it if (password) tokenParam.password = md5(password) // Save to db await new TokenModel.Token(tokenParam).save() // Sending the token to the client res.json({ status: true, data: tokenParam }) } catch (e) { res.status(500).send({ status: false, description: 'Error: There was an error creating the token' }) } }) 


curlコマンドとは異なり、そのコードは非常に簡潔で理解しやすいaxiosライブラリを使用してリクエストの例を示します。

 axios.post('https://storage.hazratgs.com/create', { domains: ['example.com', 'google.com'], backup: true, password: 'qwerty' }) 

実行の結果として、ストレージからのデータの書き込みと読み取りに使用できるトークンを含む応答を取得します。

 { "status": true, "data":{ "token": "002cac23-aa8b-4803-a94f-3888020fa0df", "refreshToken": "5bf365e0-1fc0-11e8-85d2-3f7a9c4f742e", "domains": ["example.com", "google.com"], "backup": true, "password": "d8578edf8458ce06fbc5bb76a58c5ca4" } } 

リポジトリへのデータの書き込み:

 axios.post('https://storage.hazratgs.com/002cac23-aa8b-4803-a94f-3888020fa0df/set', { name: 'hazratgs', age: 25, city: 'Derbent' skills: ['javascript', 'react+redux', 'nodejs', 'mongodb'] }) 

ストレージからアイテムを取得する:

 axios.get('https://storage.hazratgs.com/002cac23-aa8b-4803-a94f-3888020fa0df/get/name') 

その結果、以下が得られます。

 { "status": true, "data": "hazratgs" } 

GitHubのプロジェクトページで他の例を見ることができます。

リポジトリのクローンを作成し、自分用にリポジトリをデプロイできます。詳細な手順はプロジェクトリポジトリにあります。

便宜上、 JavaScriptライブラリPythonライブラリが利用可能です。
保管庫自体はstorage.hazratgs.comで入手できます

ライブラリの例


そして、すでにリポジトリとそれを操作するためのライブラリがあります。ライブラリをインストールしましょう。

 npm i online-storage 

プロジェクトにインポートします。

 import onlineStorage from 'online-storage' 

ローカルストレージの実装をオンラインで行うため、プロジェクト全体で1つのデータソースを操作するために、プロジェクトコードではクラスではなくオブジェクトを返すため、複数のオブジェクトが必要な場合は、OnlineStorageクラス自体をインポートできます。それに基づいて好きなだけオブジェクトを作成します。

トークンを作成します。

 onlineStorage.create() 

onlineStorageは、onlineStorageオブジェクトのほぼすべてのメソッドと同様に非同期メソッドであるため、async / await構文を使用するのが最善の選択肢であると言わなければなりません。

トークンを作成した後、トークンプロパティに書き込まれ、必要に応じて、たとえばデータの書き込みなどに置き換えられます。

 await onlineStorage.set({ name: 'hazratgs', age: 25, city: 'Derbent' skills: ['javascript', 'react+redux', 'nodejs', 'mongodb'] }) 

データの読み取り:

 const order = await onlineStorage.get('name') // hazratgs 

プロパティの削除:

 await onlineStorage.remove('name') 

これで、localStorageなどのオンライン実装があると安全に言えます。したがって、localStorageは文字列でのみ動作し、ストレージは動作し、文字列、数値、オブジェクト、論理型を格納できます。

おわりに


その結果、バックアップ、特定のドメインからのアクセス、およびパスワード保護を備えたデータストアがありますが、このストアはlocalStorageのように安全ではなく、プロジェクトの操作性、ユーザーデータ、あなたのアプリケーションに害を及ぼす可能性のある多くのもの。

私の例のように、無関係の公開データに対してのみ使用し、テスト購入データをウィンドウからiframeに転送します。

トークンは、私たちがクライアントに送信するすべてのjavascriptと同様に、ストレージ全体のデータを取得するのが難しくないという事実のために発生する可能性があり、特にストレージの問題ではありません。特定のドメインとパスワードを使用する場合、これはすべて回避される可能性があります。

もちろん、トークンを非表示にするために、サーバー上のapiにラッパーを書くことができますが、これはすでに当てはまります。独自のデータベースをセットアップする方が簡単です。

あまりscらないでください。これは私の最初の出版物であり、オープンソースへの貢献です。
脆弱性、ヒント、プルリクエストの排除にご協力いただきありがとうございます。

Source: https://habr.com/ru/post/J351352/


All Articles