Blog
Zoom録画の自動保存を作ってBasic認証でハマった4点。500エラーの原因はパスワードファイルの権限でした
Zoom講座の録画を、自動でサーバーへ保存してNotionに記録する仕組みを作りました。
仕組み自体は半日で組めたのですが、Basic認証まわりで4つつまずきました。 どれも検索してもすぐには出てこなかったので、原因と直し方を残しておきます。
エックスサーバーでの話ですが、Apacheを使う共有レンタルサーバーなら同じことが起きます。
作ったもの
月に2回オンライン講座をやっていて、終わるたびに同じ手作業が発生していました。録画をダウンロードし、サーバーに上げ、資料を用意し、一覧にリンクを貼る。1回30分ほどですが、「あとでやろう」が積み上がります。
そこでこう組みました。
Zoom(クラウド録画)
↓ 新しい録画を探して自動ダウンロード
レンタルサーバー(Basic認証つきのフォルダ)
↓ 動画リンクと資料リンクを書き込み
Notion(一覧データベース)
毎朝1回動き、新しい録画がなければ何もせず終わります。会議名で対象を判定するので、講座以外の録画は拾いません。
使った部品は3つだけです。
| やること | 使ったもの |
|---|---|
| 録画の一覧取得とダウンロード | Zoom API(Server-to-Server OAuth) |
| サーバーへの転送 | scp |
| 一覧に1行追加 | Notion API |
追加のライブラリは入れず、Python標準ライブラリとssh/scpだけで動いています。Zoomのアプリ作成もNotionのインテグレーション発行も無料でした。追加費用はゼロです。
ここまでは半日で組めました。つまずいたのは、この先の「限定公開」です。
配布先は受講者だけなので、置き場所にはBasic認証をかけます。この設定で4回ハマりました。
つまずき1:認証情報を送った瞬間だけ500になる
設定を入れて、ブラウザで開くとパスワードを聞かれる。ここまでは正常でした。
IDとパスワードを入力した瞬間、500 Internal Server Error。
何度入れ直しても同じ。パスワードが合っているかどうかに関係なく500になります。
原因は.htpasswdの権限でした。
-rw------- .htpasswd ← 所有者だけが読める。これがダメ
秘密の情報だから厳しくしよう、と600にしていました。ところがWebサーバーのプロセスは別のユーザーとして動いているため、このファイルを開けません。開けないので処理が失敗し、500になります。
chmod 644 /home/ユーザー名/example.com/.htpasswd
644にしたら解決しました。
「誰でも読める設定にして大丈夫なのか」と思うところですが、次の2つを満たしていれば問題ありません。
- 公開領域(public_html)の外に置く
- 中身は暗号化されたハッシュで、パスワードそのものではない
念のため、URLから読めないことも確認しておきます。
curl -o /dev/null -w "%{http_code}\n" https://example.com/.htpasswd
# 403 か 404 が返ればOK
エックスサーバーの管理画面が作るアクセス制限ファイルも644です。
つまずき2:401が返っても、守られているとは限らない
これが一番こわい落とし穴でした。
設定を確認するとき、こうやっていました。
curl -o /dev/null -w "%{http_code}\n" https://example.com/secret/
# 401 → よし、認証がかかっている
これでは分かりません。
「パスワードを入力してください」という401の応答は、パスワードファイルを1バイトも読まずに返せます。ファイルを読みにいくのは、利用者が「このIDとパスワードで入ります」と送ってきた瞬間だけです。
つまり、こうなります。
| 確認のしかた | 返るもの | 分かること |
|---|---|---|
| 認証情報を送らない | 401 | 壊れていても401。何も分からない |
| 間違ったパスワードを送る | 401なら正常/500なら異常 | ここで初めて実態が見える |
| 正しいパスワードを送る | 200 | 利用者が本当に入れるか |
3つ全部やって、はじめて「かかっている」と言えます。
U=https://example.com/secret/
curl -o /dev/null -w "認証なし: %{http_code}\n" "$U"
curl -o /dev/null -w "誤PW: %{http_code}\n" -u "user:wrong-password" "$U"
curl -o /dev/null -w "正PW: %{http_code}\n" -u "user:正しいパスワード" "$U"
期待する結果は 401 / 401 / 200 です。真ん中が500なら、設定が壊れています。
さらに大事なのが、守りたいファイルそのもののURLを叩くこと。 フォルダのトップだけ確認して安心すると、1階層下が無防備でも気づけません。
curl -o /dev/null -w "%{http_code}\n" -r 0-100 "$U/video.mp4"
動画やPDFなど、実際に守りたいファイルを名指しで確認してください。
つまずき3:ハッシュ形式で弾かれることがある
500の原因を探る過程で、ハッシュ形式も疑いました。結果としては権限が原因でしたが、環境によってはこちらが原因になります。
htpasswd -nbB user password # bcrypt。環境によっては検証できない
htpasswd -nbm user password # APR1-MD5。共有サーバーで無難
-B(bcrypt)はセキュリティ的には強いのですが、サーバー側のApacheが対応していないと検証に失敗します。共有レンタルサーバーでは-mが安全です。
ただし、症状は権限のときと同じ「認証情報を送ると500」です。まず権限を確認してから、形式を疑ってください。 私は順番を逆にして遠回りしました。
つまずき4:ファイルを差し替えた瞬間だけエラーになる
パスワードを変更するとき、.htpasswdを上書き転送していました。
転送には数秒かかります。その数秒間、ファイルは不完全な状態です。 ちょうどそのタイミングでアクセスがあると500になります。
別名で送ってからmvで置き換えると、切り替わりが一瞬で済みます。
scp .htpasswd server:/path/.htpasswd.new
ssh server "chmod 644 /path/.htpasswd.new && mv -f /path/.htpasswd.new /path/.htpasswd"
同じファイルシステム内のmvは、中身をコピーせず名前を差し替えるだけなので、途中経過が存在しません。設定ファイルの更新では基本にしておくと安心です。
おまけ:シェルスクリプトが黙って止まる
Basic認証とは別件ですが、同じ作業中にハマったので書いておきます。
パスワードを自動生成する処理で、スクリプトがエラーも出さずに途中で終了しました。
set -euo pipefail
PASS=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 16)
echo "できた: $PASS" # ここまで到達しない
headが16文字取った時点で終了すると、まだ書き込もうとしているtrにSIGPIPEが送られます。trの終了コードは141になり、pipefailとset -eの組み合わせでスクリプト全体が止まります。
正常な動作が異常終了として扱われるわけです。パイプを使わない方法に変えると解決します。
PASS=$(python3 -c "
import secrets, string
pool = string.ascii_letters + string.digits
print(''.join(secrets.choice(pool) for _ in range(16)))
")
set -euo pipefailは安全のために付けるものですが、こういう副作用があります。
Notionの記事を配布用HTMLに変換するときの注意
もう1つ、実用的な話を。
教科書はNotionで書いて、それを配布用のHTMLに変換しています。ここで注意が要るのが画像です。
Notion APIが返す画像URLは1時間ほどで期限切れになります。 そのままHTMLに埋め込むと、翌日には全部表示されなくなります。
対策は、変換時に画像を手元へダウンロードして、相対パスで貼り直すことです。ファイル名はブロックIDから生成すると、同じ画像に毎回同じ名前がついて差分が出ません。
stem = hashlib.sha1(block["id"].encode()).hexdigest()[:12]
name = f"img-{stem}.png"
設計として学んだこと
技術的な話とは別に、作り方の教訓が1つありました。
手順を飛ばせる作りにしない。
「保護をかける」→「ファイルを置く」という順番で運用するつもりでも、順番を守るかどうかを人間の注意力に任せていると、いつか飛びます。急いでいるとき、疲れているとき、割り込みが入ったとき。
いまは、保護の設定ファイルが存在しなければアップロード処理が止まるようにしています。手順書に書くのではなく、飛ばせない仕組みにするのが確実でした。
同じ考えで、作業のたびに前述の3つの確認(401/401/200)が自動で走るようにしています。チェックリストは飛ばせますが、自動で走るテストは飛ばせません。
まとめ
エックスサーバーでBasic認証がうまくいかないときの確認順序です。
.htpasswdの権限は644か(600だと500になる)- 置き場所は公開領域の外か。URLから叩いて403か404を確認
- ハッシュ形式は
htpasswd -nbm(APR1-MD5)か - 差し替えは別名転送+
mvになっているか - 間違ったパスワードを送って401が返るか(500なら未解決)
- 守りたいファイルのURLを直接叩いても401か
5番と6番を省くと、「かかっているつもり」のまま運用が始まります。ここだけは必ず確認してください。
TOMIFULLでは、AIを使った業務自動化の設計と、公開前の安全確認を支援しています。 「作ったけれど、これで本当に大丈夫なのか分からない」という段階のご相談も歓迎します。