Expressの続きです。前回はHerokuとGitHubを連携して自動デプロイしました。環境変数をGitHubのソースから除外しつつHerokuで環境変数の値を表示しました。今回はHerokuの標準DBであるPostgreSQLのデータを表示したく思います。DBの設定情報は同じく環境変数に設定します。それではいきましょう!
【目次】
- 前回のおさらい
- ExpressにPostgreSQLを追加すると環境設定が自動設定される
- 環境変数のPostgreSQL設定情報を確認
- Herokuの環境変数をローカルの.envファイルに同期
- pgモジュールをインストール
- index.jsでpgモジュールを設定
- ExpressでPostgreSQLデータを処理
- GitHubにコミットをプッシュ→Herokuにデプロイ
- SSL接続エラー発生!
- SSL設定を変更(rejectUnauthorized: false )
- rejectUnauthorized: falseについて調べたら怖いこと書いてある
- SSL設定するための試行錯誤(結果はうまくいかず)
- SSL設定に「sslmode: 'require'」を追加
- 最後に
※参考:前回記事
【Express】HerokuとGitHubを連携して自動デプロイ(環境変数は除外) - クモのようにコツコツと
※参考:Node.js / Expressを習得するためにやったことまとめ
qiita.com
前回のおさらい
HerokuとGitHubを連携して自動デプロイ。Herokuの環境変数をGitHubのソースから除外しつつ値(ケンシロウ)をHerokuの画面で表示。
今回はHerokuの標準DBであるPostgreSQLのデータを表示したい。PostgreSQLの設定情報もGitHubには含めたくないので環境変数に設定する。
HerokuのPostgreSQLをターミナルからCRUD操作した記事も参照。
※参考:【SQL】ターミナルからHerokuのPostgreSQLに接続する - クモのようにコツコツと
※参考:【SQL】ターミナルからHerokuのPostgreSQLにCRUDする - クモのようにコツコツと
ExpressにPostgreSQLを追加すると環境設定が自動設定される
HerokuのExpressアプリでPostgreSQLを使用する方法、こちらのチュートリアルを参考に進める。
※参考:Heroku スターターガイド (Node.js) | Heroku Dev Center
以前、こちらの記事でPostgreSQLをアプリに追加した。
下記のコマンド。
$ heroku addons:create heroku-postgresql:hobby-dev
※参考:【SQL】ターミナルからHerokuのPostgreSQLに接続する - クモのようにコツコツと
上記を実行すると環境変数の中にPostgreSQLの設定情報「DATABASE_URL」が自動的に設定されるらしい。
環境変数のPostgreSQL設定情報を確認
まずターミナルのcd
コマンドでアプリのあるフォルダに移動
$ cd /(パス)/node-js-getting-started
環境変数を確認するコマンドはこちら。
$ heroku config
※参考:【Express】HerokuとGitHubを連携して自動デプロイ(環境変数は除外) - クモのようにコツコツと
結果
=== aqueous-mountain-80366 Config Vars DATABASE_URL: postgres://(ユーザ名):(パスワード)@(ホスト名):(ポート番号)/(DB名) NAME: ケンシロウ TIMES: 10
DATABASE_URL
が追加されている!これがPostgreSQLの設定情報のようだ。
DATABASE_URL
の値はすごく長いが、DBのSettingを見ると意味がわかる。
「Database Credentials」の「View Credentials…」を開くと…
DBの各項目の値が表示されている。
この値とURLを照合すると下記のような並びになっているのがわかる。
DATABASE_URL: postgres://(ユーザ名):(パスワード)@(ホスト名):(ポート番号)/(DB名)
Herokuの環境変数をローカルの.envファイルに同期
ブラウザのHerokuの「Config Vars」を見るとDATABASE_URL
が追加されている!
しかし、ローカル環境の.envファイルを開くとDATABASE_URLはまだない。
# this file was created automatically by heroku-config NAME="ケンシロウ" TIMES="10"
リモートのHerokuの環境変数をローカルの「.env」ファイルにプルする。
$ heroku config:pull
※参考:【Express】heroku configでHerokuに環境変数を設定(.envファイルとHerokuの同期) - クモのようにコツコツと
結果、おっ、成功したっぽい♪
Successfully wrote config to ".env"!
「.env」ファイルを確認すると、DATABASE_URL
が同期された!
# this file was created automatically by heroku-config DATABASE_URL="postgres://(ユーザ名):(パスワード)@(ホスト名):(ポート番号)/(DB名)" NAME="ケンシロウ" TIMES="10"
pgモジュールをインストール
ExpressでPostgreSQLを利用するにはpgモジュールが必要なようだ。
node-postgres
Non-blocking PostgreSQL client for Node.js. Pure JavaScript and optional native libpq bindings.
node-postgres Node.js用のノンブロッキングPostgreSQLクライアント。 純粋なJavaScriptとオプションのネイティブlibpqバインディング。
正式名は「node-postgres」かな。
※参考:pg - npm
pg
モジュールをインストールする。
$ npm install pg
package.jsonを確認。追加された!
"dependencies": { "ejs": "^2.7.4", "express": "^4.17.1", "pg": "^8.4.1" },
index.jsでpg
モジュールを設定
まず冒頭でpg
モジュールのインポートする。
const { Pool } = require('pg');
変数pool
でPool
を起動。オプション設定も。
const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: true });
- 変数
pool
でPool()
のインスタンス作成 Pool()
の引数はオブジェクト(連想配列)でオプションを設定するconnectionString
キーの値はprocess.env.DATABASE_URL
ssl
キーの値はtrue
環境変数の読み込み方(process.env.HOGE
)は以前と同じだ。
※参考:【Express】heroku configでHerokuに環境変数を設定(.envファイルとHerokuの同期) - クモのようにコツコツと
ExpressでPostgreSQLデータを処理
ExpressでPostgreSQLのデータを読んでブラウザに返す設定
.get('/db', async (req, res) => { try { const client = await pool.connect() const result = await client.query('SELECT * FROM member'); const results = { 'results': (result) ? result.rows : null}; res.render('pages/db', results ); client.release(); } catch (err) { console.error(err); res.send("Error " + err); } })
get()
メソッドの第一引数はパスで/db
第二引数はasync/await構文で無名関数を実行(引数はreq
,res
)- 無名関数の処理はtry...catch文を実行
tryでは変数client
でpool
をconnect()
その後(await
)、変数result
でclient
からSELECT文でテーブルmember
のデータを読み込む
その後(await
)、変数results
で無名関数作成
無名関数のresults
キーの値はresult
があればそれを、無ければrows
がnull
と入れる
res.render()
でレンダリング実行。引数でpages/db
にresults
を送る
client.release()
を実行 catch()
の引数はerr
コンソールにエラー内容err
を表示
res.send()
で「Errorエラー内容err
」というレスポンスを返す
基本的にはチュートリアル通りだがテーブル名のみmember
に変えている。以前下記の記事で作ったテーブル。
※参考:【SQL】ターミナルからHerokuのPostgreSQLにCRUDする - クモのようにコツコツと
async/await構文はthen()のように非同期で処理の順番をコントロールする構文。
※参考:【JS】async/await構文で書いたFetch APIからJSONデータを読み込む - クモのようにコツコツと
try...catch文は例外処理でtryの処理が失敗した時にcatchを実行する。
※参考:try...catch 文 - JavaScript | MDN
GitHubにコミットをプッシュ→Herokuにデプロイ
GitHubにコミットをプッシュする。下記の3つのコマンドを実行
$ git add . $ git commit -m "PostgreSQLと接続" $ git push -u origin main
GitHubに反映された!
※参考:PostgreSQLと接続 · ryo-i/Express-Heroku-Test@3439663 · GitHub
GitHubとHerokuは連携しているため、Herokuの方にも修正内容がデプロイされる。
※参考:【Express】HerokuとGitHubを連携して自動デプロイ(環境変数は除外) - クモのようにコツコツと
SSL接続エラー発生!
Herokuアプリをブラウザで表示してみる。先ほどExpressで設定したように、トップページではなく下層パス「/db」を開く。
ありゃ?エラーになっているぞ。
「Error: self signed certificate(エラー:自己署名証明書)」と。
ローカルでも起動してみる。ポート番号は5000で下層パス「/db」
$ heroku local web
ローカルも同じエラーだ。
どうもエラーメッセージの意味を調べるとSSL通信のエラーのようだ。
SSL通信というとURLを「https」にしてサーバとのデータ通信を暗号化するイメージ。
※参考:SSL/TLSってなんだろう?|SSL/TLS-総合解説サイト
HerokuのPostgreSQLとの通信もこれが必要で、それがエラーになっていると。
SSL設定を変更(rejectUnauthorized: false )
調べてみて多く見られた方法は下記だった。
const pool = new Pool({ connectionString: process.env.DATABASE_URL, // ssl: true, ssl: { rejectUnauthorized: false } });
ssl
の設定をtrue
からrejectUnauthorized: false
に変更。
「reject Unauthorized(不正を拒否する)」をfalse(無効)にするんだ…不正を拒否しない。。気になる項目だ(次項で掘り下げます)
※参考:Herokuアップデートを実行後、node-postgres(pg)のバージョンが上がりWEBアプリ(Express)が死んだ話 | 神戸システムデザイン
まずはローカルでHerokuを起動してみる。
$ heroku local web
おぉ、表示された!
GitHubにコミットをプッシュ
$ git add . $ git commit -m "SSL設定変更テスト" $ git push -u origin main
ブラウザのHerokuでも表示された!
※参考:ssl設定変更テスト · ryo-i/Express-Heroku-Test@1010c08 · GitHub
ちなみにこの画面はEJSテンプレートを表示している。
先ほどindex.jsのres.render()
で指定しているpages/db.ejs
res.render('pages/db', results );
この部分でresults
のid
とname
を読み込んでいる!
<ul> <% results.forEach(function(r) { %> <li><%= r.id %> - <%= r.name %></li> <% }); %> </ul>
rejectUnauthorized: falseについて調べたら怖いこと書いてある
rejectUnauthorized: false
(「不正を拒否する」を無効化)でページが表示されたのはいいのが、これってSSL通信設定true
を無効にてるんじゃ??
気になるので調べてみる。
TLS what exactly does 'rejectUnauthorized' mean for me?
TLS「rejectUnauthorized」は私にとって正確にはどういう意味ですか?
By setting rejectUnauthorized: false, you're saying "I don't care if I can't verify the server's identity." Obviously this is not a good solution as it leaves you vulnerable to MITM attacks.
rejectUnauthorized: false を設定すると、「サーバーのIDを確認できなくてもかまいません」と表示されます。明らかに、これはMITM攻撃に対して脆弱なままになるため、適切なソリューションではありません。
怖いこと書いてるなー。。
※参考:https://stackoverrun.com/ja/q/8774469
「MITM攻撃」とは中間者攻撃(Man In The Middle Attack)のこと。
サイバー攻撃の中でも、中間者攻撃とは二者間の通信を特別なソフトウェアなどの不正な手段を用いて傍受、盗聴して内容を取得するといったものです。
※参考:中間者攻撃(Man In The Middle Attack)とは?仕組みや危険性、対策について徹底解説
怖いぞー。何とかならないか。
SSL設定するための試行錯誤(結果はうまくいかず)
SSL設定の成功法はHerokuを有料プランにすることで実現できるようだ。
Heroku「Settings」の「SSL Certificates」を見るとこうある。
Configure SSL
Upgrade to paid dynos to configure Heroku SSL
SSLを構成する 有料dynoにアップグレードして、HerokuSSLを構成します
フリープランでは「Let's Encrypt」や「Cloudflare」などの外部サービスと連携する方法があるらしい。
※参考:HerokuアプリにSSLを設定する方法 | Casual Developers Note
フリープランかつ、Heroku内で完結する方法がないか、もう少し調べる。
環境変数を追加する方法
Heroku 上の環境変数に PGSSLMODE=require もしくは PGSSLMODE=allow
※参考:「pg」パッケージを使ってローカルの PostgreSQL や Heroku Postgres に接続する - Corredor
index.jsのSSL設定を元に戻す
const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: true, // ssl: { rejectUnauthorized: false } });
環境変数にPGSSLMODEを設定してみる
$ heroku config:set PGSSLMODE=allow
結果、追加された!
ローカルの.envファイルにもプル
heroku config:pull
結果、追加された!
# this file was created automatically by heroku-config DATABASE_URL="postgres://(ユーザ名):(パスワード)@(ホスト名):(ポート番号)/(DB名)" NAME="ケンシロウ" PGSSLMODE="allow" TIMES="10"
しかし、Herokuの画面の表示は 「Error Error: self signed certificate」のまま…
DBのパスにパラメータ?sslmode=require
を追加する方法
ほとんどのクライアントはデフォルトで SSL で接続しますが、Postgres 接続で sslmode=require パラメータを設定する必要がある場合もあります。環境設定を直接編集するのではなく、コードにこのパラメータを追加してください。特に Java クライアントまたは Node.js クライアントを使用している場合は、SSL の使用を強制していることを確認してください。
※参考:Heroku Postgres | Heroku Dev Center
環境変数DATABASE_URL
の値にパラメータを追加
$ heroku config:set DATABASE_URL=postgres://(ユーザ名):(パスワード)@(ホスト名):(ポート番号)/(DB名)?sslmode=require
結果、エラーで追加できない。
Cannot overwrite attachment values DATABASE_URL.
ブラウザでやってみる。「Settings」の「Config Vars」で「DATABASE_URL」の鉛筆アイコンを押すと編集のポップアップが出る。
?sslmode=require
を追記してみるが…
変更できない。。
どうやら「DATABASE_URL」は上書きできない仕様のようだ。
※参考:HerokuでDATABASE_URLを変更しようとしたらCannot overwrite attachment values DATABASE_URLと言われる
証明書および中間証明書の手動アップロードする方法
certs:add コマンドで、証明書、中間証明書のバンドル、およびプライベートキーを追加します。
※参考:Heroku SSL | Heroku Dev Center
下記のコマンド
$ heroku certs:add server.crt server.key
結果、そんなファイルはないよ、と。。
ENOENT: ENOENT: no such file or directory, open 'server.key'
SSL設定に「sslmode: 'require'」を追加
おそらくフリープランで一番最善な方法はssl
のオプションにsslmode: 'require'
を追加することと思われる。
Is it safe to set rejectUnauthorized to false when using Heroku's Postgres database?
HerokuのPostgresデータベースを使用するときにrejectUnauthorizedをfalseに設定しても安全ですか?
My understanding is that in this case, the best you can do is:
- Your connection is encrypted and you are protected from eavesdropping
- Not protected against Man-In-The-Middle (MITM) attacks
This is the highest level without certificate validation, according to the table here:
私の理解では、この場合、あなたができる最善のことは次のとおりです。 * 接続は暗号化されており、盗聴から保護されています * Man-In-The-Middle(MITM)攻撃から保護されていません ここの表によると、これは証明書の検証なしの最高レベルです。
require
モードの詳細についてはこちら。
※参考:PostgreSQL: Documentation: 13: 33.18. SSL Support
ssl
のオプションにsslmode: 'require'
を追加してみる。
ssl: { sslmode: 'require', rejectUnauthorized: false }
※参考:SSL設定変更 · ryo-i/Express-Heroku-Test@42d9b23 · GitHub
GitHubにコミットをプッシュ
$ git add . $ git commit -m "SSL設定変更" $ git push -u origin main
ローカル環境のHeroku、表示が復活!
ブラウザのHeroku、表示が復活!
Herokuアプリ
※参考:Node.js Getting Started on Heroku
GitHub(PostgreSQLの設定情報は含まれていない!)
※参考:GitHub - ryo-i/Express-Heroku-Test at 42d9b23ca7d5dcf02c3003be3d8b2ba120272456
最後に
ということでHerokuのExpressアプリとPostgreSQLを連携できましたー。GitHubにはPostgreSQLを含まずにアプリの画面にDBのデータを表示することができました。
SSLの設定がフリープランだとうまくできなかったのが残念でしたが、SSLやセキュリティのことを調べる機会にはなりました。
今回はHerokuのチュートリアルとほぼ同じ内容でしたが、次回からは独自のコードでブラウザのフォームと連携したCRUD処理をやってみたく思います。
それではまた!
※参考:Node.js / Expressを習得するためにやったことまとめ
qiita.com