prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Session middleware is highly insecure
  The session middleware does not have any timestamp component to it, which means that if a session happens to be somehow leaked, it can be reused any time in the future.
  
  Can we have a timestamp component added to the session, so that the session middleware can reject such sessions after a reasonable period of time (e.g. 24 hours)?
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `starlette/middleware/sessions.py`
  Content:
  ```
  1 import functools
  2 import json
  3 from base64 import b64decode, b64encode
  4 
  5 import itsdangerous
  6 
  7 from starlette.datastructures import MutableHeaders
  8 from starlette.requests import Request
  9 from starlette.types import ASGIApp, ASGIInstance, Message, Receive, Scope, Send
  10 
  11 
  12 class SessionMiddleware:
  13     def __init__(
  14         self, app: ASGIApp, secret_key: str, session_cookie: str = "session"
  15     ) -> None:
  16         self.app = app
  17         self.signer = itsdangerous.Signer(secret_key)
  18         self.session_cookie = session_cookie
  19 
  20     def __call__(self, scope: Scope) -> ASGIInstance:
  21         if scope["type"] in ("http", "websocket"):
  22             request = Request(scope)
  23             if self.session_cookie in request.cookies:
  24                 data = request.cookies[self.session_cookie].encode("utf-8")
  25                 data = self.signer.unsign(data)
  26                 scope["session"] = json.loads(b64decode(data))
  27             else:
  28                 scope["session"] = {}
  29             return functools.partial(self.asgi, scope=scope)
  30         return self.app(scope)  # pragma: no cover
  31 
  32     async def asgi(self, receive: Receive, send: Send, scope: Scope) -> None:
  33         was_empty_session = not scope["session"]
  34         inner = self.app(scope)
  35 
  36         async def sender(message: Message) -> None:
  37             if message["type"] == "http.response.start":
  38                 if scope["session"]:
  39                     # We have session data to persist.
  40                     data = b64encode(json.dumps(scope["session"]).encode("utf-8"))
  41                     data = self.signer.sign(data)
  42                     headers = MutableHeaders(scope=message)
  43                     header_value = "%s=%s" % (self.session_cookie, data.decode("utf-8"))
  44                     headers.append("Set-Cookie", header_value)
  45                 elif not was_empty_session:
  46                     # The session has been cleared.
  47                     headers = MutableHeaders(scope=message)
  48                     header_value = "%s=%s" % (
  49                         self.session_cookie,
  50                         "null; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT",
  51                     )
  52                     headers.append("Set-Cookie", header_value)
  53             await send(message)
  54 
  55         await inner(receive, sender)
  56
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/starlette/middleware/sessions.py b/starlette/middleware/sessions.py
  --- a/starlette/middleware/sessions.py
  +++ b/starlette/middleware/sessions.py
  @@ -3,6 +3,7 @@
   from base64 import b64decode, b64encode
   
   import itsdangerous
  +from itsdangerous.exc import BadTimeSignature, SignatureExpired
   
   from starlette.datastructures import MutableHeaders
   from starlette.requests import Request
  @@ -11,19 +12,27 @@
   
   class SessionMiddleware:
       def __init__(
  -        self, app: ASGIApp, secret_key: str, session_cookie: str = "session"
  +        self,
  +        app: ASGIApp,
  +        secret_key: str,
  +        session_cookie: str = "session",
  +        max_age: int = 14 * 24 * 60 * 60,  # 14 days, in seconds
       ) -> None:
           self.app = app
  -        self.signer = itsdangerous.Signer(secret_key)
  +        self.signer = itsdangerous.TimestampSigner(secret_key)
           self.session_cookie = session_cookie
  +        self.max_age = max_age
   
       def __call__(self, scope: Scope) -> ASGIInstance:
           if scope["type"] in ("http", "websocket"):
               request = Request(scope)
               if self.session_cookie in request.cookies:
                   data = request.cookies[self.session_cookie].encode("utf-8")
  -                data = self.signer.unsign(data)
  -                scope["session"] = json.loads(b64decode(data))
  +                try:
  +                    data = self.signer.unsign(data, max_age=self.max_age)
  +                    scope["session"] = json.loads(b64decode(data))
  +                except (BadTimeSignature, SignatureExpired):
  +                    scope["session"] = {}
               else:
                   scope["session"] = {}
               return functools.partial(self.asgi, scope=scope)
  
verification_info: {"golden_diff": "diff --git a/starlette/middleware/sessions.py b/starlette/middleware/sessions.py\n--- a/starlette/middleware/sessions.py\n+++ b/starlette/middleware/sessions.py\n@@ -3,6 +3,7 @@\n from base64 import b64decode, b64encode\n \n import itsdangerous\n+from itsdangerous.exc import BadTimeSignature, SignatureExpired\n \n from starlette.datastructures import MutableHeaders\n from starlette.requests import Request\n@@ -11,19 +12,27 @@\n \n class SessionMiddleware:\n     def __init__(\n-        self, app: ASGIApp, secret_key: str, session_cookie: str = \"session\"\n+        self,\n+        app: ASGIApp,\n+        secret_key: str,\n+        session_cookie: str = \"session\",\n+        max_age: int = 14 * 24 * 60 * 60,  # 14 days, in seconds\n     ) -> None:\n         self.app = app\n-        self.signer = itsdangerous.Signer(secret_key)\n+        self.signer = itsdangerous.TimestampSigner(secret_key)\n         self.session_cookie = session_cookie\n+        self.max_age = max_age\n \n     def __call__(self, scope: Scope) -> ASGIInstance:\n         if scope[\"type\"] in (\"http\", \"websocket\"):\n             request = Request(scope)\n             if self.session_cookie in request.cookies:\n                 data = request.cookies[self.session_cookie].encode(\"utf-8\")\n-                data = self.signer.unsign(data)\n-                scope[\"session\"] = json.loads(b64decode(data))\n+                try:\n+                    data = self.signer.unsign(data, max_age=self.max_age)\n+                    scope[\"session\"] = json.loads(b64decode(data))\n+                except (BadTimeSignature, SignatureExpired):\n+                    scope[\"session\"] = {}\n             else:\n                 scope[\"session\"] = {}\n             return functools.partial(self.asgi, scope=scope)\n", "issue": "Session middleware is highly insecure\nThe session middleware does not have any timestamp component to it, which means that if a session happens to be somehow leaked, it can be reused any time in the future.\r\n\r\nCan we have a timestamp component added to the session, so that the session middleware can reject such sessions after a reasonable period of time (e.g. 24 hours)?\n", "before_files": [{"content": "import functools\nimport json\nfrom base64 import b64decode, b64encode\n\nimport itsdangerous\n\nfrom starlette.datastructures import MutableHeaders\nfrom starlette.requests import Request\nfrom starlette.types import ASGIApp, ASGIInstance, Message, Receive, Scope, Send\n\n\nclass SessionMiddleware:\n    def __init__(\n        self, app: ASGIApp, secret_key: str, session_cookie: str = \"session\"\n    ) -> None:\n        self.app = app\n        self.signer = itsdangerous.Signer(secret_key)\n        self.session_cookie = session_cookie\n\n    def __call__(self, scope: Scope) -> ASGIInstance:\n        if scope[\"type\"] in (\"http\", \"websocket\"):\n            request = Request(scope)\n            if self.session_cookie in request.cookies:\n                data = request.cookies[self.session_cookie].encode(\"utf-8\")\n                data = self.signer.unsign(data)\n                scope[\"session\"] = json.loads(b64decode(data))\n            else:\n                scope[\"session\"] = {}\n            return functools.partial(self.asgi, scope=scope)\n        return self.app(scope)  # pragma: no cover\n\n    async def asgi(self, receive: Receive, send: Send, scope: Scope) -> None:\n        was_empty_session = not scope[\"session\"]\n        inner = self.app(scope)\n\n        async def sender(message: Message) -> None:\n            if message[\"type\"] == \"http.response.start\":\n                if scope[\"session\"]:\n                    # We have session data to persist.\n                    data = b64encode(json.dumps(scope[\"session\"]).encode(\"utf-8\"))\n                    data = self.signer.sign(data)\n                    headers = MutableHeaders(scope=message)\n                    header_value = \"%s=%s\" % (self.session_cookie, data.decode(\"utf-8\"))\n                    headers.append(\"Set-Cookie\", header_value)\n                elif not was_empty_session:\n                    # The session has been cleared.\n                    headers = MutableHeaders(scope=message)\n                    header_value = \"%s=%s\" % (\n                        self.session_cookie,\n                        \"null; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT\",\n                    )\n                    headers.append(\"Set-Cookie\", header_value)\n            await send(message)\n\n        await inner(receive, sender)\n", "path": "starlette/middleware/sessions.py"}], "after_files": [{"content": "import functools\nimport json\nfrom base64 import b64decode, b64encode\n\nimport itsdangerous\nfrom itsdangerous.exc import BadTimeSignature, SignatureExpired\n\nfrom starlette.datastructures import MutableHeaders\nfrom starlette.requests import Request\nfrom starlette.types import ASGIApp, ASGIInstance, Message, Receive, Scope, Send\n\n\nclass SessionMiddleware:\n    def __init__(\n        self,\n        app: ASGIApp,\n        secret_key: str,\n        session_cookie: str = \"session\",\n        max_age: int = 14 * 24 * 60 * 60,  # 14 days, in seconds\n    ) -> None:\n        self.app = app\n        self.signer = itsdangerous.TimestampSigner(secret_key)\n        self.session_cookie = session_cookie\n        self.max_age = max_age\n\n    def __call__(self, scope: Scope) -> ASGIInstance:\n        if scope[\"type\"] in (\"http\", \"websocket\"):\n            request = Request(scope)\n            if self.session_cookie in request.cookies:\n                data = request.cookies[self.session_cookie].encode(\"utf-8\")\n                try:\n                    data = self.signer.unsign(data, max_age=self.max_age)\n                    scope[\"session\"] = json.loads(b64decode(data))\n                except (BadTimeSignature, SignatureExpired):\n                    scope[\"session\"] = {}\n            else:\n                scope[\"session\"] = {}\n            return functools.partial(self.asgi, scope=scope)\n        return self.app(scope)  # pragma: no cover\n\n    async def asgi(self, receive: Receive, send: Send, scope: Scope) -> None:\n        was_empty_session = not scope[\"session\"]\n        inner = self.app(scope)\n\n        async def sender(message: Message) -> None:\n            if message[\"type\"] == \"http.response.start\":\n                if scope[\"session\"]:\n                    # We have session data to persist.\n                    data = b64encode(json.dumps(scope[\"session\"]).encode(\"utf-8\"))\n                    data = self.signer.sign(data)\n                    headers = MutableHeaders(scope=message)\n                    header_value = \"%s=%s\" % (self.session_cookie, data.decode(\"utf-8\"))\n                    headers.append(\"Set-Cookie\", header_value)\n                elif not was_empty_session:\n                    # The session has been cleared.\n                    headers = MutableHeaders(scope=message)\n                    header_value = \"%s=%s\" % (\n                        self.session_cookie,\n                        \"null; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT\",\n                    )\n                    headers.append(\"Set-Cookie\", header_value)\n            await send(message)\n\n        await inner(receive, sender)\n", "path": "starlette/middleware/sessions.py"}]}