检查字符串是否是任何URL模式的前缀

时间:2018-05-31 03:47:12

标签: django django-urls

不建议与否,我的Django网站会在每个用户的根目录分配一个页面,例如/rgov

我使用字符集白名单,因此应该阻止创建index.html或邪恶的 。我的URL配置也最后路由用户页面,因此不应该通过注册相应的名称来劫持/admin或其他任何内容。

但是,我想阻止用户注册admin,因为他们的网页会被破坏。

Similar question,没有理想的解决方案,如下面的部分所述。)

这是我的尝试:

def is_reserved(username):
  r = urls.resolvers.get_resolver('mysite.systemurls')
  hit = False
  for path in ('/{}', '/{}/'):
    try:
      r.resolve(path.format(username))
      hit = True
      break
    except urls.exceptions.Resolver404:
      continue
  return hit

此处,mysite.systemurls模块定义除用户页面之外的每个URL模式。

这会阻止选择用户名admin,因为有/admin/定义的路由。但它不会阻止api,因为虽然有/api/foo/bar,但/api/没有路由。

有没有办法测试是否有一个后缀为/api/的路由(例如)?由于URL模式是正则表达式,可能不是那么容易,但从理论上讲它应该是可能的。

1 个答案:

答案 0 :(得分:0)

这是我不雅的解决方案,对眼睛很抱歉。我使用Django系统检查框架实现了检查。该检查收集应用程序使用的所有URL模式,然后从每个URL模式中提取第一个路径组件。然后确保没有那些第一个路径组件

如果您的网址格式中有一些re_path会破坏正在做出的假设,那么这将不起作用。

import re

from django import urls
from django.core.checks import register, Error, Tags, Warning

from . import usernames


@register(Tags.urls, Tags.security)
def check_scary_available_usernames(app_configs=None, **kwargs):
  '''
  Checks that there are no URL patterns /x/y where /x itself is not a pattern.
  In this case, /x might be available for user registration, which would be bad.
  '''
  errors, prefixes = [], set()
  r = urls.resolvers.get_resolver('mysite.systemurls')
  descend_into_resolver(r, [], errors, prefixes)

  # Check to make sure none of these usernames is taken or available
  for prefix in prefixes:
    if not prefix:
      continue
    if not usernames.is_reserved(prefix):
      errors.append(Warning(
        'There is no restriction on registering the forbidden username {}, '
        'which would conflict with a URL in use by the system.'.format(prefix)
      ))
    if usernames.user_exists(prefix):
      errors.append(Warning(
        'A user has the forbidden username {}, which conflicts with a URL in '
        'use by the system.'.format(prefix)
      ))
  return errors


def descend_into_resolver(resolver, chain, errors, prefixes):
  for up in resolver.url_patterns:
    regex = up.pattern.regex.pattern
    if isinstance(up, urls.resolvers.URLResolver):
      descend_into_resolver(up, chain + [regex], errors, prefixes)
    elif isinstance(up, urls.resolvers.URLPattern):
      collect_pattern_prefix(chain + [regex], errors, prefixes)
    else:
      errors.append(Warning(
        'Resolver has unexpected URL pattern: {}'.format(repr(up))
      ))


def collect_pattern_prefix(patterns, errors, prefixes):
  # Remember, we are matching against a regular expression pattern! We are not
  # taking a robust approach; if it fails, this could report spurious warnings.
  uberpattern = r''
  for i, pattern in enumerate(patterns):
    if i != 0:
      pattern = pattern.lstrip('^')
    if i != len(patterns) - 1:
      pattern = pattern.rstrip('$')
    uberpattern += pattern
  uberpattern = uberpattern.replace('\\/', '/')

  m = re.match(r'^\^?([^/$]*)', uberpattern)
  if m is None:
    errors.append(Warning(
      'Could not determine first component of URL pattern '
      '{}'.format(uberpattern)
    ))
  else:
    prefixes.add(m.group(1))