WSGIでダイジェスト認証
DigestAuth Middlewareの紹介
tags: | python, web, tips |
---|---|
created: | 2008-04-11T04:18:44 |
ダイジェスト認証のほうが安全に認証できるらしいので、 WSGIでダイジェスト認証をするミドルウェアを作成してみました。
ダイジェスト認証のほうが安全に認証できるらしいので、 WSGIでダイジェスト認証をするミドルウェアを作成してみました。
概要
ダイジェスト認証というものがほとんどのブラウザで利用可能らしいので、 ガンガン使えるようにミドルウェアにしてみました。
ダイジェスト認証の特徴
ベーシック認証に比べると、
- パスワードをハッシュキー化して送る。
- ハッシュキーにはランダムキーが含まれる。
- トライ毎にインクリメントするコードを含む。
といった点からより安全に認証できるという利点があります。
ただ、クライアント側から見ると
- 必ず一度認証接続失敗を受け取らなければならないので2度手間がかかる。
というデメリットもあります。
ただ、多くのブラウザはサポートしているのでちゃんと ユーザーからIDとパスワードのダイアログを表示して、 その2度手間を実行してくれます。
ソースコード
- wsgiauth.py
#!/usr/local/bin/python # -*- encoding: utf-8 -*- import sys import os import traceback import webob import md5 import random from ConfigParser import ConfigParser, NoOptionError class DigestAuthMiddleware: def __init__(self, app, filename=u'users.conf', realm='admin-page'): self.app = app self.filename = filename self.realm = realm self.users = ConfigParser() try: assert self.users.read(filename) except: print 'read error :', filename self.add_user('admin', 'admin') def add_user(self, username, password, realm=None): if realm==None: realm = self.realm a1 = md5.new('%(username)s:%(realm)s:%(password)s' % locals()).hexdigest() if not self.users.has_section(realm): self.users.add_section(realm) self.users.set(realm, username, a1) self.users.write(open(self.filename, 'w')) def del_user(self, username, realm=None): if realm==None: realm = self.realm self.users.remove_option(realm, username) self.users.write(open(self.filename, 'w')) def make_auth_header(self): values = [] values.append('realm="%s"' % self.realm) chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' salt = ''.join([random.choice(chars) for i in range(11)]) nonce = '%s=%s' % (salt, md5.new(salt).hexdigest()) values.append('nonce="%s"' % nonce) values.append('algorithm=MD5') values.append('qop="auth"') return ('WWW-Authenticate', 'Digest ' + ', '.join(values)) def check(self, environ): req = webob.Request(environ) authorization = req.headers.get('Authorization', '') if authorization: try: authtype, valstr = authorization.strip().split(' ', 1) assert authtype.strip().lower()=='digest' values = dict([ [j.strip().strip('"') for j in i.split('=', 1)] for i in valstr.split(',')]) values['method'] = req.method values['uri'] = req.path_info values['a1'] = self.users.get(values['realm'], values['username']) values['a2'] = md5.new('%(method)s:%(uri)s' % values).hexdigest() response = md5.new('%(a1)s:%(nonce)s:%(nc)s:%(cnonce)s:%(qop)s:%(a2)s' % values).hexdigest() return values['response']==response except NoOptionError: pass return False def __call__(self, environ, start_response): try: assert self.check(environ) return self.app(environ, start_response) except AssertionError: headers = [self.make_auth_header()] headers.append(('Content-Type', 'text/html; charset=utf-8')) start_response('401 Authorization Required', headers) return 'Authorization Required.' except: headers = [] headers.append(('Content-Type', 'text/html; charset=utf-8')) start_response('500 Internal Server Error', headers) return ''.join([ '<pre>', ''.join(traceback.format_exception(*sys.exc_info())), '</pre>' ]) def __main(): def application(environ, start_response): headers = [] headers.append(('Content-Type', 'text/html; charset=utf-8')) start_response('200 OK', headers) return 'hello!' from wsgiref.simple_server import make_server app = DigestAuthMiddleware(application) app.add_user('admin', 'hogefoo') httpd = make_server('', 8000, app) httpd.serve_forever() if __name__=='__main__': __main()
使い方
import wsgiauth
from wsgiref.simple_server import make_server
application = あなたのアプリ
authapp = DigestAuthMiddleware(application)
authapp.add_user('admin1', 'hogefoo')
authapp.add_user('admin2', 'hogege')
httpd = make_server('', 8000, authapp)
httpd.serve_forever()
add_userを呼び出すごとに 「users.conf」というファイルにユーザーアカウント情報が保存されます。
users.confの中身はiniファイル形式になっていますが、 パスワードは認証ハッシュキー化されていますので手書きではアカウントは作れません。
まとめ
最近WSGIが面白くていろいろ実験してます。
このサイトの動的な部分はほとんどWSGIを利用して構築しています。 簡易データストレージや、XML-RPC&ダイジェスト認証などをつかった管理、 ローカルでテストでき、そのままレンタルサーバにデプロイすることも出来ます。 ローカルでテストできるようにするため、CGIラッパーミドルウェアも作りました・・・。 これらの仕組みと今話題の Google App Engine がそっくりすぎてびっくり!! なんてこったい! 当然スケーラビリティは Google App Engine の方が圧倒的に高いじゃん!
でも、Mercurialをつかったデプロイが出来るので乗り換えるほどのメリットはないですね。 (このサイトでそんなにトラフィックがあるわけでもないので)
このダイジェスト認証、WSGIミドルウェアになっていますので、 Google App Engine などでも使えるはずですよ~