Merge pull request #83 from ticketfrei/master

Added CSRF Protection
stable2
b3yond 2019-01-27 17:57:58 +01:00 committed by GitHub
commit c203393b53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 29 additions and 7 deletions

View File

@ -178,9 +178,10 @@ def register_telegram(user):
return city_page(user.get_city(), info="Thanks for registering Telegram!") return city_page(user.get_city(), info="Thanks for registering Telegram!")
@get('/api/state') # unused afaik
def api_enable(user): #@get('/api/state')
return user.state() #def api_enable(user):
# return user.state()
@get('/static/<filename:path>') @get('/static/<filename:path>')
@ -197,6 +198,7 @@ def guides(filename):
def logout(): def logout():
# clear auth cookie # clear auth cookie
response.set_cookie('uid', '', expires=0, path="/") response.set_cookie('uid', '', expires=0, path="/")
response.set_cookie('csrf', '', expires=0, path="/")
# :todo show info "Logout successful." # :todo show info "Logout successful."
redirect('/') redirect('/')

View File

@ -1,4 +1,4 @@
from bottle import redirect, request from bottle import redirect, request, abort, response
from db import db from db import db
from functools import wraps from functools import wraps
from inspect import Signature from inspect import Signature
@ -17,10 +17,14 @@ class SessionPlugin(object):
if self.keyword in Signature.from_callable(route.callback).parameters: if self.keyword in Signature.from_callable(route.callback).parameters:
@wraps(callback) @wraps(callback)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
uid = request.get_cookie('uid', secret=db.secret) uid = request.get_cookie('uid', secret=db.get_secret())
if uid is None: if uid is None:
return redirect(self.loginpage) return redirect(self.loginpage)
kwargs[self.keyword] = User(uid) kwargs[self.keyword] = User(uid)
if request.method == 'POST':
if request.forms['csrf'] != request.get_cookie('csrf',
secret=db.get_secret()):
abort(400)
return callback(*args, **kwargs) return callback(*args, **kwargs)
return wrapper return wrapper

View File

@ -61,6 +61,7 @@
<option value='octodon.social'> <option value='octodon.social'>
<option value='soc.ialis.me'> <option value='soc.ialis.me'>
</datalist> </datalist>
<input name='csrf' value='{{csrf}}' type='hidden' />
<input name='confirm' value='Log in' type='submit'/> <input name='confirm' value='Log in' type='submit'/>
</form> </form>
</section> </section>
@ -82,6 +83,7 @@
</p> </p>
<form action="/settings/telegram" method="post"> <form action="/settings/telegram" method="post">
<input type="text" name="apikey" placeholder="Telegram bot API key" id="apikey"> <input type="text" name="apikey" placeholder="Telegram bot API key" id="apikey">
<input name='csrf' value='{{csrf}}' type='hidden' />
<input name='confirm' value='Login with Telegram' type='submit'/> <input name='confirm' value='Login with Telegram' type='submit'/>
</form> </form>
</div> </div>
@ -106,6 +108,7 @@
</p> </p>
<form action="/settings/markdown" method="post"> <form action="/settings/markdown" method="post">
<textarea id="markdown" rows="20" cols="70" name="markdown" wrap="physical">{{markdown}}</textarea> <textarea id="markdown" rows="20" cols="70" name="markdown" wrap="physical">{{markdown}}</textarea>
<input name='csrf' value='{{csrf}}' type='hidden' />
<input name='confirm' value='Save' type='submit'/> <input name='confirm' value='Save' type='submit'/>
</form> </form>
</div> </div>
@ -123,6 +126,7 @@
</p> </p>
<form action="/settings/mail_md" method="post"> <form action="/settings/mail_md" method="post">
<textarea id="mail_md" rows="20" cols="70" name="mail_md" wrap="physical">{{mail_md}}</textarea> <textarea id="mail_md" rows="20" cols="70" name="mail_md" wrap="physical">{{mail_md}}</textarea>
<input name='csrf' value='{{csrf}}' type='hidden' />
<input name='confirm' value='Save' type='submit'/> <input name='confirm' value='Save' type='submit'/>
</form> </form>
</div> </div>
@ -137,6 +141,7 @@
</p> </p>
<form action="/settings/goodlist" method="post"> <form action="/settings/goodlist" method="post">
<textarea id="goodlist" rows="8" cols="70" name="goodlist" wrap="physical">{{triggerwords}}</textarea> <textarea id="goodlist" rows="8" cols="70" name="goodlist" wrap="physical">{{triggerwords}}</textarea>
<input name='csrf' value='{{csrf}}' type='hidden' />
<input name='confirm' value='Submit' type='submit'/> <input name='confirm' value='Submit' type='submit'/>
</form> </form>
</div> </div>
@ -151,6 +156,7 @@
</p> </p>
<form action="/settings/blocklist" method="post"> <form action="/settings/blocklist" method="post">
<textarea id="blocklist" rows="8" cols="70" name="blocklist" wrap="physical">{{badwords}}</textarea> <textarea id="blocklist" rows="8" cols="70" name="blocklist" wrap="physical">{{badwords}}</textarea>
<input name='csrf' value='{{csrf}}' type='hidden' />
<input name='confirm' value='Submit' type='submit'/> <input name='confirm' value='Submit' type='submit'/>
</form> </form>
</div> </div>

14
user.py
View File

@ -1,9 +1,10 @@
from config import config from config import config
from bottle import response from bottle import response, request
from db import db from db import db
import jwt import jwt
from mastodon import Mastodon from mastodon import Mastodon
from pylibscrypt import scrypt_mcf, scrypt_mcf_check from pylibscrypt import scrypt_mcf, scrypt_mcf_check
from os import urandom
class User(object): class User(object):
@ -11,6 +12,13 @@ class User(object):
# set cookie # set cookie
response.set_cookie('uid', uid, secret=db.get_secret(), path='/') response.set_cookie('uid', uid, secret=db.get_secret(), path='/')
self.uid = uid self.uid = uid
response.set_cookie('csrf', self.get_csrf(), db.get_secret(), path='/')
def get_csrf(self):
csrf_token = request.get_cookie('csrf', secret=db.get_secret())
if not csrf_token:
csrf_token = str(urandom(32))
return csrf_token
def check_password(self, password): def check_password(self, password):
db.execute("SELECT passhash FROM user WHERE id=?;", (self.uid,)) db.execute("SELECT passhash FROM user WHERE id=?;", (self.uid,))
@ -235,6 +243,7 @@ schlitz
# - mail_md # - mail_md
# - goodlist # - goodlist
# - blocklist # - blocklist
# - csrf
# - logged in with twitter? # - logged in with twitter?
# - logged in with mastodon? # - logged in with mastodon?
# - enabled? # - enabled?
@ -244,7 +253,8 @@ schlitz
mail_md=citydict['mail_md'], mail_md=citydict['mail_md'],
triggerwords=self.get_trigger_words(), triggerwords=self.get_trigger_words(),
badwords=self.get_badwords(), badwords=self.get_badwords(),
enabled=self.enabled) enabled=self.enabled,
csrf=self.get_csrf())
def save_request_token(self, token): def save_request_token(self, token):
db.execute("""INSERT INTO db.execute("""INSERT INTO