Slide 1

Slide 1 text

Djangoのパスワードハッシュア ルゴリズムで PyramidのWebアプリケーショ ン作った 2020-05-18 mizzsugar0425 @BPLL

Slide 2

Slide 2 text

お前誰よ ● みずきと申します。 ● Twitter @mizzsugar0425 ● PythonでWebサービス開発してます。(本業Django, 副業Pyramid) ● PyConJPのCfPの締切に焦ってます(;´Д`)

Slide 3

Slide 3 text

今日話す話の対象となる人 ● ある程度Djangoを使ったWebアプリケーションまたはサービスの開発をできる 人

Slide 4

Slide 4 text

今日話すこと ● Djangoデフォルトパスワードアルゴリズムの説明(ざっくり) ● Djangoのソースコードを一部適用させたのでライセンスの話 ● 作ったアプリでどうやってパスワードアルゴリズムを適用させたか ● 爆誕させたもの↓ https://github.com/mizzsugar/pyramid-app-with-django-password-hash

Slide 5

Slide 5 text

きっかけ

Slide 6

Slide 6 text

最近思うこと

Slide 7

Slide 7 text

Djangoは制約が多すぎて辛い

Slide 8

Slide 8 text

そうだ、リプレースすればいいんだ(雑)

Slide 9

Slide 9 text

いろいろやらないといけないこ とあるけど・・・ migrationファイルどーするの ログインユーザーのトークンとか ORMで発行したクエリ その他Djangoにまかせた何か

Slide 10

Slide 10 text

Djangoデフォルトの パスワードハッシュアルゴリズムを 他のWebフレームワークで使うには どうすれば?

Slide 11

Slide 11 text

Djangoのパスワードの仕組みたどってみた from django.contrib.auth.models import User user = User.objects.create(email='sample.example.com') user.set_password('password') # パスワードハッシュ user.save() # return True user.check_password('password') # パスワードチェック

Slide 12

Slide 12 text

check_password, set_passwordが エントリポイントらしいぞ

Slide 13

Slide 13 text

たどったら django.contrib.auth.hashersモジュールの check_password(上), make_password(下) にたどり着いた。 https://github.com/django/django/blob/master/django/contrib/auth/hashers. py#L30 https://github.com/django/django/blob/master/django/contrib/auth/hashers. py#L64

Slide 14

Slide 14 text

色んなハッシュアルゴリズム使えるよう settings.pyのPASSWORD_HASHERSで使いたいパスワードアルゴリズムを定義 PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', ] デフォルトのsettings.pyのPASSWORD_HASHERS https://docs.djangoproject.com/ja/3.0/topics/auth/passwords/#how-django-stores-passwords

Slide 15

Slide 15 text

デフォルトではどのアルゴリズムが? PASSWORD_HASHERS = [ 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.Argon2PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', ] このリストを読み込んで一番上のアルゴリズムのクラスを使う仕組み。 デフォルトだとdjango.contrib.auth.hashersモジュールの PBKDF2PassswordHasherクラスを使うよう。

Slide 16

Slide 16 text

これを適用するには? PBKDF2PassswordHasherクラスをコピペするのが楽だけど ライセンス大丈夫?

Slide 17

Slide 17 text

Djangoは3-clause BSD-3-Clauseライセンス 以下の条件下なら修正あり、なしに関わらずソースコードの再配布OK 1. ソースコードの再配布は、上記の著作権表示、ここに列挙された条件、および下記の免責条項を保 持すること。 2. バイナリ形式の再配布は、上記の著作権表示、ここに列挙された条件、および下記の免責条項は、ド キュメントまたは他の資料で配布すること。 3. このソフトウェアのコントリビューター (貢献者)の名前は、特定の書面による事前の許可なしに、このソ フトウェアから派生した製品の保証または販売促進のために使用してはいけない。 https://opensource.org/licenses/BSD-3-Clause

Slide 18

Slide 18 text

リポジトリにDjangoライセンス明記 https://github.com/mizzsugar/pyramid-app-with-django-password-hash/blob/ master/LICENSE.Django

Slide 19

Slide 19 text

今回作ったリポジトリ自体のライセンス ● BSD-3-Clauseにした ● Django自身のライセンスよりもゆるくするのは怖いのでやめといた

Slide 20

Slide 20 text

今回作ったもののディレクトリ構成 ● Djangoから取り入れた部分はビジネスロジックとは切り離してライブラリとして導 入する方針 ● ビジネスロジックではmake_password, check_passwordを呼び出すだけにす る

Slide 21

Slide 21 text

├── LICENSE ├── LICENSE.Django ├── README.rst └── application ├── Makefile ├── migrations │ └── 0001_core.sql ├── mypy.ini ├── openapi.yaml ├── poetry.lock ├── sample │ ├── __init__.py │ ├── application.py │ ├── bootstrap.py │ ├── domain │ │ ├── __init__.py │ │ └── authentication.py(viewで呼び出す認証処理書いてる ) │ │ │ ├── libs │ │ ├── __init__.py │ │ ├── crypto.py(make_password, check_passwordに必要) │ │ ├── encoding.py(make_password, check_passwordに必要) │ │ ├── module_loading.py(make_password, check_passwordに必要) │ │ └── password.py(ここにmake_password, check_passwordがある) │ ├── repository │ ├── scripts │ ├── views │ └── wsgi.py └── tests ※一部省略しています。

Slide 22

Slide 22 text

パスワードチェックの実装 def sign_in(self, draft: sample.dto.SignIn) -> sample.domain.dto.User: try: user = self._repository.authentication.fetch_by_email(draft.email) # DBアクセス except sample.repository.exceptions.NotFoundError: raise sample.domain.exceptions.InvalidCredentialError() if not sample.libs.password.check_password(draft.password, user.password): raise sample.domain.exceptions.InvalidCredentialError() return sample.domain.converters.User.from_repository(user)

Slide 23

Slide 23 text

パスワードを暗号化する実装 def register_user(self, draft: sample.domain.dto.DraftUser) -> None: password = sample.libs.password.make_password(draft.password) # パスワードハッシュ try: self._repository.authentication.register_user( sample.repository.dto.DraftUser(email=draft.email, password=password)) # DBに登録 except sample.repository.exceptions.ConflictError: raise sample.domain.exceptions.AlreadyRegisteredError()

Slide 24

Slide 24 text

これからしたいこと ● Djangoから得た部分のTypeHint ● CIの設定 ● パスワードのところもうちょっとテスト追加 ● iterationの回数の増加タイミング見極め ● ログインした際のトークン発行

Slide 25

Slide 25 text

ありがとうございました。