Djangoのメール周りメモ
追記: 翔泳社さんでDjangoの書籍を出版するのでぜひ読んでみてください。
はじめに
この記事は 2015 tech-yuruyuru アドベントカレンダー - connpass の18日目です
メール周りの機能は基本的に↓のEmailのトピックページにまとまってはいるのですが、他のページに書いてある内容が必要になったりするのでその辺りのことも一緒にまとめてます。
開発中の設定
開発中は実際にメールを送信してほしくないので、その設定をします。 ドキュメントを見た感じ2つぐらい方法があるみたいです。
Dummy Backend
settings.pyで以下のように記述すると、メールを実際には送信せず送信内容等をコンソールに表示してくれます。
EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
SMTPサーバをたてる
以下のコマンドを打つと、localhostのリスニングポート1025でSMTPサーバがたちあがります。
python -m smtpd -n -c DebuggingServer localhost:1025
EMAIL_HOST
と EMAIL_PORT
をこれに合わせて設定しておくと、SMTPサーバは受け取った内容を端末に表示します。もちろんこの時送信はしません。
基本的にDummy Backendを使っておけばいい気がします。
メールの送信
準備
settings.pyに下記の設定を追記します。 EMAIL_PORTやEMAIL_HOSTは先程建てたSMTPサーバに合わせます。
# settings.py EMAIL_HOST = 'localhost' EMAIL_PORT = 1025 EMAIL_HOST_USER = '' EMAIL_HOST_PASSWORD = '' EMAIL_USE_TLS = False DEFAULT_FROM_EMAIL = 'system@example.com'
メールの送信 (send_mail)
「件名」「本文」「送信元メールアドレス」「送信先メールアドレス」の順で指定します。
# send_mail.py from django.core.mail import send_mail from <project_name> import settings def send_mail_for_inquiring(to_email): send_mail('問い合わせについて, 'お問い合わせありがとうございました。', settings.DEFAULT_FROM_EMAIL, [to_email], fail_silently=False)
なお送信先はリストで指定します(複数指定可)。
email_userメソッド
https://docs.djangoproject.com/en/1.9/ref/contrib/auth/#django.contrib.auth.models.User.email_user
django.contrib.auth.models
のUserクラスにもemail_user
メソッドというのが用意されています。
中ではsend_mail呼んでいるだけですが、宛先が1人だけならこちらを使えばよさそうです。
user.email_user('メールの件名', 'メールの本文')
メール送信周りのテスト
https://docs.djangoproject.com/en/1.7/topics/testing/tools/#email-services
django.core.mail.outbox
が送信されるメールを保持しますが、manage.pyのtestコマンド実行中は実際には送信されません。
またoutboxは各テストケースの開始時に初期化されます。
>>> from django.core import mail >>> mail.send_mail('件名', 'メッセージ', 'from@example.com', ['to@example.com'], fail_silently=False) >>> mail.outbox[0].subject 件名 >>> mail.outbox[0].message メッセージ
メールを使ったアカウント登録処理
ユーザが登録時に入力したメールアドレスにメールを送信し、そこに記載されているリンクを踏むとアカウントを有効化する。というサービスを結構みかけます。 1から実装するのは少し大変そうですが、django-registration-reduxというライブラリを使えば簡単に実装できます。
ドキュメントも丁寧に書かれてあるので英語が大丈夫な人はそちらで十分ですが、以前 id:ottati 先生がまとめてくださってたのでそちらを参考にすると良さそうです。
http://ottati.hatenablog.com/entry/2015/04/14/184637
以上です
DockerでHerokuの環境を再現する
はじめに
2015 tech-yuruyuru アドベントカレンダー - connpass 13日目です。
PythonのWebアプリを開発するとき、Mac上でそのまま開発することが多いんですがHerokuに近い環境で動作確認もしたい。 Heroku-toolbeltの中にDockerfileを生成してくれるコマンドがあるみたいなので試してみる。
ここをザッと読む限り app.json ファイルにプロジェクトの構成を記述すればHeroku-Toolbeltから簡単にDockerfileが生成出来るみたいなので試してみる。
Dockerfileの作成
heroku-toolbeltに heroku-docker
プラグインをインストール。
$ heroku plugins:install heroku-docker
python版のdocker-imageは↓にあるみたい。
app.jsonの用意
{ "name": "python/flask docker environment", "description": "Docker configulation for run python/flask application.", "image": "heroku/python", "addons": [ "heroku-postgresql" ] }
Procfileと他の設定ファイルも用意
Procfile
# Flask web: gunicorn manage:app -w 1 --log-file -
runtime.txt: デフォルトでは2系なので指定。もう3.5が使える。
python-3.5.0
requirements.txt
$ pip freeze > requirements.txt
Dockerfile生成
Dockerfileとdocker-compose.ymlを生成
$ heroku docker:init
実行
docker-machineを起動してIPアドレスをdocker-daemonに伝える(手順は省略)。
$ docker-compose up web $ open "http://$(docker-machine ip default):8080"
docker-compose up web
が何度も失敗して不思議だったけどdocker-machineをrestartしたら上手くいった。
ただしブラウザでアクセスしてもつながらない
python2系だと上手くいく。
その他
- shell
$ docker-compose run shell
- コンテナのrebuild
$ docker-compose build
- デプロイ
heroku docker:release
が使える。それ以外は今まで通り
$ heroku create # 登録 $ heroku docker:release # リリース $ heroku open # ブラウザで開く $ heroku logs # ログの確認 $ heroku run python # pythonの実行
おわりに
ギリギリ13日目...
今のところ、変更があるたびにdocker buildし直すのは面倒そうなので、開発は基本的にMac上で直接行うつもり。
Pythonにおけるハッシュ計算
- 作者: Michal Jaworski,Tarek Ziade,稲田直哉,芝田将,渋川よしき,清水川貴之,森本哲也
- 出版社/メーカー: KADOKAWA
- 発売日: 2018/02/26
- メディア: 単行本
- この商品を含むブログを見る
はじめに
ADVENTARのPythonアドベントカレンダー 4日目 です。 今作っているもの で後々必要になるかなと思い調べていたハッシュ関数について書きます。
ハッシュ関数について
ハッシュ関数は、任意長のデータ x
を与えると固定長のビット列 y
を返す関数です。
イメージとして↓のような感じになると思います。
H(x) -> y # Hはハッシュ関数、xは任意長のデータ、yは固定長のビット列
この時の特性として y
から x
を特性するのが困難な一方向性関数である必要があり、データの正当性を検証する際などに使われます。
代表的なハッシュ関数のアルゴリズムとして、 crc32
や md5
、 sha-1
、 sha-256
などがありますが、これらは目的に応じて使い分けます。
例えば crc32
はチェックサムのために設計されたもので、高速に動作し大きなファイルに有効です。そのためファイルが破損してないかどうかの確認によく利用されます。
ただし衝突が多くセキュリティの面では望ましくないようです。
一方、SHA-256は衝突が少なく非常に安全ですが動作は比較的遅く、速さと安全性はトレードオフとなっています。
Pythonのhashlibを触ってみる
Pythonの標準モジュールに含まれているhashlibの中にはMD5やSHA-1、SHA-256などのハッシュ関数が入っています。
>>> import hashlib >>> hashlib.md5(b'hello hello').hexdigest() 'f52d885484f1215ea500a805a86ff443' >>> hashlib.md5(b'hello hellO').hexdigest() '263ac69e8be54cdc5baeb3638a63709e'
文字列を少し変更するだけで全く違うハッシュ値が返ってきました。この特徴は y
から x
を特定されないために重要な特徴です。
ちなみにSHA-256を使いたければ hashlib.sha256
を使えば同じことが簡単にできます(sha-256なので256ビットのビット列が表示されるはずです)。
Cookieのハッシング
もう少し具体的な例を考えてみます。ここではHTTPのリクエストヘッダに↓を加えWebサイトに訪れた回数をクッキーに格納する例を考えます。
set-cookie: visits = s, [hash]
この時、ハッシュ関数に s
を与えた時の値が hash と一致すれば値は改ざんされていないとみなします。
コードにすると↓のようになります。
import hashlib def hash_str(s): return hashlib.md5(s).hexdigest() def make_secure_value(s): return "%s,%s" % (s, hash_str(s)) def check_secure_value(h): value = h.split(',')[0] if h == make_secure_value(value): return value
しかしこの例では、使っているアルゴリズムを推定して簡単に偽造が出来てしまうという大きな問題があります。
hmacを使ってみる
ハッシュ関数への入力 x
に文字列を加えると当然ですがハッシュ値は大きく変化します。さっきの方法ではアルゴリズムがバレれば容易に偽造できますが、入力 x
に文字列を足す場合、アルゴリズムが推測されてもこの文字列がばれない限り改ざんが出来ません。
さっきはhashlibを使ってましたが、pythonには標準でhmac(Hash-based Message Authentication Code)モジュールがあるので、そちらを利用します。
>>> import hmac >>> hmac.new(b'secret', b'shibata').hexdigest() '5ad21c9df4140462850c3c8cdbf37358'
hmacを使って書き換えると↓のようになります。
import hmac SECRET = 'secretstring' def hash_str(s): return hmac.new(SECRET, s).hexdigest() def make_secure_value(s): return "%s|%s" % (s, hash_str(s)) def check_secure_value(h): value = h.split('|')[0] if h == make_secure_value(value): return value
これで、SECRETの文字列とアルゴリズムがバレないかぎり、偽造出来ないようになりました。
もう少し考えるとすればSECRETを工夫する必要がありそうです。 具体的にはユーザIDなどから、攻撃者に推測されないユーザ固有の文字列を生成すると良さそうです。 これについて興味のある方は「Salt 作り方」とかで調べれば出てくると思います。 今度それについても書きたい
おわりに
この記事が今年初のAdvent Calender。ためてる内容がいくつかあるのでこの調子で減らしていきたい。