【Node.js】axiosで CSRF 対策への対応
どうも,筆者です.
今回は,axios を用いて,外部サーバと通信する際の CSRF 対策への対応方法を示す。
ここでは,Django で構築された外部サーバ http://hogehoge.com/login
にログイン後,同一サーバの http://hogehoge.com/loggedin/sample
にアクセスすることを考える.前提として、対象の URL はログイン済みの時のみアクセス可能とする.
python の場合
普段,python を利用しているので,requests
モジュールによる実装を示しておく.今回は,ここに示す内容を Node.js で実現する.
準備
pip install requests
実装
import requests base_url = 'http://hogehoge.com' with requests.session() as session: # step1: login login_url = '{}/login'.format(base_url) session.get(login_url) csrf_token = session.cookies['csrftoken'] data = { 'csrfmiddlewaretoken': csrf_token, 'username': 'username', 'password': 'password', } session.post(login_url, data=data, headers=dict(Referer=login_url)) # Step2: access to loggedin/sample response = session.get('{}/loggedin/sample'.format(base_url)) print(response.text) # loggedin/sampleのページの内容が表示される
Nodejs
Node.js による実装を示す.
準備
npm install axios npm install axios-cookiejar-support npm install tough-cookie npm install querystring
実装
const querystring = require('querystring'); const axios = require('axios'); const axiosCookieJarSupport = require('axios-cookiejar-support').default; const tough = require('tough-cookie'); axiosCookieJarSupport(axios); class CustomAxios { constructor(baseURL) { const Cookie = tough.Cookie; const cookieJar = new tough.CookieJar(); const csrfHeaderName = 'X-CSRFToken'; // 外部サーバの設定に合わせる const csrfCookieName= 'csrftoken'; // 外部サーバの設定に合わせる this.postTokenName = 'csrfmiddlewaretoken'; // 外部サーバの設定に合わせる this.csrfToken = null; this.api = axios.create({ baseURL: baseURL, jar: cookieJar, withCredentials: true, xsrfHeaderName: csrfHeaderName, xsrfCookieName: csrfCookieName, headers: { 'x-requested-with': 'XMLHttpRequest', }, }); // Add cookie support for Node this.api.interceptors.request.use((config) => { if (this.csrfToken) { config.headers[csrfHeaderName.toLowerCase()] = this.csrfToken; } cookieJar.getCookies(config.baseURL, (err, cookies) => { if (!err && cookies) { config.headers['cookie'] = cookies.join('; '); } }); return config; }); this.api.interceptors.response.use((response) => { const header = response.headers['set-cookie']; if (header) { const cookies = (header instanceof Array) ? header.map(Cookie.parse) : [Cookie.parse(header)]; cookies.forEach((cookie) => { cookieJar.setCookie(cookie, response.config.baseURL, (err, targetCookie) => { if (!err && targetCookie) { // Store previous csrfToken if (targetCookie.key === csrfCookieName) { this.csrfToken = targetCookie.value; } } }); }); } return response; }); // bind this.get = this.get.bind(this); this.post = this.post.bind(this); } async get(linkName, params) { const data = params || {}; // get request const response = await this.api.get(linkName, {params: data}); return response; } async post(linkName, sendData) { let response; // get csrfToken response = await this.api.get(linkName); const data = Object.assign({[this.postTokenName]: this.csrfToken}, JSON.parse(JSON.stringify(sendData))); const headers = { Referer: `${response.config.baseURL}${linkName}`, }; // post request response = await this.api.post(linkName, querystring.stringify(data), {headers: headers}); return response; } }
実行例
(async () => { const session = new CustomAxios('http://hogehoge.com'); let response; // Step1: login const data = { username: 'username', password: 'password', }; response = await session.post('/login', data); // Step2: access to loggedin/sample response = await session.get('/loggedin/sample'); console.log(response.data); })().catch((err) => console.log(err));
Raspberry Pi と FreePBX と brastel(My 050) で VoIP 環境を構築する②
どうも,筆者です.
続いて,FreePBX で設定をしていく.今回も,以下のサイトを参考に進める.
全体構成
後で,具体的な設定が出てくるが,先に全体構成を示しておく.IAX や PJSIP は VoIP 通信を行う際のプロトコルであると認識している.
上記の図にあるように,それぞれの以下のような方法で通信する.
経路 | 通信プロトコル |
---|---|
スマホから asterisk | IAX2 |
asterisk から brastel server | PJSIP |
必ず,この方法で通信しないといけないわけではないが,ほかの方法でうまく実現ができなかった.このため,今回は上記の構成で進める.
FreePBXの設定
基本的な操作は,参考サイトをもとに進めていただきたい.ここでは,設定した結果を示すに留める.
アカウントの設定
参考サイトをもとに,アカウントを設定し, apply config
を実行する.FreePBX を LAN 外からアクセス可能な構成にする人は,推測可能なユーザ名やパスワードは避けること.
具体的な設定
具体的な設定となるが,すべての画像データを格納できないため,詳細情報を Google Drive 上に残しておく.こちらの PDF を参考に設定していただきたい.
ファイル名:RasPBX_setting.pdf
また,各設定項目では,設定が完了したら,右下の「送信」を押下後に,右上の「設定適用」を押下すること.
内線の設定 Extensions
- General の設定.ディスプレイ名は,好きな名前を記入すること.また,初回追加時の内線番号は以降では変更できないため,3 桁から 4 桁程度の値を任意で設定すること.
- 高度な設定.ここも特に設定することはない.type が friend,port が 4569,qualify が yes となっていることを確認しておくこと.type に関しては,friend のほか,peer と user がある.それぞれの違いは以下を参照のこと.
type | 役割 |
---|---|
peer | 発信専用(電話を受けることはできない) |
user | 着信専用(電話をかけることはできない) |
friend | 発信・着信の双方が可能(通常はこちらを使用) |
外線設定 Trunks
外線設定となる.外部へ電話をかける際は,ここの設定が重要となる.リンク先の内容に従って設定すること.
発信設定 Outbound Routes
リンク先の内容に従って設定すること.
内線グループ設定 Ring Groups
リンク先の内容に従って設定すること.
着信設定 Inbound Routes
リンク先の内容に従って設定すること.
外部から使いたい人向け
出先から使いたい人向けの設定も載せておく.理解していない人が設定を行うと非常に危険なため,ポート開放の設定など,具体的な方法は示していない.どうしても外から利用したい場合は,VPN の利用などを検討すること.
また,4569のポートをそのまま開放するとすぐに攻撃が来るため,公開するポートは推測しづらいものにすること.
ファイル名:RasPBX_public.pdf
スマホ側の設定
参考サイトと同じように Zoiper
を利用する.これは,IAX2 に対応したソフトウェアが Zoiper 位しかないためである.
LAN 内から利用する場合
アカウント作成時,hostname or provider
に Raspberry Pi の IP アドレスを入力する.
# 例 192.168.11.2
外部から利用する場合
アカウント作成時,hostname or provider
に FQDN か 固定 IP アドレスを入力する.また,基本的にポート番号を変更しているため,hostname の後ろに公開用のポート番号を指定する.
# 例
www.exmaple.com:12345
上記以外は,参考サイトの内容に従って設定すればよい.
Raspberry Pi と FreePBX と brastel(My 050) で VoIP 環境を構築する①
どうも,筆者です.
今回は,Rasbperry Pi と FreePBX と brastel(My 050) を用いて,VoIP 環境を構築していこうと思う. LINE 等が普及して,今更感はあるが,気にせず進める.
参考サイト
環境構築にあたり,以下の 2 つのサイトを参考にした.今回は,こちらをベースに進める.
環境構築
概要を述べるに留め,具体的な作業手順は省略する.
イメージの取得と書き込み
参照サイトにもあるように,下記のリンクからイメージファイルをダウンロードし,SD カードに書き込む.
2021/9/5 時点の最新版は,以下のようになっている.ここで,Asterisk や FreePBX という用語が出てくるが,これは以降の対応関係を示すときに説明する.
- Asterisk 16.13.0
- FreePBX 15.0.16.75
raspbx-upgrade の失敗
先に断っておくと,今記載の内容は,筆者の記憶をもとに記載している.一部間違っている箇所があるかもしれないので,出力内容を確認した上で実行して欲しい.
raspbx-upgrade
コマンドを実行しても,以下のようなメッセージが表示され,途中で失敗する.
署名照合中にエラーが発生しました。リポジトリは更新されず、過去のインデックスファイルが使われます。GPG エラー: https://packages.sury.org/php buster InRelease: 以下の署名が無効です
このエラーに関しては,以下のサイトが参考になった.
参考先のサイトに記載があるように,apt-key list
コマンドで GPG 署名状態を確認する.期限切れの署名があるため,署名を削除後,最新のものに更新する.
# GPG 署名状態を確認 apt-key list # 該当する署名を削除 rm /etc/apt/trusted.gpg.d/php.gpg # DEB.SURY.ORG の key を削除 apt-key del [表示された key の値] # DEB.SURY.ORG の GPG ファイルをダウンロード wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
他にもエラーが残っていたかもしれないが,これで,raspbx-upgrade
コマンドが実行できた(と記憶している).
対応関係
次回,FreePBX を用いて Asterisk の設定を行っていく.その際に,筆者が理解している範囲で,対応関係を示す.
それぞれの位置づけとしては,以下のようになると認識している.
-
- IP-PBX(IP 型の電話交換機)の実体.このプログラム群が PBX の役割をする.
- 大量の設定ファイルが存在し,内容も複雑.
FreePBX
今回も FreePBX に頼るため,基本的に FreePBX の使い方を覚える方針となる.具体的な設定内容は次回以降で説明する.
IPv4 と IPv6 の併用環境下におけるルーティング情報の設定
どうも,筆者です.
最近,v6プラスを導入した.v6プラスの導入により,ルータに搭載されている VPN サーバ機能が利用できなくなった.このため,IPv4 のネットワークを構築し,VPN 環境を作成する方針とした. ここまでは良かったのだが,IPv4 から IPv6 のネットワークにアクセスできない.ネットワークが異なるため,アクセスできないのは当然であるが,これでは非常に不便である.
ここでは,構築した IPv4 と IPv6 の併用環境下において,IPv4 のネットワークから IPv6 のネットワーク上のサーバにアクセスできるように設定を行う.
前提
ネットワーク構成
IPv4 と IPv6 のネットワークを以下に示す.ここで,Router A は,IPv6 のネットワークが,Router B は,IPv4 のネットワークが構築されているものとする.また,PC のデフォルトゲートウェイは,Router B,Raspberry Pi のデフォルトゲートウェイは,Router A に設定されているものとする.
ネットワーク機器の設定情報
- 192.168.0.x(IPv6)側の設定
対象機器 | NIC 名 | IP アドレス |
---|---|---|
Router A | - | 192.168.0.1 |
Raspberry Pi X | eth0 | 192.168.0.3 |
Raspberry Pi Y | eth0 | 192.168.0.5 |
- 192.168.100.x(IPv4)側の設定
対象機器 | NIC 名 | IP アドレス |
---|---|---|
Router B | - | 192.168.100.1 |
Raspberry Pi Y | eth1 | 192.168.100.5 |
PC | eth0 | 192.168.100.10 |
目的
ここでは,以下に示すように,PC から Router B,Raspberry Pi Y,Router A を経由し,Raspberry Pi X 内にある IRC サーバにアクセスする場合を考える.
通信のための設定
目的に示した通信を実現するためには,以下の 2 点について設定を行う.
- Router B におけるルーティング情報の登録
- Raspberry Pi X におけるルーティング情報の登録
Router B におけるルーティング情報の登録
Router B の設定画面を開き,「Static Route」や「静的経路情報」の設定画面を開く.その設定画面で,以下の情報を追加後,設定を有効にする.
項目 | 内容 |
---|---|
Destination IP | 192.168.0.0 |
Subnet Mask | 255.255.255.0 |
Next Hop | 192.168.100.5 |
Interface | LAN |
Metric | 0 |
上記の設定の意味としては,「192.168.0.0/24 宛のパケットを 192.168.100.5 に渡す」という内容になる.
これにより,PC から Raspberry Pi X 宛の通信は,PC -> Router B -> Raspberry Pi Y -> Router A -> Raspberry Pi X
の順に渡されることになる.
ただ,このままでは,Raspberry Pi X は,192.168.100.0/24 からの通信をどこに渡せばよいかが分からない.すなわち,応答時のルーティング情報がないため,応答が返ってこない状態となる.
Raspberry Pi X におけるルーティング情報の登録
今度は,Raspberry Pi X に 192.168.100.0/24 宛の通信のルーティング情報を登録する.
Raspberry Pi X において,以下のコマンドを実行し,ルーティングテーブルを更新する.
sudo ip route add 192.168.100.0/24 via 192.168.0.5 dev eth0
上記の意味としては,「192.168.100.0/24 宛のパケットを 192.168.0.5 に渡す」という内容になる.
ただし,このままでは再起動後に設定内容が消えてしまうため,/lib/dhcpcd/dhcpcd-hooks/40-route
というファイルを作成し,以下の内容を書き込む.
ip route add 192.168.100.0/24 via 192.168.0.5 dev eth0
以上で,設定は完了となる.一部,理解できていない部分もあるが現時点で期待通りの動作をしているため,しばらく様子見をする.
【TeraTerm】公開鍵認証方式による SSH 接続の自動化
どうも,筆者です.
最近,TeraTerm で LAN 内のサーバにアクセスすることが増えたが,毎回,サーバの指定とパスワードの入力が必要となる.
ここでは,TeraTerm の機能を利用し,ショートカットキーで TeraTerm のマクロを呼び出し,自動的に SSH 接続できるような仕組みを構築する.
本来は,安全のためにパスフレーズを入力するが,今回は,LAN 内の使用を前提とするため,パスフレーズの設定は省略する.
使用する TeraTerm に関する情報
- TeraTerm は,ポータブル版を利用する.
- 使用する TeraTerm のバージョンは,4.102 である.
TeraTerm の配置場所は以下のようにする.
C:\Users\user\OneDrive\デスクトップ\apps\teraterm-4.102
参考サイト
以下のサイトを参考に,作業を行った.
自動化の準備
公開鍵と秘密鍵の生成
TeraTerm の機能を用いて,公開鍵と秘密鍵を生成する.今回は,C:\Users\user\OneDrive\デスクトップ\apps\ssh_keyfile
以下に公開鍵と秘密鍵を保存する.
「Setup」→「SSH KeyGenerator」を押下する.
「Key type」を「RSA」,「Key Bits」を「4096」とする(Key Bits:鍵の長さは 4096 以外でもよいが,なるべく長い方が良い).また,「bcrypt KDF format」のチェックを外し,「Generate」を押下する.
今回はパスフレーズなしのため,「Key passphrase」と「Confirm passphrase」は空欄のままとし,「Save public key」と「Save private key」を順に押下する.
「Save private key」を押下時に,以下のようなメッセージが表示される.これは,「はい」を押下する.今回はパスフレーズなしのため,このような警告が表示される.
公開鍵の設置
生成した公開鍵を SSH 接続先のサーバに配置する.
「File」→「New connection ...」を押下する.表示されたダイアログから接続先のサーバを指定し,「OK」を押下する.
いつも通り「username」と「password」を入力し,「OK」を押下する.ここでは,ラズパイに接続するため,username を
pi
とする.ログイン後,「File」→「SSH SCP」を押下し,公開鍵「id_rsa.pub」をサーバのホームディレクトリにコピーする.ここでは,「From」には,
C:\Users\user\OneDrive\デスクトップ\apps\ssh_keyfile\id_rsa.pub
を,「To」には,~/
を指定した.ファイルを転送が完了したら,コマンド操作で公開鍵(~/id_rsa.pub)を authorized_keys に追記する.
# 作業ディレクトリの確認 pwd # 出力結果:/home/pi # ファイルの確認 ls # 出力結果:id_rsa.pub # .ssh ディレクトリの生成とパーミッションの設定 mkdir .ssh chmod 700 .ssh # 公開鍵の追記 cat id_rsa.pub >> .ssh/authorized_keys # authorized_keys のパーミッションの設定(初回生成時のみ) chmod 600 .ssh/authorized_keys # サーバから切断 exit
公開鍵認証方式によるログイン
再度,TeraTerm を立ち上げ,接続先を選択し,ユーザ名とパスワードを入力する画面を表示させる.その状態で,「Use RSA/DSA/ECDSA/ED 25519 key to log in」を選択し,「Private key file:」を押下し,先ほど生成した秘密鍵を選択する.今回の場合,秘密鍵は C:\Users\user\OneDrive\デスクトップ\apps\ssh_keyfile\id_rsa
となる.
上記において,username を入力後に「OK」を押下して,サーバに接続できることを確認する.
TeraTerm マクロによる自動化
本題に入る.ここでは,TeraTerm マクロを用いて,以下を実現する.
- ショートカットキーによる接続先の選択
- SSH 接続に対し,有効なショートカットキーの一覧の表示
- ショートカットキー押下後に自動接続
TeraTerm マクロの作成
サーバへ自動接続するために,マクロを作成する.ここでは,C:\Users\user\OneDrive\デスクトップ\apps\teraterm_macros
というディレクトリを作成し,その中にマクロファイルを格納する.
サーバへの自動接続時に使用するマクロを以下に示す.ここでは,このファイルを「nas_server.ttl」として保存する.
; ===== setting ===== ; usrename username = 'pi' ; port port = 22 ; secret file path keyfile = '"..\ssh_keyfile\id_rsa"' ; hostname hostname = 'nas.example.com' ; ===== end ===== ; create connection command getdir current_dir sprintf2 private_key_fullpath '%s\%s' current_dir keyfile sprintf2 msg '%s:%d /ssh2 /auth=publickey /user=%s /keyfile=%s' hostname port username private_key_fullpath ; connect to server connect msg
作成したファイルをダブルクリックし,パスフレーズを入力することなくログインできることを確認する.
ショートカットキーの登録
teraterm-4.102
内にある KEYBOARD.CNF
を修正し,ショートカットキーを登録する.ここでは,Ctrl + F1 を押下時に,先ほど作成した「nas_server.ttl」が呼び出されるように設定する.
上記を実現するために,ファイルの末尾にある [User keys]
部分に以下を追記する.
; === User key definitions === [User keys] ; Ctrl + F1 key: connect nas server User1=1083,2,..\teraterm_macros\nas_server.ttl
上記を設定後,TeraTerm を開き,ショートカットキー(Ctrl + F1)を押下すれば,指定したマクロが呼び出され,サーバに接続できる.ショートカットキーを押下する際は,事前に「New connection」の画面を閉じること.
TeraTerm 起動時の設定の変更
上記の方法でショートカットキーによりサーバへ接続できるようになった.しかし,毎回表示される「New connection」を閉じる必要があることとショートカットキーを覚えておく必要がある.ここでは,この 2 点について改善する.
ショートカットキーの一覧を表示するマクロの作成
先ほど追加したショートカットキーを表示するマクロを作成する.ここでは,このマクロは teraterm-4.102
内に「startup_dialog.ttl」として保存する.
作成したマクロを以下に示す.
; refer to user key definitions sprintf2 msg '' strconcat msg 'Ctrl + F1: nas server\n' ; 今後追加する際は,この行を複製する ; convert special characters strspecial msg ; show status dialog statusbox msg 'server list' ; wait [sec] pause 3 ; close status dialog closesbox
設定ファイルの更新
teraterm-4.102
内にある TERATERM.INI
を開き,以下を修正する.
HostDialogOnStartup=on
をHostDialogOnStartup=off
に修正.これにより,「New connection」のダイアログが非表示になる.
StartupMacro=
をStartupMacro=startup_dialog.ttl
に修正.これにより,上記で作成した「ショートカットキーを表示するマクロ」が起動時に実行される.
起動時に表示される画面を以下に示す.このダイアログは 3 秒程度経過すると表示されなくなる.