The X-Headers issue


#1

Hi guys,

are you considering x-headers to be treated correctly inside apistar?

  • Component SchemeComponent gives clumsy value if SSL termination was done before
  • If security matters for you, known upstream proxies are good to be welcome, unknown ones otherwise rejected.

Simple example: Unreal X-Headers


class RedirectPermanent(HTTPException):
    default_status_code = 301
    default_detail = 'Permanent'

    def __init__(self,
                 location: str,
                 detail: Union[str, dict]=None,
                 status_code: int=None) -> None:
        self.location = location
        super().__init__(detail, status_code)

    def get_headers(self):
        return {'Location': self.location} 

X_FORWARDED_PROTO = 'X-Forwarded-Proto'
X_FORWARDED_FOR = 'X-Forwarded-For'
X_FORWARDED_UPSTREAMS = (
     "10.504.*",
     "127.0.0.*"
)
def skip_security(fn):
    fn.security = False  # or not settings.DEBUG
    return fn

# As a hook, as SchemeComponent is lying to me
def security_hook_handler(
    request: http.Request,
    scheme: http.Scheme,
    route: Route
):
        if not getattr(route.handler, "security", True):
              return  # Skip security
        # HTTPS enforce
        if scheme != "https":
            # scheme is a bitch?
            x_forwarded = request.headers.get(
                 X_FORWARDED_PROTO, None
            )
            if "https" == x_forwarded:
                # Yes it is!
                message = (
                    f"Invalid scheme: {request.headers.get(X_FORWARDED_PROTO, None)}"
                )
                # I do not want redirects, then: raise Forbidden("Invalid scheme")
                raise RedirectPermanent(request.url.replace("http", "https"))

      # UPSTREAMS
      if getattr(request.headers, X_FORWARDED_FOR , None):
           x_for = request.headers[X_FORWARDED_FOR]
           if not any(
                   filter(
                       lambda x: x.startswith(x_for.replace('*', '')), 
                       X_FORWARDED_UPSTREAMS
                    )
                ):
               # Note that ("*",) means all upstreams
               raise Forbidden("You takin' to me?")

def secure_handler():
      return

@skip_security
def unsecure_handler():
      return

If SchemeComponent did this not that heavy lifting then code would be ridiculous:

def security_hook_handler(
    request: http.Request,
    scheme: http.Scheme,
    route: Route
):
        if not getattr(route.handler, "security", True):
              return  # Skip security
        # HTTPS enforce
        if scheme != "https":
              raise RedirectPermanent(request.url.replace("http", "https"))
        # The rest of upstreams stuff is more specific...

Of course we are using this in production and if any interest I would be glad to PR.