cocuh's note

type(あうとぷっと) -> 駄文

Pyramidのauthentication policyで気になったこととその実装

groupを使っている時にuseridとgroup名が同じ場合、認可されてしまう気がしたのでちょっと調べたことです。

結論としては、callbackでgroupfinderを叩かない、
もしくは、AuthenticationPolicyは自前で実装するほうが安全だと思います。

誤解していなければ問題はないとは思いますが。。。



問題のコードはこちら(一部略)
pyramid/pyramid/authentication.py at master · Pylons/pyramid · GitHub

class CallbackAuthenticationPolicy(object):
    """ Abstract class """
    
    (略)

    def effective_principals(self, request):
        effective_principals = [Everyone]
        userid = self.unauthenticated_userid(request)

        if userid is None:
            return effective_principals

        if self._clean_principal(userid) is None:
            return effective_principals
            
        if self.callback is None:
            groups = []
        else:
            groups = self.callback(userid, request)

        if groups is None: # is None!
            return effective_principals

        effective_principals.append(Authenticated)
        effective_principals.append(userid)
        effective_principals.extend(groups)

        return effective_principals

turtorialではこう書いてある
Adding Authorization — The Pyramid Web Application Development Framework v1.4.5

  • security.py
USERS = {'editor':'editor',
          'viewer':'viewer'}
GROUPS = {'editor':['group:editors']}

def groupfinder(userid, request):
    if userid in USERS:
        return GROUPS.get(userid, [])
  • models.py(wiki class)
class Wiki(PersistentMapping):
    __name__ = None
    __parent__ = None
    __acl__ = [ (Allow, Everyone, 'view'),
                (Allow, 'group:editors', 'edit') ]
  • __init__.py
def main(global_config, **settings):
    authn_policy = AuthTktAuthenticationPolicy(
        'sosecret', callback=groupfinder, hashalg='sha512')
    authz_policy = ACLAuthorizationPolicy()
    config = Configurator(root_factory=root_factory, settings=settings)
    config.set_authentication_policy(authn_policy)
    config.set_authorization_policy(authz_policy)
    config.include('pyramid_chameleon')
    config.add_static_view('static', 'static', cache_max_age=3600)
    config.scan()
    return config.make_wsgi_app()

もしuseridに'group:editors'が入ったら危なくないですか…('A`)

調べたところ

https://groups.google.com/forum/#!topic/pylons-discuss/ObPtBp67f0U

(中略)
Thus it's recommended that you store an integer for the user id, or something that would not conflict accidentally with your ACLs.

user_idをintでしといたほうがいいとなるほど。
でもそういうことあまり書かれてないような…
というかBasicAuthAuthenticationPolicyとかはそうしてないんじゃないっすか…
でもusername, password = auth.split(':', 1)してるからいいのかな…

自分でauthentication policy書くとき

pyramid cookbookにいい感じのテンプレートがあったのでこうするとよさげ

http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/auth/custom.html

class MyAuthenticationPolicy(object):
    (略)
    def effective_principals(self, request):
        principals = [Everyone]
        user = request.user
        if user:
            principals += [Authenticated, 'u:%s' % user.id]
            principals.extend(('g:%s' % g.name for g in user.groups))
        return principals

私の場合の実装

身内で使うものだったのであまりセキュリティ高くなくても良いもので

digestっぽい感じでこのhashの整合性を確かめる風にしてます
policyはこんな感じに
https://github.com/cocu/otodo/blob/master/otodo/security.py

@implementer(IAuthenticationPolicy)
class APIAuthenticationPolicy(object):
    (略)
    def effective_principals(self, request):
        effective_principals = [Everyone]
        username = self.authenticated_userid(request)
        
        if username:
            effective_principals.append('user:{username}'.format(username=username))
            effective_principals.append('group:user')
        return effective_principals

これでたぶん大丈夫かな…