@@ -132,6 +132,14 @@ async def prepare(self):
132132 async def any_author_exists (self ):
133133 return bool (await self .query ("SELECT * FROM authors LIMIT 1" ))
134134
135+ def redirect_to_next (self ):
136+ next = self .get_argument ("next" , "/" )
137+ if next .startswith ("//" ) or not next .startswith ("/" ):
138+ # Absolute URLs are not allowed because this would be an open redirect
139+ # vulnerability (https://cwe.mitre.org/data/definitions/601.html).
140+ raise tornado .web .HTTPError (400 )
141+ self .redirect (next )
142+
135143
136144class HomeHandler (BaseHandler ):
137145 async def get (self ):
@@ -243,7 +251,7 @@ async def post(self):
243251 tornado .escape .to_unicode (hashed_password ),
244252 )
245253 self .set_signed_cookie ("blogdemo_user" , str (author .id ))
246- self .redirect ( self . get_argument ( "next" , "/" ) )
254+ self .redirect_to_next ( )
247255
248256
249257class AuthLoginHandler (BaseHandler ):
@@ -270,15 +278,15 @@ async def post(self):
270278 )
271279 if password_equal :
272280 self .set_signed_cookie ("blogdemo_user" , str (author .id ))
273- self .redirect ( self . get_argument ( "next" , "/" ) )
281+ self .redirect_to_next ( )
274282 else :
275283 self .render ("login.html" , error = "incorrect password" )
276284
277285
278286class AuthLogoutHandler (BaseHandler ):
279287 def get (self ):
280288 self .clear_cookie ("blogdemo_user" )
281- self .redirect ( self . get_argument ( "next" , "/" ) )
289+ self .redirect_to_next ( )
282290
283291
284292class EntryModule (tornado .web .UIModule ):
0 commit comments