- 投稿日:2021-10-22T21:39:04+09:00
ajax通信とは?jQueryでの実装
ajax通信についてきちんと理解していなかったのでまとめる。 ajaxとは?というところから始まるため、jQueryの実装部分のみ興味がある方はこちらまで読み飛ばしてください。 尚、ajaxの説明についてはこちらの記事が大変わかりやすかったので、踏襲しつつ自分の言葉でまとめたり追加で調べたりしている。 ajaxとは? 「Asynchronous JavaScript XML」の略。 Asynchronousとは、非同時性の、非同期のという意味で、「非同期通信」を実装するためのもの。 非同期通信 画面遷移のない通信のことで、LINEやGoogle Mapなどのサービスが例として挙げられる。 LINEを例に考えると、メッセージを送信した瞬間、画面遷移が発生することなくトークルームに送信したメッセージが表示される。 仮に同期通信であればメッセージを送信するたびに画面遷移が発生するため、毎回ストレスを感じるだろう。 またGoogle Mapでは、どこのエリアを表示しても画面遷移が発生することはなく、ロードのために一時的に画面が白くなるだけで指定のエリアを表示することができる。 同期通信との比較でもう少し考えてみる。 両者の違いは以下。 ・同期通信はリクエストが送られてからレスポンスが返ってくるまで他の処理を実行できない ・非同期通信はリクエストが送られてからレスポンスを待っている間に他の処理を実行することができる LINEで考えると、同期通信の場合はメッセージが送信(リクエスト)された際、 そのメッセージがトークルームに反映(レスポンス)されるまで他の処理が行えないため、画面にロードが走ることになる。 一方、非同期通信の場合、送信したメッセージがトークルームに反映(レスポンス)されるのを待たずして他の処理が行える状態にある(=ロードが走らない)、ということになる。 例えば動画を送信した際、アップロード処理の実行中に追加でメッセージを送信することができる。 これが同期通信であれば、動画の送信が完了するまでロードが走ることになり何もできない。 いつ使うのか 代表例としては前述の通りチャットやマップサービスが挙げられる。 他にも、検索機能において入力内容に応じてリアルタイムでレコメンドを表示させたい場合や、ログの取得目的で、あるボタンがクリックされた際にロードすることなくデータを取得しDBに保存したい場合など、様々な用途で使われる。 ajax通信の仕組み 冒頭で『「Asynchronous JavaScript XML」の略。』と述べたように、大前提としてJavaScriptを使用している。 具体的には、JavaScriptのHTTP通信機能であるXMLHttpRequestを用いている。 XMLHttpRequestとは 非同期なデータの通信を実現するためのAPIのことで、JavaScriptで初めから定義されているオブジェクトである。 具体的なイメージでいうと、JavaScriptのイベント発火によってWebサーバへ新たにHTTPリクエストを送信することができ、サーバーにデータを通知したり、サーバから返送されたHTTPレスポンスをページ上に反映することができるもの。 なんだかわかったようなわからないような、、、 一番しっくりきたのは、「ブラウザ上でサーバーとHTTP通信を行うためのAPI」という表現。 本来サーバーとの通信をする際には、PHPなどのサーバーサイド言語の処理が必要となる。 例えばフォームに入力した内容を指定のパスに飛ばすと、そこで初めてサーバーとの通信が行われるからだ。 一方ブラウザに変化をつけるJavaScriptは、あくまでブラウザに表示された範囲でしか活躍することはできない。 ページ遷移することも、裏側でDBとやりとりすることもできない。 そこで、JavaScriptによってブラウザ上で取得したデータをXML形式のオブジェクトとして送受信することで、サーバーとのやりとりを可能にするのがXMLHttpRequestである。 なぜXML形式のオブジェクトにするのか ここまで調べてみて、なぜXML形式のオブジェクトとして送受信するのか疑問だった。 というかその前にXMLとは、、、?となった。 XMLとは XMLは、文章の見た目や構造を記述するためのマークアップ言語の一種です。 主にデータのやりとりや管理を簡単にする目的で使われ、記述形式がわかりやすいという特徴があります。 https://hnavi.co.jp/knowledge/blog/xml/ XMLの正式名称は「Extensible Markup Language」で、日本語では「拡張可能なマークアップ言語」と訳される。 マークアップ言語といえばHTMLが思いつくが、それと同類とのこと。 実際に見てみたほうが早い。 <?xml version="1.0" encoding="Shift_JIS"?> <?xml-stylesheet type="text/xsl" href="testxsl.xsl"?> <おこづかい帳> <支出> <内容> <日付>1月20日</日付> <交通費>780</交通費> <食費>980</食費> <嗜好品>250</嗜好品> </内容> <内容> <日付>1月21日</日付> <交通費>950</交通費> <食費>1200</食費> <嗜好品>300</嗜好品> </内容> <内容> <日付>1月22日</日付> <交通費>500</交通費> <食費>1500</食費> <嗜好品>250</嗜好品> </内容> </支出> </おこづかい帳> このように、HTMLと同じくタグで文字列を囲って表現している。 大きな違いとしては、タグ名をこちら側で指定できるということだろう。 なぜXMLを使用? XMLがどのようなものかということだけ理解した上で、改めてなぜXMLを使用するのか。 それは、記述方式が世界標準で統一されているため、データをXML化することであらゆるコンピュータシステムに応用ができるから。 JavaScriptでサーバーとやりとりをするためには、JavaScriptではなく共通言語を使う必要があるからである。 JSON 前段でXMLの必要性について見てきたが、実のところ今ではXMLよりJSONが使われている。 ajaxは「Asynchronous JavaScript XML」の略だが、XMLを使用しなくても良いらしい。 JSONもXMLと同じくプログラミング言語問わず利用可能であり、 データをダグで囲んで扱うXMLよりコードの記述量が少なく、データの読み込みが早くなるためJSONが主流となっているようだ。 JSONとは「JavaScript Object Notation」の略で、そのまま訳すと「JavaScriptオブジェクト記法」ということになる。 XMLではデータをタグで囲って扱っていたが、JSONはJavaScriptのオブジェクト形式で扱うことができる。 JavaScriptのオブジェクト形式というのは以下のような形式だ。 let obj = {name: 'taro', age: 20}; JSONもこのような形式で表記される。 一点異なる点があるとすれば、ダブルクオートのみ使用可能ということと、キーもダブルクオートで囲む必要があるということだ。 以下のようになる。 let obj = {"name": "taro", "age": 20}; jQueryによるajax通信の実装 jQueryでajaxを実装するにはajax関数を使用する。 以下3通りの記述法があるがどれでも実装できるため、記述量が一番短い③を使用する。 ①window.jQuery.ajax(プロパティ) ②jQuery.ajax(プロパティ) ③$.ajax(プロパティ) 上記のプロパティの部分に色々と設定することで、「どこに・どんなデータを・どんな形式で送るのか」など他にも様々な指定ができる。 プロパティに記述する項目は決まっているわけではなく、必要に応じて記述する。 以下、具体的なコードで見てみる。 $.ajax({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') //headerの設定(laravel使用時の例) }, type: "POST", //HTTPメソッド url: "example.php", //データの送信先 data: { "name": "John", "location": "Boston" }, //送信するデータ contentType: "application/x-www-form-urlencoded; charset=UTF-8", //送信するデータの型、種類 dataType: "json" //レスポンスの型、種類 }) // Ajaxリクエストが成功(ステータスコード200)した場合の処理 .done(function() { console.log('成功'); }) // Ajaxリクエストが失敗した場合の処理 .fail(function() { console.log('失敗'); }); 上記はあくまでほんの一例に過ぎず、気になる方は調べていただくことをお勧めします。 とはいえ、基本的にはtype、url、dataがあればたいていのことは実装は可能かと思う。 順を追って見ていく。 header リクエストheaderに追加したい要素がある場合に記述する。 例に挙げたものはLaravelでPOSTする際に必須となるもの。 type サーバーへのリクエストのHTTPメソッドを指定する。 デフォルト値はGET。 PUTPATCHDELETE等も指定することができるが、全てのブラウザでサポートされていないため推奨されていない。 url 送信先のファイル名、パス、URLのいずれかを指定。 ファイル名はそのままで、同一プロジェクト内のどのファイルにリクエストを送信するか。 パスはルーティングとの対応を確認する。 URLはhttps://example.comのような形で、外部ドメインへの送信が可能である。(クロスドメインの設定に注意) data リクエスト先に送信するデータを指定。 例に挙げたようにJSON形式で記述する。 変数を指定しても中身がJSON形式となっていれば問題ない。 contentType 送信するデータの型を指定。 デフォルト値はapplication/x-www-form-urlencodedで、基本的にはこのままでまず問題ない。 「タイプ/サブタイプ」という記載の仕方で、「タイプ」でデータの種類(テキスト、画像、動画など)を定義し、「サブタイプ」で具体的なデータ形式を定義している。 text/plain, text/html, application/jsonのようになる。 application/x-www-form-urlencodedは、エンコードされたURLでデータが送受信される。 どうやらPHPの$_POSTなどで受け取るときにこの設定である必要があるみたいだが、 そもそもJSONでデータを送信するんじゃないの?という疑問が解消されない、、、 知見のある方がいらっしゃればコメントいただけると幸いです。 dataType サーバーから返されるデータの型を指定。 省略した場合は、jQueryがMIMEタイプ1などを見ながら自動的に判別する。 dataTypeを指定した場合、何かしらデータが奇形であると判断されるとエラーになるため、MIMEタイプ判定に任せてdataTypeの設定自体をなくすとエラーが解消することもある。 指定可能な値は次のようなもの。 "xml": XMLドキュメント "html": HTMLをテキストデータとして受け取る。ここにscriptタグが含まれた場合、処理は実行されます。 "script": JavaScriptコードをテキストデータとして受け取る。cacheオプションに特に指定が無ければ、キャッシュは自動的に無効になります。リモートドメインに対するリクエストの場合、POSTはGETに変換されます。 "json": JSON形式のデータとして評価し、JavaScriptのオブジェクトに変換します。 "jsonp": JSONPとしてリクエストを呼び、callbackパラメータで指定した関数に呼び戻された値をJSONデータとして処理します。(jQuery 1.2より追加) "text": 通常の文字列。 以上のような内容を適宜設定し実装することでajax通信が可能となる。 仕事でちゃんと使ってみて、とんでもなく便利な技術だと感じた。 これからどしどし使っていこうと思う。 リクエスト⇔レスポンス間でやりとりするデータの種類を示す情報のこと。textなのかHTMLなのかJSONなのか画像なのかなどの情報のことで、前述のcontentTypeとほぼ同義。 ↩
- 投稿日:2021-10-22T20:47:23+09:00
[Javascript]ファイルを読み込まない
やりたいこと javascriptのファイルがビューファイルに読み込まれるようにしたい。 エラー内容 items/newがprofit.jsを読み込まない。 ## 調査 ERR_ABORTED=javascriptのファイルが正しいディレクトリに保管されていない。 GET [http://localhost:3000/items/profit.js](http://localhost:3000/items/profit.js) net::ERR_ABORTED 404 (Not Found) →下記の原因が考えられる。 items/newのscript属性の記載が誤っている。 application.jsの直下のディレクトリに配置していない。 ファイル名の記載が間違っている 発火するイベントの記載が間違っている ## 原因 profit.jsを読み込む記述を二重に行っていたため。 application.jsのDOMツリーに、require '../profit'と記述した時点で、自動で読み込まれるようになっている。 →すでに読み込まれているjsファイルを再び読み込もうとしているので、エラーになった。 app/javascript/application.js application.js require("@rails/ujs").start() // require("turbolinks").start() require("@rails/activestorage").start() require("channels") require("../profit"); app/views/items/new.html.erb hew.html.erb <div class="items-sell-contents"> <header class="items-sell-header"> <%= link_to image_tag('furima-logo-color.png' , size: '185x50'), "/" %> <script src="profit.js"></script> 解決策 new.html.erbのscript要素を削除する。 app/views/items/new.html.erb new.html.erb <div class="items-sell-contents"> <header class="items-sell-header"> <%= link_to image_tag('furima-logo-color.png' , size: '185x50'), "/" %> # 削除 <script src="profit.js"></script>
- 投稿日:2021-10-22T20:43:00+09:00
[Javascript]Uncaught TypeError:
やりたいこと Payjpを使って、クレジットカードの情報をサーバーに送信したい。 出たエラー card.js:25 Uncaught TypeError: "order_number".removeAttribute is not a function at Object.<anonymous> (card.js:25) at (index):1 実際のコード -card.js card.js const card = { number: formData.get("order[number]"), cvc: formData.get("order[cvc]"), exp_month: formData.get("order[exp_month]"), exp_year: `20${formData.get("order[exp_year]")}`, }; Payjp.createToken(card, (status, response) => { if (status == 200) { const token = response.id; const renderDom = document.getElementById("charge-form"); const tokenObj = `<input value=${token} name='token' type="hidden">`; renderDom.insertAdjacentHTML("beforeend", tokenObj); } document.getElementById("order_number".removeAttribute("name")); document.getElementById("order_cvc").removeAttribute("name"); document.getElementById("order_exp_month").removeAttribute("name"); document.getElementById("order_year").removeAttribute("name"); document.getElementById("charge-form").submit(); }); 原因 is not a functionの意味 =値を関数として呼び出そうとしたものの、その値が実際には関数で定義されていなかった 関数のタイプミス 関数を呼び出すときのタイプミス "order_number"の)が抜けている。 -card.js card.js document.getElementById("order_number".removeAttribute("name")); 解決策 コードを下記のように修正 -card.js card.js document.getElementById("order_number").removeAttribute("name"); 参考 TypeError: "x" is not a function - JavaScript | MDN
- 投稿日:2021-10-22T18:07:56+09:00
JavaScriptですぐに使える、市区町村のポリゴンデータを公開した話
サマリ? 日本の全ての市区町村の境界線データを、JavaScriptからすぐに使うことができるデータセットjpCityPolygonを公開しました。 面倒な緯度経度の計算だとか、メルカトルが云々とか考えずに、ただそこに市区町村ポリゴンを描きたいあなたにお勧めです。 成果物 jpCityPolygon: a dataset consisting of polygon data of all cities in Japan. 2021/10/21 現在のversionは0.8.0のbeta releaseです。 2021/10/23 現在のversionは0.9.0のbeta releaseです。 これを使うと、この様なGenerative Artも秒で描けます。 jpCityPolygonのサンプルとして、思い入れのある街をジェネレーティブアートにできるツール #GenerativeHometown を作りました。スマホでもPCでも、ドロップダウンで街を選ぶだけで、アートができあがるので、生まれ故郷や住んでる町で楽しんでみてください!https://t.co/loq00lmP1B pic.twitter.com/C2uVCkMQOA— Tetsunori NAKAYAMA | 中山 哲法 (@tetunori_lego) October 22, 2021 - Generative Hometown きっかけ Saitama.jsという名前のイベントでanozonsさんが発表されていた、『埼玉県の描き方』の内容が面白かったので、自分でも、思い入れのある土地、なんなら都道府県よりも、もっと愛着のある市区町村レベルで絵を描けないかな?と調べ始めました。 実際に使えそうなデータがないかと調べ始めると、ライセンス的に使用が難しいものだったり、データが不正確だったり・・・そして、なによりJSでパッとURLにアクセスしたらすぐ描けるようなデータというものは見当たりませんでした。(検索力ないだけかも・・・) それなら自分で作ろうと思い立ったのが、本取り組みのきっかけです。 簡単な使い方 環境 今回のデータはWebから扱えるようにJavaScirptのObjectデータとして公開しています。 データのインポート https://tetunori.github.io/jpCityPolygon/dist/v0.9.0/の下にすべての市区町村に対応したスクリプトファイルがあるので、お好きな都市を選んでください。 ファイルは<都道府県名>/<市区町村名>.min.jsおよび<都道府県名>/<市区町村名>.jsの両方の形式で提供しています。もちろんmin.jsのほうがファイルサイズが小さいので、使用される際はこちらがお勧めです。なお、v0.9.0より、都道府県全域のポリゴンデータも追加しました。ファイルは<都道府県名>/<都道府県名>.min.jsおよび<都道府県名>/<都道府県名>.jsの両方の形式で提供しています。 GitHubのリポジトリへアクセスし確認してみてください。 例えば、北海道北広島市というデータを取得する場合は、このようなscriptタグを読み込んでください。 <script src="https://tetunori.github.io/jpCityPolygon/dist/v0.9.0/北海道/北広島市.min.js"></script> <script src="https://tetunori.github.io/jpCityPolygon/dist/v0.9.0/北海道/北海道.min.js"></script> データを使う オブジェクトの構成 全ての<都道府県名>/<市区町村名>.jsと同.min.jsは1つのメインオブジェクト<都道府県名><市区町村名>を持っています。また、全ての<都道府県名>/<都道府県名>.jsと同.min.jsは1つのメインオブジェクト<都道府県名>を持っています。 先程の例ですと、北海道北広島市オブジェクトや北海道オブジェクトが含まれています。 このオブジェクトには、下記の5つのプロパティが含まれています: prefecture: String 都道府県名 name: String 市区町村名 latlons: ポリゴンデータのArray 緯度経度情報の生値。{'lat':..., 'lon':...}の形式で複数のポリゴンを形成。 polygons: ポリゴンデータのArray 緯度経度情報からメルカトル投影で平面にマッピングした(x, y)情報の生値。{'x':..., 'y':...}の形式で複数のポリゴンを形成。 normalizedPolygons: ポリゴンデータのArray 上記polygonsのデータをサイズ360*360にちょうど収まるように正規化したポリゴンデータ。{'x':..., 'y':...}の形式で複数のポリゴンを形成。 特にこのデータを使うことをお勧めします! 実際にp5.jsで使ってみる なんてシンプルなんだ! function setup() { createCanvas(360, 360); } function draw() { background(220); // 今回はnormalizedPolygonsを使います。 const polygons = 北海道北広島市.normalizedPolygons; // const polygons = 北海道.normalizedPolygons; // メインのオブジェクトは複数のポリゴンデータで構成されています。 polygons.forEach((polygon) => { beginShape(); for (let i = 0; i < polygon.length; i++) { // vertexに放り込むだけ! vertex(polygon[i].x, polygon[i].y); } endShape(); }); } 実装サンプル Basic Sample: シンプルに境界線を描く - Sample On GitHub - Sample On OpenProcessing Generative Hometown: ジェネラティブアートにする - Sample On GitHub - Sample On OpenProcessing 技術的なポイント 元データの素性 正確性とライセンスの都合から、国土交通省のデータを使いました。比較的緩い制約で使えるので大変助かります。 「国土数値情報(行政区域データ)」(国土交通省) ※ なお、データの基準年月日は平成31(2019)年 1月 1日時点となります。 メルカトル投影のロジック いろいろ調べたりしたのですが、緯度経度が与えられたときに、XY平面に変換するロジックは下記で実装してみました。たぶんあっていると思うけどあまり自信がないです。 // (中略) // k: each (lat, lon) coordinates // calucurate as Mercator projection const radians = (deg) => deg * (Math.PI / 180); const polygon = l.map((k) => ({ x: radians(k[0]), y: Math.log(Math.tan(Math.PI / 4 + radians(k[1]) / 2)), })); サンプルの実装 スクリプトの動的ロード ポリゴンデータを含んでいるため結構なサイズのファイルになってしまい、事前に全部読み込むことは現実的ではありません。そこでサンプルでは、市区町村が選択された際に動的にスクリプトを読みこむようにしてみました。ロードのパフォーマンスも個人的には満足しています。 generativeHometownのパターン生成について SYM380さんが作られているスーパークールなツール、p5.patternを使っています。下記のサンプルのようにめちゃめちゃ簡単にパターン生成できるので、大変たすかりました!みんな積極的に使おうな。 How to use p5.pattern by SYM380
- 投稿日:2021-10-22T15:23:03+09:00
Native File System API でテキストエディタを作る
先日公開された vscode.dev を触った際、なんか Native File System API というのを使えばブラウザから普通にローカルファイルを扱えると知ったので、試しにブラウザで動くテキストエディタを作ってみた。 Chromium 系のブラウザ (Chrome, Edge など) だと大体動くけど Brave はだめ らしい。http サーバーを立てずに、ローカルの HTML ファイルとして開いても動く。 注意点としては、既存ファイルを上書き保存をすると作成日が保存した時点になってしまう。 html <div id='filename'>名称未設定ファイル</div> <textarea id='editor'></textarea> <div> <button id='open'>開く</button> <button id='save'>上書き保存</button> <button id='saveas'>名前をつけて保存</button> </div> css #editor { width: 320px; height: 160px; } #filename { color: #888888; } JS const fileNameDiv = document.getElementById('filename') let fileHandle // テキストファイルだけ開けるようなオプション const pickerOpts = { types: [ { description: 'Texts', accept: { 'text/*': ['.txt', '.text'] } } ], multiple: false, } document.getElementById('open').onclick = async () => { try { [ fileHandle ] = await window.showOpenFilePicker(pickerOpts) if (fileHandle.kind !== 'file') return fileNameDiv.textContent = fileHandle.name const fileData = await fileHandle.getFile() const content = await fileData.text() document.getElementById('editor').value = content } catch (e) { // ファイル選択をキャンセルした時などにここに飛ぶ console.error(e) } } document.getElementById('save').onclick = async () => { if (fileHandle.kind !== 'file') return const content = document.getElementById('editor').value const writableStream = await fileHandle.createWritable() await writableStream.write(content) await writableStream.close() } document.getElementById('saveas').onclick = async () => { try { const content = document.getElementById('editor').value fileHandle = await window.showSaveFilePicker(pickerOpts) if (fileHandle.kind !== 'file') return const writableStream = await fileHandle.createWritable() await writableStream.write(content) await writableStream.close() fileNameDiv.textContent = fileHandle.name } catch (e) { console.error(e) } }
- 投稿日:2021-10-22T15:03:49+09:00
axiosのinterceptorsでリクエストに共通のパラメータを付与する
axiosを利用してAPIリクエストを送る際に、全リクエストに同じパラメータを付与したいパターンがありました。 リクエストを送るタイミングで個別にパラメータを追加してもよかったのですが、不毛なのでinterceptorsを利用して処理を共通化しました。 interceptors の書き方 こんな感じで前処理を追加できます。 import axios from 'axios' axios.interceptors.request.use(request => { console.log(request.url) return request }) 詳しくはGithubを参照してください。 共通パラメータを追加する GET場合 import axios from 'axios' axios.interceptors.request.use(request => { const hoge = { id: xxx, value: xxx } request.params = { ...request.params, hoge } return request }) POSTの場合 import axios from 'axios' axios.interceptors.request.use(request => { const hoge = { id: xxx, value: xxx } request.data = { ...JSON.parse(JSON.stringify(request.data)), hoge } return request }) メソッドやURLで分岐させることもできる import axios from 'axios' axios.interceptors.request.use(request => { if(request.method === 'get') { ... } if(request.url === '/hoge') { ... } return request })
- 投稿日:2021-10-22T12:32:41+09:00
formにid足すだけでreCAPTCHA v3を自動適用させる
やりたいこと formに id="recaptcha" を足すだけでreCAPTCHAが動作するようにしたい。 トークン期限2分は短すぎるので送信の直前にトークンを取得したい。 必要なもの jQuery JavaScriptの文法に書き換えるなら不要。 reCAPTCHA サイトキー reCAPTCHAのサイトで発行できる。 準備 JavaScript 以下のコードを入れる。 アクション名、サイトキー、id名は必要に応じて書き換える。 js $(function () { // 任意のアクション名 let action = "LOGIN"; // reCAPTCHAのサイトキー let siteKey = "SITE KEY"; // 適用したいformのid名 let formId = "#recaptcha"; // api.js読み込み if ($(formId).length) { $("<script>", { src: "https://www.google.com/recaptcha/api.js?render=" + siteKey, }).appendTo("head"); } // 送信イベント $(formId).submit(function (e) { let form = this; e.preventDefault(); // トークンを受け取るinputを生成 $("<input>", { type: "hidden", name: "recaptchaResponse", id: "recaptchaResponse", }).appendTo(form); // トークンを取得 grecaptcha.ready(function() { grecaptcha.execute(siteKey, { action: action, }).then(function(token) { $("#recaptchaResponse").val(token); // 送信 $(form).unbind("submit").submit(); }); }); }); }); HTML あとはreCAPTCHAを適用したいformにjs側で指定したid名をつけるだけ。 api.js はスクリプト側で挿入するため、HTML側での挿入は必要なし。 html <form id="recaptcha"> <!-- idを入れる --> <input type="email"> <input type="password"> <button type="submit">送信</button> </form> 結果 うごくよー
- 投稿日:2021-10-22T09:07:55+09:00
【JavaScript】関数とオブジェクト⑦ アロー関数
はじめに Udemyの【JS】ガチで学びたい人のためのJavaScriptメカニズムの講座の振り返りです。 前回の記事 目的 関数とオブジェクトについての理解を深める 本題 1.アロー関数 無名関数を記述しやすくした省略記法のこと 基本構文. () => {}; 例1 基本的な書き方 // 一般的な関数 function a(name){ return "hello " + name; } // 関数式にした場合 const b = function(name){ return "hello " + name; } // 上記をアロー関数に直すと下記の通り const c = name => "hello " + name; // functionを削除して、{}の前に=>を置く // 引数が1つの場合()は省略できる // 実行行が一行の場合は{}が省略できる // そうすると変数cにアロー関数が代入される console.log(c("炭治郎")); 例2 2つ以上引数を渡したい場合 // ()で引数を囲い、第二引数を用意 const d = (name, name1) => "hello " + name + ", " + name1 console.log(d("善逸", "伊之助")); 例3 引数がない場合 // 引数がなくても()の省略は不可 const e = () => "hello"; console.log(e()); // ()ではなく_でも代用可 const f = _ => "hello"; console.log(f()); // 複数行ある場合は{}使う // {}がある場合はreturn省略不可 const g = () => { const h = "うまい!" + "うまい!"; return h; } console.log(g()) 2.無名関数とアロー関数の違い 省略記法であるだけなので、機能が同じというわけではない 無名関数 アロー関数 this ◎ ✗ arguments ◎ ✗ new ◎ ✗ prototype ◎ ✗ 上記のように使えない機能もある 今日はここまで! 参考にさせて頂いた記事 【JS】ガチで学びたい人のためのJavaScriptメカニズム
- 投稿日:2021-10-22T01:05:55+09:00
コードインジェクション攻撃
こんにちは。 今日のお題はコードインジェクションについてです。 JapaScriptのコーディングには自身がある方も、お役にたちそうな記事内容を見つけました。 コードインジェクション攻撃で、注射針を思い出すのは私だけかと思っていたところ、こちらのブログ記事にも注射アイコンを発見。万国共通のイメージなのでしょう。 コーディングの段階からセキュリティを取り入れるという考え方が理にかなっているとはわかるけど、やっぱりちょっと面倒、、、という方も、最後まで読んでみてください。 JavaScriptとNode.jsでコードインジェクションを防ぐ5つの方法 JavaScriptとNode.jsでコードインジェクションを防ぐ5つの方法 Liran Tal (リラン・タル) 2021年4月5日 コードインジェクションを防ぐ安全なコードを書くのは簡単なようで実は、多くの落とし穴があります。たとえば、あなた自身がデベロッパとしてセキュリティのベストプラクティスに従っているからといって、他のデベロッパも同じことをしているとは限りません。普段あなたもオープンソースパッケージをアプリケーションで使用していると思いますが、それらが安全に開発されたかどうかまでどのように知ることができるでしょうか。そこにeval()のような安全でないコードが存在するとしたらどうしますか?では、実際に考えていきましょう。 コードインジェクションとは? コードインジェクションは、ブラウザまたはNode.jsのランタイム時に、JavaScriptまたはNode.jsコードを送信する広範なインジェクション攻撃の手法です。デベロッパが意図する信頼できるコードと攻撃者により挿入されたコードが区別なく読み込まれ、セキュリティの脆弱性となります。 コードインジェクションを防ぐ方法 安全なコーディングの基本として重要なのは、動的なコードの実行をアプリケーションで許可をしないことです。例えば、eval 、setTimeout() やFunction に渡されるコード文字列などの言語コンストラクトを避ける必要があり、シリアライズプロセスでコードを実行するインジェクション攻撃に対して脆弱な可能性のあるシリアライズも回避する必要があります。また、サードパーティのオープンソースコンポーネントが原因でアプリケーションがコードインジェクションの攻撃を受けないように、依存関係のスキャンを実行します。Snyk Codeなどの静的コード分析ツールを使用して、自分やチーム内のコードインジェクションに関するセキュリティの脆弱性を確認することが重要です。 ここでは、コードインジェクションを防ぐための5つの方法を検討します。 eval()、setTimeout()、setInterval()を回避 new Function()を回避 JavaScriptでコードのシリアライズを回避 Node.jsセキュリティlinterの使用 静的コード分析(SCA)ツールの使用によるコードインジェクションの発見と修正 1.eval()、setTimeout()、setInterval()を回避 またか、と思われるかもしれませんが、まず、evalを避ける方法について説明します。後々、重大な脆弱性や問題となりえるeval関連のライブラリ(または他の形式のコード構築)の実際の例も紹介します。 脆弱なサードパーティパッケージについて触れる前に、まずevalとそれに付随する機能について説明します。ブラウザやサーバー側のNode.jsプラットフォームなどのJavaScriptランタイム環境では、実行時にコードの評価と、実行が行われます。 実際の例: const getElementForm = getElementType == “id” ? “getElementById” : “getElementByName”; const priceTagValue = eval(“document.”+getElementForm+”(“+elementId+”).value”); このコードで、プログラマーはDOM上のデータにアクセスするための動的な方法を作成しようとしています。この例ではgetElementform、elementId変数だけでなく、ユーザーによる制御があると想定しています。しかし、evalを使わずにこのタスクを実行するためのより良い方法があるので、この例のような動的コードは絶対に避けてください。 Node.jsでは、動的評価に基づいて、アプリケーション内の特定のデータポイントへのアクセスを許可させたい場合を考えましょう。 例: const db = "./db.json" const dataPoints = eval("require('"+db+"')"); こちらは、必要なファイルが動的で、ユーザーの制御も可能にしたいと一般的に考えられる例です。この場合にも、コードインジェクションによるセキュリティの脆弱性が発生する可能性があります。 Dustjsコードインジェクションの、安全でないevalの使用法の実例 LinkedInのnpmパッケージdustjs(ブラウザーとサーバー側のNode.js用の非同期テンプレートプロジェクト)の例は、コードインジェクションの脆弱性がどれほど深刻になりえるということを教えてくれます。 このパッケージは、長らく十分なメンテナンスがされていないにもかかわらず、月間約72,000のダウンロードをキャプチャしており、コードインジェクションによるセキュリティの脆弱性に対処する必要がありました。 dustjsのメンテナは、eval()関数のように、安全が確保されないコード構造に対する潜在的な危険があるユーザー入力を回避するための対策を行うことにしました。しかし、escapeHtml関数にも文字列タイプのみをチェックしてから入力をエスケープするというセキュリティ上の欠陥があり、配列など他の要素についてもチェックする必要がありました。 このようなpullリクエストにより、コードインジェクションのセキュリティの脆弱性を修正しました。 eval()の扱いについて気になりますよね? dustjsを使用する場合は、数学演算や論理演算などの追加のテンプレートヘルパーを取得するnpmパッケージのdustjs-helpersを導入しました。これらの追加のヘルパーの1つはif条件であり、これを独自のdustテンプレートファイルで次のように使用しました。 ここまでは、理にかなっていますね。 問題は、そのクエリパラメータでは、制御されていないユーザー入力がdeviceif条件ヘルパーに直接流れ込むことです。227行目にあるように、if条件ヘルパーがevalを使用して条件を動的に評価しています: この例で、いくつかのセキュリティ問題が予期しない方法でどのように組み合わされるのかをわかっていただけると思います。 オープンソースパッケージであるdustjs-linkedinには、escapeHtml関数内の入力文字列が誤ってサニタイズされるというセキュリティ上の欠陥がある。 オープンソースパッケージであるdustjs-helpersは、eval()関数などの安全でないコーディング規則を使用して、実行時にコードを動的に評価する。 では実際に、私がこの脆弱性をどのように悪用し、この正確な脆弱性に基づいて実際に動作するアプリケーションをハッキングしたかをご紹介します。こちらをご覧ください: setTimeout()とsetInterval()も回避する。 eval()回避のベストプラクティスについて締めくくる前に、JavaScriptのデベロッパとしてよく知られており、アプリケーションで少なくとも一度はあなたが使用したであろうsetTimeout()とsetInterval()関数についても触れさせてください。 これらの関数についてあまり知られていない事実は、それらがコード文字列も受け取るということです。たとえば、次のように使えてしまうということです: setTimeout(“console.log(1+1)”, 1000); 幸いなことに、Node.js環境では文字列リテラルが使用できません。 EEE2. new Function()を回避 既に述べたeval()、setTimeout()、setInterval()と同様の言語コンストラクトとしては、文字列リテラルに基づいて定義できる動的な関数コンストラクタがあげられます。 よくある例を考えてみましょう: const addition = new Function(‘a’, ‘b’, ‘return a+b’);addition(1, 1) 気をつけて読み進めていただいたデベロッパの皆さんであれば、そのような関数にユーザー入力で流れ込みで発生する可能性がある潜在的なセキュリティの問題についてお気づきでしょう。 3.JavaScriptでのコードのシリアライズを回避 シリアライズは、Javaエコシステムにおいて重要です。同僚であるBrianVermeerが、安全でないシリアライズ操作によるJavaアプリケーションへのセキュリティの脆弱性についてブログに投稿しています。是非、読んでみてください:Javaでのシリアライズとデシリアライズ:Javaのデシリアライズの脆弱性の説明 JavaScriptでは、シリアライズはとても重要です。 独自のシリアライズおよびデシリアライズのロジックを自分でコーディングすることは稀ですが、npmと150万を超えるオープンソースパッケージを自由に使えるのに利用しない手はありませんよね。 js-yamlは、週に2,800万回以上ダウンロードされる大人気のパッケージです。Snyk Advisorによると、パッケージ全体の状態が良好であることがわかります。 しかしながら、上記のnpmパッケージjs-yamlのスクリーンショットから、以前のバージョンにはセキュリティの脆弱性があったことがわかります。この場合、どうすればいいのでしょう? js-yamlのバージョンは、デシリアライズのためのコード実行に対して脆弱であることがわかりました。脆弱性は、new Function()コンストラクターの使用によるものとわかりました: function resolveJavascriptFunction(object /, explicit/) { /jslint evil:true/ var func; try { func = new Function('return ' + object); return func(); } catch (error) { return NIL; } } この脆弱性に対する概念実証エクスプロイトがどのように表示されるか確認してみましょう: var yaml = require('js-yaml'); x = "test: !!js/function > \n \ function f() { \n \ console.log(1); \n \ }();" yaml.load(x); このように、悪意のあるアクターが、上記の概念実証コードでx変数の作成に使用されるような入力や、その一部を提供する場合、潜在的な脆弱性から実在する危険となります。 この脆弱性は2013年に遡ったものですが、2019年のセキュリティの脆弱性レポートでは js-yamlで任意のコードが実行される別のケースも見つかりましたので、注意は怠らないようにしてください。 具体的には、new Function()を避けること。さらに、サードパーティのオープンソースパッケージをスキャンし、これらの脆弱性がないことを確認してください。脆弱性がある場合は、プルリクエストにより自動的に修正が可能となります。 4.Node.jsセキュリティlinterの使用 本ガイドのツールに関する分野として、linterについて説明します。JavaScriptデベロッパはlinterを好んで使います。standardsやeslintのどちらのコードスタイルを適用するかという問題ではなく、これらはJavaScriptまたはNode.jsプロジェクトで非常に一般的なツールです。 そこで、ぜひ、良いセキュリティプラクティスを取り入れてみませんか?早速、eslint-plugin-securityを使ってみましょう。READMEの手順に従えば、簡単にプラグインが使えます。次のeslintプラグイン構成を追加するだけで、推奨の構成が有効になります。 "plugins": [ "security" ], "extends": [ "plugin:security/recommended" linterはどのように役立つのでしょう? 次のような、安全でないコーディング規則を検出するルールがあります。例えば、detect-eval-with-expression– eval()with式または文字列リテラルの使用を検出してくれます。child_process Node.js APIの使用なども可能です。 ただ、eslint-plugin-securityの最終公開日は4年以上前ですので、機能的には正常に機能しますが、eslint-plugin-security-nodeなどの後続のパッケージを検討することを推奨します。 5. 静的コード分析(SCA)ツールの使用によるコードインジェクションの発見と修正 ESLintで使用される基本的な形式の静的コード分析(SCA)linterは、出発点としては良いのですが、コードスタイルを適用するのに十分なコンテキストを提供するだけで、Node.js security linterで確認したように、セキュリティの問題に必要な対処が実際にできるほど柔軟ではありません。 Node.js security linterに関してデベロッパが抱く懸念事項として: 誤検知が多すぎる:linterルールは非常に基本的なものであり、誤検知のアラートが多すぎる。結果として、デベロッパの不満と混乱を助長するだけに終わってしまうという可能性があるということ。例えば、RegExp(matchEmailRegEx)場合、RegExp関数がリテラル以外で使用されているため、Node.jsセキュリティlinterでエラーが発生。単にmatchEmailRegEx、shared / variables.jsファイルの定数だとしても、linterはそれを知るのに十分なほど賢くはありません。 ルールが厳し過ぎる:前述と重複しますがルールが厳しすぎます。例えば、child_process.exec(someCommand, [])を使うか、使わないかの選択しかありません。linterを使用した静的コード分析プロセスは、someCommandがハードコーディングした定数であるとわかるほど賢くはありません。リテラル以外でchild_process.exec()を使用しているということだけで、linterエラーをトリガーしてしまうため、結局、デベロッパは嫌気がさしてルールを無視するという結果となりがちです。 ルールが基本的すぎる:ルールのコレクションが少なすぎて、調査結果があまりに基本的です。例えば、データが特定のユーザー入力から、コマンド実行、SQLクエリなどの潜在的な機密コードに実際にどのように流れるかについての多くのコンテキストがなく、All or Nothingの選択しかありません。 このような懸念があるものの、eslint-plugin-security-nodeなどのセキュリティlinterは出発点としては良いと言えます。なぜならセキュリティ対策をしないよりは、間違いなく良いからです。 ただ、コーディング中に自分のコードのセキュリティ問題を見つけるもっと良い方法があります。 それは、デベロッパ向けに構築された静的アプリケーションセキュリティテストツール(SAST)であるSnykCodeを使うことです。 #Node.jsアプリケーションでのコマンドインジェクションの検索 Snyk Codeはまもなくリリースされる予定ですが、その仕組みについての予告編をご紹介します。 まず、GitHubアカウントでSnykに接続し、GiitHubリポジトリのインポートを行います。プロジェクトの追加[Add project] をクリックし、次にGitHubアイコンをクリックします。 次に、リポジトリのリストからリポジトリを見つけるか、検索ボックスでリポジトリを入力し、リポジトリをオンにしてスキャンを開始します。 SnykはGitHubリポジトリをインポート、迅速にスキャンします。 既知の脆弱性を持つオープンソースの依存関係を使用している場合や、Dockerイメージに多数のセキュリティの脆弱性が発生している場合など、潜在的なセキュリティ問題に関連するマニフェストファイルが自動的に検出されます。 実際に、コード分析をクリックして、何が見つかるか、このNode.jsアプリケーションの独自のコードで試してみましょう。 Snyk Codeはいくつかの脆弱性を発見しました。 そのうちの1つは、ここに示すようにコマンドインジェクションです。 このコード行にある問題のあるセキュリティに関する、懸念事項を説明しています。 「HTTPリクエスト本文からのサニタイズされていない入力はchild_process.execに流れ込み、そこでシェルコマンドを構築するために使用されます。これにより、コマンドインジェクションの脆弱性が発生する可能性があります。」 “Unsanitized input from the HTTP request body flows into child_process.exec, where it is used to build a shell command. This may result in a Command Injection vulnerability.” データがこのurlパラメーターから安全でないexec()関数にどのように流れてくるのでしょう? 詳細ボタンをクリックすると、コンテキストを追加するためのデータフローについて知ることが可能です: SnykCodeで分析すると、脆弱性について全体像をはっきりと確認することができます。 urlパラメータがitem配列から作成されて、それ自体がユーザ制御入力のソースとなり、req.body.contentのメッセージボディ入力として流れてきています。 コマンドインジェクションの修正 セキュリティ問題に対処するため、さらなる手順を次のように実行します。 安全でないexec()を使用する代わりに、そのAPIの安全なバージョンを使用する。execFile()は、配列関数の引数の形式で提供された引数をエスケープの処理をする。 システムプロセスの実行など機密性の高いコードへの影響がないように、ユーザー入力からアイテム変数を検証、エスケープ、またはサニタイズすべきである。 まとめ 最後まで読んでいただきありがとうございます! コードインジェクションを引き起こす可能性のある脆弱性についての問題について、より理解を深めていただけたとしたら幸いです。 独自のコードにおいても、アプリケーションにインポートするサードパーティの依存関係においても、コードインジェクションの脆弱性が存在するのです。 Contents provided by: Jesse Casman, Fumiko Doi, Content Strategists for Snyk, Japan, and Randell Degges, Community Manager for Snyk Global
- 投稿日:2021-10-22T00:35:13+09:00
Gmailの件名と本文テンプレートをワンクリック入力【ブックマークレット】
Gmailで予めテンプレートとして作成しておいた件名と文章をワンクリックで入力するブックマークレット GmailをChromeブラウザで利用している方向けのものです。 新規作成ボタンを押した後に、件名と本文を自動入力します。 業務の効率化を目的としています。 Gmailにもテンプレート入力機能あるのですが、 若干ステップ多いので・・・ (これは超ショートカット版のようなイメージです) コード ▼シンプルな例: javascript:var subj = "ご挨拶とお打ち合わせに関しまして";var com ="<div>様</div><div>★お好きな文章</div>";mails();function mails(){document.getElementsByName("subjectbox")[0].value=subj;var mailms = document.querySelectorAll('[aria-label="メッセージ本文"]')[1];mailms.insertAdjacentHTML('afterbegin',com);} ▼営業向けテンプレートの例 javascript:var subj = "ご挨拶とお打ち合わせに関しまして";var com ="<div>様</div><div><br></div><div>お世話になっております。</div><div>株式会社〇〇の☆☆です。</div><div><br></div><div>もしよろしければ、一度ご挨拶も兼ねて</div><div>お打ち合わせの機会を頂けないでしょうか。</div><div><br></div><div>お打ち合わせ候補日:</div>";mails();function mails(){document.getElementsByName("subjectbox")[0].value=subj;var mailms = document.querySelectorAll('[aria-label="メッセージ本文"]')[1];mailms.insertAdjacentHTML('afterbegin',com);} 使用方法 コードを編集: subj = "★"の部分に件名を入力 com= "★"の部分に本文を入力 ※Gmailの仕様に合わせ、1段落毎に<div></div>囲み 改行は<div><br></div> ブックマークレットの作成: 1.適当なブックマークを作る 2.ブックマークを編集 名前を「URLコピー」(任意) URLに上記のスクリプトを貼り付ける 3.Gmailで新規作成ボタンを押し、作成したブックマークをクリック →件名と本文が入力されるはずです。 コード補足 document.getElementsByName("subjectbox")[0].value=subj; 件名に入力する document.querySelectorAll('[aria-label="メッセージ本文"]')[1];mailms.insertAdjacentHTML('afterbegin',com); 本文に入力する(HTMLの差し込み) 補足 Gmail全体の仕様が変更となった場合は利用出来ません。