1
0
mirror of https://github.com/kakwa/ldapcherry synced 2024-11-22 09:24:21 +01:00

Protect against XSS vulnerabilities in URL redirection

- Switch from base64 to URL encoding for the passing the URL, using the built-in Mako filtering
- Apply HTML filtering to Mako output by default
- Disable HTML filtering for nested templates in adduser, modify, and selfmodify
This commit is contained in:
John Thiltges 2019-01-02 14:31:10 -06:00
parent 1ed654c91b
commit 6f98076281
5 changed files with 24 additions and 23 deletions

View File

@ -15,7 +15,7 @@ import logging
import logging.handlers import logging.handlers
from operator import itemgetter from operator import itemgetter
from socket import error as socket_error from socket import error as socket_error
import base64 import urllib
import cgi import cgi
from exceptions import * from exceptions import *
@ -387,7 +387,8 @@ class LdapCherry(object):
) )
# preload templates # preload templates
self.temp_lookup = lookup.TemplateLookup( self.temp_lookup = lookup.TemplateLookup(
directories=self.template_dir, input_encoding='utf-8' directories=self.template_dir, input_encoding='utf-8',
default_filters=['unicode', 'h']
) )
# load each template # load each template
self.temp = {} self.temp = {}
@ -573,7 +574,7 @@ class LdapCherry(object):
def _check_auth(self, must_admin, redir_login=True): def _check_auth(self, must_admin, redir_login=True):
""" check if a user is autheticated and, optionnaly an administrator """ check if a user is autheticated and, optionnaly an administrator
if user not authentifaced -> redirection to login page (with base64 if user not authenticated -> redirect to login page (with escaped URL
of the originaly requested page (redirection after login) of the originaly requested page (redirection after login)
if user authenticated, not admin and must_admin enabled -> 403 error if user authenticated, not admin and must_admin enabled -> 403 error
@boolean must_admin: flag "user must be an administrator to access @boolean must_admin: flag "user must be an administrator to access
@ -588,13 +589,13 @@ class LdapCherry(object):
qs = '' qs = ''
else: else:
qs = '?' + cherrypy.request.query_string qs = '?' + cherrypy.request.query_string
# base64 of the requested URL # Escaped version of the requested URL
b64requrl = base64.b64encode(cherrypy.url() + qs) quoted_requrl = urllib.quote_plus(cherrypy.url() + qs)
if not username: if not username:
# return to login page (with base64 of the url in query string # return to login page (with quoted url in query string)
if redir_login: if redir_login:
raise cherrypy.HTTPRedirect( raise cherrypy.HTTPRedirect(
"/signin?url=%(url)s" % {'url': b64requrl}, "/signin?url=%(url)s" % {'url': quoted_requrl},
) )
else: else:
raise cherrypy.HTTPError( raise cherrypy.HTTPError(
@ -606,7 +607,7 @@ class LdapCherry(object):
or not cherrypy.session['connected']: or not cherrypy.session['connected']:
if redir_login: if redir_login:
raise cherrypy.HTTPRedirect( raise cherrypy.HTTPRedirect(
"/signin?url=%(url)s" % {'url': b64requrl}, "/signin?url=%(url)s" % {'url': quoted_requrl},
) )
else: else:
raise cherrypy.HTTPError( raise cherrypy.HTTPError(
@ -631,7 +632,7 @@ class LdapCherry(object):
else: else:
if redir_login: if redir_login:
raise cherrypy.HTTPRedirect( raise cherrypy.HTTPRedirect(
"/signin?url=%(url)s" % {'url': b64requrl}, "/signin?url=%(url)s" % {'url': quoted_requrl},
) )
else: else:
raise cherrypy.HTTPError( raise cherrypy.HTTPError(
@ -919,7 +920,7 @@ class LdapCherry(object):
if url is None: if url is None:
redirect = "/" redirect = "/"
else: else:
redirect = base64.b64decode(url) redirect = url
raise cherrypy.HTTPRedirect(redirect) raise cherrypy.HTTPRedirect(redirect)
else: else:
message = "login failed for user '%(user)s'" % { message = "login failed for user '%(user)s'" % {
@ -932,7 +933,7 @@ class LdapCherry(object):
if url is None: if url is None:
qs = '' qs = ''
else: else:
qs = '?url=' + url qs = '?url=' + urllib.quote_plus(url)
raise cherrypy.HTTPRedirect("/signin" + qs) raise cherrypy.HTTPRedirect("/signin" + qs)
@cherrypy.expose @cherrypy.expose

View File

@ -9,11 +9,11 @@
<form method='POST' autocomplete="off" action='/adduser' role="form" class="form-signin" id=form> <form method='POST' autocomplete="off" action='/adduser' role="form" class="form-signin" id=form>
<fieldset> <fieldset>
<legend>Fill new user's attributes:</legend> <legend>Fill new user's attributes:</legend>
${form} ${form | n}
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Enable/Disable user's roles:</legend> <legend>Enable/Disable user's roles:</legend>
${roles} ${roles | n}
</fieldset> </fieldset>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">

View File

@ -4,13 +4,13 @@
<div class="row clearfix" style="margin-top:30px"> <div class="row clearfix" style="margin-top:30px">
<div class="col-md-4 column"></div> <div class="col-md-4 column"></div>
<div class="col-md-4 column well"> <div class="col-md-4 column well">
<% <form method='POST' role="form" class="form-signin"
if url is None: % if url:
qs='' action='login?url=${url | u}'
else: % else:
qs='?url=' + url action='login'
%> % endif
<form method='POST' action='/login${qs}' role="form" class="form-signin"> >
<div class="form-group"> <div class="form-group">
<h2 class="form-signin-heading">Please sign in</h2> <h2 class="form-signin-heading">Please sign in</h2>
<div class="input-group"> <div class="input-group">

View File

@ -9,11 +9,11 @@
<form method='POST' action='/modify' role="form" class="form-signin" id="form"> <form method='POST' action='/modify' role="form" class="form-signin" id="form">
<fieldset> <fieldset>
<legend>Modify user's attributes:</legend> <legend>Modify user's attributes:</legend>
${form} ${form | n}
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Enable/Disable user's roles:</legend> <legend>Enable/Disable user's roles:</legend>
${roles} ${roles | n}
</fieldset> </fieldset>
% if len(standalone_groups) != 0: % if len(standalone_groups) != 0:
<fieldset> <fieldset>

View File

@ -8,7 +8,7 @@
<div class="well well-sm"> <div class="well well-sm">
<form method='POST' action='/selfmodify' autocomplete="off" role="form" class="form-signin" id="form"> <form method='POST' action='/selfmodify' autocomplete="off" role="form" class="form-signin" id="form">
<legend>Modify your attributes:</legend> <legend>Modify your attributes:</legend>
${form} ${form | n}
</fieldset> </fieldset>
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">