check if Origin matches Host to mitigate CSRF, part of #40

This commit is contained in:
Martin Zimmermann 2013-12-01 13:24:37 +01:00
parent b0ecc9c16a
commit 32e4b70510

View File

@ -31,6 +31,28 @@ class JSON(Response):
return super(JSON, self).__init__(*args, content_type='application/json') return super(JSON, self).__init__(*args, content_type='application/json')
def csrf(view):
"""A decorator to check if HTTP_Origin matches configured host. If not,
return 401 Forbidden. See
* https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Checking_The_Origin_Header
* http://tools.ietf.org/html/draft-abarth-origin-09
* https://wiki.mozilla.org/Security/Origin
for details.
"""
def dec(self, environ, request, *args, **kwargs):
origin = request.headers.get("Origin")
if parse.host(origin) not in map(parse.host, self.conf.getiter("host")):
raise Forbidden("CSRF")
return view(self, environ, request, *args, **kwargs)
return dec
class API(object): class API(object):
FIELDS = set(['id', 'parent', 'text', 'author', 'website', 'email', FIELDS = set(['id', 'parent', 'text', 'author', 'website', 'email',
@ -91,6 +113,7 @@ class API(object):
return True, "" return True, ""
@csrf
@requires(str, 'uri') @requires(str, 'uri')
def new(self, environ, request, uri): def new(self, environ, request, uri):
@ -174,6 +197,7 @@ class API(object):
return Response(json.dumps(rv), 200, content_type='application/json') return Response(json.dumps(rv), 200, content_type='application/json')
@csrf
def edit(self, environ, request, id): def edit(self, environ, request, id):
try: try:
@ -217,6 +241,7 @@ class API(object):
resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"])) resp.headers.add("X-Set-Cookie", cookie("isso-%i" % rv["id"]))
return resp return resp
@csrf
def delete(self, environ, request, id, key=None): def delete(self, environ, request, id, key=None):
try: try:
@ -294,11 +319,13 @@ class API(object):
return JSON(json.dumps(rv), 200) return JSON(json.dumps(rv), 200)
@csrf
def like(self, environ, request, id): def like(self, environ, request, id):
nv = self.comments.vote(True, id, utils.anonymize(str(request.remote_addr))) nv = self.comments.vote(True, id, utils.anonymize(str(request.remote_addr)))
return Response(json.dumps(nv), 200) return Response(json.dumps(nv), 200)
@csrf
def dislike(self, environ, request, id): def dislike(self, environ, request, id):
nv = self.comments.vote(False, id, utils.anonymize(str(request.remote_addr))) nv = self.comments.vote(False, id, utils.anonymize(str(request.remote_addr)))