add an auth token to any URL that modifies the system, to avoid CSRF vulnerabilities
This commit is contained in:
parent
84b0b40615
commit
99143b3413
13 changed files with 76 additions and 16 deletions
|
|
@ -9,6 +9,7 @@
|
||||||
#end if
|
#end if
|
||||||
|
|
||||||
<form action="#if $target then 'buildtargetedit' else 'buildtargetcreate'#">
|
<form action="#if $target then 'buildtargetedit' else 'buildtargetcreate'#">
|
||||||
|
$util.authToken($self, form=True)
|
||||||
#if $target
|
#if $target
|
||||||
<input type="hidden" name="targetID" value="$target.id"/>
|
<input type="hidden" name="targetID" value="$target.id"/>
|
||||||
#end if
|
#end if
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@
|
||||||
</tr>
|
</tr>
|
||||||
#if 'admin' in $perms
|
#if 'admin' in $perms
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2"><a href="buildtargetedit?targetID=$target.id">Edit</a></th>
|
<th colspan="2"><a href="buildtargetedit?targetID=$target.id$util.authToken($self)">Edit</a></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2"><a href="buildtargetdelete?targetID=$target.id">Delete</a></th>
|
<th colspan="2"><a href="buildtargetdelete?targetID=$target.id$util.authToken($self)">Delete</a></th>
|
||||||
</tr>
|
</tr>
|
||||||
#end if
|
#end if
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
|
|
||||||
#if 'admin' in $perms
|
#if 'admin' in $perms
|
||||||
<br/>
|
<br/>
|
||||||
<a href="buildtargetcreate">Create new Build Target</a>
|
<a href="buildtargetcreate$util.authToken($self, first=True)">Create new Build Target</a>
|
||||||
#end if
|
#end if
|
||||||
|
|
||||||
#include "includes/footer.chtml"
|
#include "includes/footer.chtml"
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@
|
||||||
$util.imageTag($enabled)
|
$util.imageTag($enabled)
|
||||||
#if 'admin' in $perms
|
#if 'admin' in $perms
|
||||||
#if $host.enabled
|
#if $host.enabled
|
||||||
<span class="adminLink">(<a href="disablehost?hostID=$host.id">disable</a>)</span>
|
<span class="adminLink">(<a href="disablehost?hostID=$host.id$util.authToken($self)">disable</a>)</span>
|
||||||
#else
|
#else
|
||||||
<span class="adminLink">(<a href="enablehost?hostID=$host.id">enable</a>)</span>
|
<span class="adminLink">(<a href="enablehost?hostID=$host.id$util.authToken($self)">enable</a>)</span>
|
||||||
#end if
|
#end if
|
||||||
#end if
|
#end if
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
|
|
@ -168,8 +168,8 @@
|
||||||
<td>#if $notif.package then $notif.package.name else 'all'#</td>
|
<td>#if $notif.package then $notif.package.name else 'all'#</td>
|
||||||
<td>#if $notif.tag then $notif.tag.name else 'all'#</td>
|
<td>#if $notif.tag then $notif.tag.name else 'all'#</td>
|
||||||
<td>#if $notif.success_only then 'success only' else 'all'#</td>
|
<td>#if $notif.success_only then 'success only' else 'all'#</td>
|
||||||
<td><a href="notificationedit?notificationID=$notif.id">edit</a></td>
|
<td><a href="notificationedit?notificationID=$notif.id$util.authToken($self)">edit</a></td>
|
||||||
<td><a href="notificationdelete?notificationID=$notif.id">delete</a></td>
|
<td><a href="notificationdelete?notificationID=$notif.id$util.authToken($self)">delete</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
#end for
|
#end for
|
||||||
#if $len($notifs) == 0
|
#if $len($notifs) == 0
|
||||||
|
|
@ -180,7 +180,7 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<a href="notificationcreate">Add a Notification</a>
|
<a href="notificationcreate$util.authToken($self, first=True)">Add a Notification</a>
|
||||||
#end if
|
#end if
|
||||||
|
|
||||||
#include "includes/footer.chtml"
|
#include "includes/footer.chtml"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import Cheetah.Filters
|
||||||
import Cheetah.Template
|
import Cheetah.Template
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
import md5
|
||||||
import koji
|
import koji
|
||||||
import kojiweb.util
|
import kojiweb.util
|
||||||
|
|
||||||
|
|
@ -49,6 +50,30 @@ def _getUserCookie(req):
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _truncTime():
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
# truncate to the nearest 15 minutes
|
||||||
|
return now.replace(minute=(now.minute / 15 * 15), second=0, microsecond=0)
|
||||||
|
|
||||||
|
def _genToken(req, tstamp=None):
|
||||||
|
if hasattr(req, 'currentLogin') and req.currentLogin:
|
||||||
|
user = req.currentLogin
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
if tstamp == None:
|
||||||
|
tstamp = _truncTime()
|
||||||
|
return md5.new(user + str(tstamp) + req.get_options()['Secret']).hexdigest()[-8:]
|
||||||
|
|
||||||
|
def _getValidTokens(req):
|
||||||
|
tokens = []
|
||||||
|
now = _truncTime()
|
||||||
|
for delta in (0, 15, 30):
|
||||||
|
token_time = now - datetime.timedelta(minutes=delta)
|
||||||
|
token = _genToken(req, token_time)
|
||||||
|
if token:
|
||||||
|
tokens.append(token)
|
||||||
|
return tokens
|
||||||
|
|
||||||
def _krbLogin(req, session, principal):
|
def _krbLogin(req, session, principal):
|
||||||
options = req.get_options()
|
options = req.get_options()
|
||||||
wprinc = options['WebPrincipal']
|
wprinc = options['WebPrincipal']
|
||||||
|
|
@ -81,6 +106,19 @@ def _assertLogin(req):
|
||||||
raise koji.AuthError, 'could not login using principal: %s' % req.currentLogin
|
raise koji.AuthError, 'could not login using principal: %s' % req.currentLogin
|
||||||
else:
|
else:
|
||||||
raise koji.AuthError, 'KojiWeb is incorrectly configured for authentication, contact the system administrator'
|
raise koji.AuthError, 'KojiWeb is incorrectly configured for authentication, contact the system administrator'
|
||||||
|
|
||||||
|
# verify a valid authToken was passed in to avoid CSRF
|
||||||
|
authToken = req.form.get('a', '')
|
||||||
|
validTokens = _getValidTokens(req)
|
||||||
|
if authToken and authToken in validTokens:
|
||||||
|
# we have a token and it's valid
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# their authToken is likely expired
|
||||||
|
# send them back to the page that brought them here so they
|
||||||
|
# can re-click the link with a valid authToken
|
||||||
|
_redirectBack(req, page=None, forceSSL=(_getBaseURL(req).startswith('https://')))
|
||||||
|
assert False
|
||||||
else:
|
else:
|
||||||
mod_python.util.redirect(req, 'login')
|
mod_python.util.redirect(req, 'login')
|
||||||
assert False
|
assert False
|
||||||
|
|
@ -117,7 +155,8 @@ def _genHTML(req, fileName):
|
||||||
req._values['currentUser'] = req.currentUser
|
req._values['currentUser'] = req.currentUser
|
||||||
else:
|
else:
|
||||||
req._values['currentUser'] = None
|
req._values['currentUser'] = None
|
||||||
|
req._values['authToken'] = _genToken(req)
|
||||||
|
|
||||||
tmpl_class = TEMPLATES.get(fileName)
|
tmpl_class = TEMPLATES.get(fileName)
|
||||||
if not tmpl_class:
|
if not tmpl_class:
|
||||||
tmpl_class = Cheetah.Template.Template.compile(file=fileName)
|
tmpl_class = Cheetah.Template.Template.compile(file=fileName)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#end if
|
#end if
|
||||||
|
|
||||||
<form action="#if $notif then 'notificationedit' else 'notificationcreate'#">
|
<form action="#if $notif then 'notificationedit' else 'notificationcreate'#">
|
||||||
|
$util.authToken($self, form=True)
|
||||||
#if $notif
|
#if $notif
|
||||||
<input type="hidden" name="notificationID" value="$notif.id"/>
|
<input type="hidden" name="notificationID" value="$notif.id"/>
|
||||||
#end if
|
#end if
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#end if
|
#end if
|
||||||
|
|
||||||
<form action="#if $tag then 'tagedit' else 'tagcreate'#">
|
<form action="#if $tag then 'tagedit' else 'tagcreate'#">
|
||||||
|
$util.authToken($self, form=True)
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<table>
|
<table>
|
||||||
#if $child and 'admin' in $perms
|
#if $child and 'admin' in $perms
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2"><a href="tagparent?tagID=$child.id&parentID=$tag.id&action=add">Add $tag.name as parent of $child.name</a></th>
|
<th colspan="2"><a href="tagparent?tagID=$child.id&parentID=$tag.id&action=add$util.authToken($self)">Add $tag.name as parent of $child.name</a></th>
|
||||||
</tr>
|
</tr>
|
||||||
#end if
|
#end if
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
<span class="treeLabel">
|
<span class="treeLabel">
|
||||||
<a href="taginfo?tagID=$parent.parent_id">$parent.name</a>
|
<a href="taginfo?tagID=$parent.parent_id">$parent.name</a>
|
||||||
#if $depth == 1 and 'admin' in $perms
|
#if $depth == 1 and 'admin' in $perms
|
||||||
<span class="treeLink">(<a href="tagparent?tagID=$tag.id&parentID=$parent.parent_id&action=edit">edit</a>) (<a href="tagparent?tagID=$tag.id&parentID=$parent.parent_id&action=remove">remove</a>)</span>
|
<span class="treeLink">(<a href="tagparent?tagID=$tag.id&parentID=$parent.parent_id&action=edit$util.authToken($self)">edit</a>) (<a href="tagparent?tagID=$tag.id&parentID=$parent.parent_id&action=remove$util.authToken($self)">remove</a>)</span>
|
||||||
#end if
|
#end if
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -125,10 +125,10 @@
|
||||||
</tr>
|
</tr>
|
||||||
#if 'admin' in $perms
|
#if 'admin' in $perms
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2"><a href="tagedit?tagID=$tag.id">Edit tag</a></td>
|
<td colspan="2"><a href="tagedit?tagID=$tag.id$util.authToken($self)">Edit tag</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2"><a href="tagdelete?tagID=$tag.id">Delete tag</a></td>
|
<td colspan="2"><a href="tagdelete?tagID=$tag.id$util.authToken($self)">Delete tag</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
#end if
|
#end if
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#end if
|
#end if
|
||||||
|
|
||||||
<form action="tagparent">
|
<form action="tagparent">
|
||||||
|
$util.authToken($self, form=True)
|
||||||
<input type="hidden" name="action" value="#if $inheritanceData then 'edit' else 'add'#"/>
|
<input type="hidden" name="action" value="#if $inheritanceData then 'edit' else 'add'#"/>
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
|
|
||||||
#if 'admin' in $perms
|
#if 'admin' in $perms
|
||||||
<br/>
|
<br/>
|
||||||
<a href="tagcreate">Create new Tag</a>
|
<a href="tagcreate$util.authToken($self, first=True)">Create new Tag</a>
|
||||||
#end if
|
#end if
|
||||||
|
|
||||||
#include "includes/footer.chtml"
|
#include "includes/footer.chtml"
|
||||||
|
|
|
||||||
|
|
@ -171,9 +171,9 @@
|
||||||
<td class="task$state">$state
|
<td class="task$state">$state
|
||||||
#if $currentUser and ('admin' in $perms or $task.owner == $currentUser.id)
|
#if $currentUser and ('admin' in $perms or $task.owner == $currentUser.id)
|
||||||
#if $task.state in ($koji.TASK_STATES.FREE, $koji.TASK_STATES.OPEN, $koji.TASK_STATES.ASSIGNED)
|
#if $task.state in ($koji.TASK_STATES.FREE, $koji.TASK_STATES.OPEN, $koji.TASK_STATES.ASSIGNED)
|
||||||
<span class="adminLink">(<a href="canceltask?taskID=$task.id">cancel</a>)</span>
|
<span class="adminLink">(<a href="canceltask?taskID=$task.id$util.authToken($self)">cancel</a>)</span>
|
||||||
#elif $task.state in ($koji.TASK_STATES.CANCELED, $koji.TASK_STATES.FAILED) and (not $parent)
|
#elif $task.state in ($koji.TASK_STATES.CANCELED, $koji.TASK_STATES.FAILED) and (not $parent)
|
||||||
<span class="adminLink">(<a href="resubmittask?taskID=$task.id">resubmit</a>)</span>
|
<span class="adminLink">(<a href="resubmittask?taskID=$task.id$util.authToken($self)">resubmit</a>)</span>
|
||||||
#end if
|
#end if
|
||||||
#end if
|
#end if
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
|
|
@ -316,6 +316,23 @@ def escapeHTML(value):
|
||||||
replace('<', '<').\
|
replace('<', '<').\
|
||||||
replace('>', '>')
|
replace('>', '>')
|
||||||
|
|
||||||
|
def authToken(template, first=False, form=False):
|
||||||
|
"""Return the current authToken if it exists.
|
||||||
|
If form is True, return it enclosed in a hidden input field.
|
||||||
|
Otherwise, return it in a format suitable for appending to a URL.
|
||||||
|
If first is True, prefix it with ?, otherwise prefix it
|
||||||
|
with &. If no authToken exists, return an empty string."""
|
||||||
|
token = template.getVar('authToken', default=None)
|
||||||
|
if token != None:
|
||||||
|
if form:
|
||||||
|
return '<input type="hidden" name="a" value="%s"/>' % token
|
||||||
|
if first:
|
||||||
|
return '?a=' + token
|
||||||
|
else:
|
||||||
|
return '&a=' + token
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
def explainError(error):
|
def explainError(error):
|
||||||
"""Explain an exception in user-consumable terms
|
"""Explain an exception in user-consumable terms
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue