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
|
||||
|
||||
<form action="#if $target then 'buildtargetedit' else 'buildtargetcreate'#">
|
||||
$util.authToken($self, form=True)
|
||||
#if $target
|
||||
<input type="hidden" name="targetID" value="$target.id"/>
|
||||
#end if
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@
|
|||
</tr>
|
||||
#if 'admin' in $perms
|
||||
<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>
|
||||
<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>
|
||||
#end if
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@
|
|||
|
||||
#if 'admin' in $perms
|
||||
<br/>
|
||||
<a href="buildtargetcreate">Create new Build Target</a>
|
||||
<a href="buildtargetcreate$util.authToken($self, first=True)">Create new Build Target</a>
|
||||
#end if
|
||||
|
||||
#include "includes/footer.chtml"
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@
|
|||
$util.imageTag($enabled)
|
||||
#if 'admin' in $perms
|
||||
#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
|
||||
<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
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -168,8 +168,8 @@
|
|||
<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.success_only then 'success only' else 'all'#</td>
|
||||
<td><a href="notificationedit?notificationID=$notif.id">edit</a></td>
|
||||
<td><a href="notificationdelete?notificationID=$notif.id">delete</a></td>
|
||||
<td><a href="notificationedit?notificationID=$notif.id$util.authToken($self)">edit</a></td>
|
||||
<td><a href="notificationdelete?notificationID=$notif.id$util.authToken($self)">delete</a></td>
|
||||
</tr>
|
||||
#end for
|
||||
#if $len($notifs) == 0
|
||||
|
|
@ -180,7 +180,7 @@
|
|||
</table>
|
||||
|
||||
<br/>
|
||||
<a href="notificationcreate">Add a Notification</a>
|
||||
<a href="notificationcreate$util.authToken($self, first=True)">Add a Notification</a>
|
||||
#end if
|
||||
|
||||
#include "includes/footer.chtml"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import Cheetah.Filters
|
|||
import Cheetah.Template
|
||||
import datetime
|
||||
import time
|
||||
import md5
|
||||
import koji
|
||||
import kojiweb.util
|
||||
|
||||
|
|
@ -49,6 +50,30 @@ def _getUserCookie(req):
|
|||
|
||||
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):
|
||||
options = req.get_options()
|
||||
wprinc = options['WebPrincipal']
|
||||
|
|
@ -81,6 +106,19 @@ def _assertLogin(req):
|
|||
raise koji.AuthError, 'could not login using principal: %s' % req.currentLogin
|
||||
else:
|
||||
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:
|
||||
mod_python.util.redirect(req, 'login')
|
||||
assert False
|
||||
|
|
@ -117,7 +155,8 @@ def _genHTML(req, fileName):
|
|||
req._values['currentUser'] = req.currentUser
|
||||
else:
|
||||
req._values['currentUser'] = None
|
||||
|
||||
req._values['authToken'] = _genToken(req)
|
||||
|
||||
tmpl_class = TEMPLATES.get(fileName)
|
||||
if not tmpl_class:
|
||||
tmpl_class = Cheetah.Template.Template.compile(file=fileName)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#end if
|
||||
|
||||
<form action="#if $notif then 'notificationedit' else 'notificationcreate'#">
|
||||
$util.authToken($self, form=True)
|
||||
#if $notif
|
||||
<input type="hidden" name="notificationID" value="$notif.id"/>
|
||||
#end if
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#end if
|
||||
|
||||
<form action="#if $tag then 'tagedit' else 'tagcreate'#">
|
||||
$util.authToken($self, form=True)
|
||||
<table>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<table>
|
||||
#if $child and 'admin' in $perms
|
||||
<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>
|
||||
#end if
|
||||
<tr>
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
<span class="treeLabel">
|
||||
<a href="taginfo?tagID=$parent.parent_id">$parent.name</a>
|
||||
#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
|
||||
</span>
|
||||
</span>
|
||||
|
|
@ -125,10 +125,10 @@
|
|||
</tr>
|
||||
#if 'admin' in $perms
|
||||
<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>
|
||||
<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>
|
||||
#end if
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#end if
|
||||
|
||||
<form action="tagparent">
|
||||
$util.authToken($self, form=True)
|
||||
<input type="hidden" name="action" value="#if $inheritanceData then 'edit' else 'add'#"/>
|
||||
<table>
|
||||
<tr>
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@
|
|||
|
||||
#if 'admin' in $perms
|
||||
<br/>
|
||||
<a href="tagcreate">Create new Tag</a>
|
||||
<a href="tagcreate$util.authToken($self, first=True)">Create new Tag</a>
|
||||
#end if
|
||||
|
||||
#include "includes/footer.chtml"
|
||||
|
|
|
|||
|
|
@ -171,9 +171,9 @@
|
|||
<td class="task$state">$state
|
||||
#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)
|
||||
<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)
|
||||
<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
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -316,6 +316,23 @@ def escapeHTML(value):
|
|||
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):
|
||||
"""Explain an exception in user-consumable terms
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue