这是记录某人的正确/安全方式吗?

时间:2015-04-08 02:00:28

标签: python sqlite flask session-cookies flask-login

好的,我对编程非常陌生(刚刚学会了如何建立数据库连接并从中进行写入/读取)并且我试图创建一个允许您登录用户的基本站点当用户登录时,有一些不同的页面。我已决定使用Flask,Flask-session,Sqlite3进行攻击,基本认证如下:用户使用表单登录,表单数据根据sqlite3数据库中的信息进行检查,如果正确,则将相应的值写入CLIENT端cookie。这是记录某人的可接受方式吗?请原谅我的无知,但我对编程很陌生,而且我缺少一些关键术语来搜索我自己的答案。我已经四处搜索过,但是我们无法确定这是否是一种可以接受登录/退出的方式。

我的网站有效,但我不知道我是否真的记录了传统意义上的任何内容。还要注意一些路线被奇怪地打破或隐藏这只是因为我试图破坏它们的工作方式。为了让我的网站运行,我没有包含模板:

from app.py import init_db

init_db()

python app.py

这是我的应用文件:

#importations
import sqlite3
from flask import Flask, request, session, g, redirect, url_for, \
abort, render_template, flash, escape
from contextlib import closing
import sys
import datetime
from models import User
from formms import RegistrationForm, Login
import os


key1 = os.urandom(24)
#config
DATABASE = '/tmp/fitty1.db'
DEBUG = True
SECRET_KEY = key1
USERNAME = 'admin'
PASSWORD = 'default'
print key1

#Initialize the application
app = Flask(__name__)
app.config.from_object(__name__)

#login_manager = LoginManager()
#login_manager.init_app(app)




#Method to connect to database, use to open a connection on request, or from interactive python shell
def connect_db():
    return sqlite3.connect(app.config['DATABASE'])

#Run this before application to initialize the DB
def init_db():
    with closing(connect_db()) as db:
        with app.open_resource('schema.sql', mode='r') as f:
            db.cursor().executescript(f.read())
        db.commit()


def query_db(query, args=(), one=False):
    cur = get_db().execute(query, args)
    rv = cur.fetchall()
    cur.close()
    return (rv[0] if rv else None) if one else rv



@app.teardown_request
def teardown_request(exception):
    db = getattr(g, 'db', None)
    if db is not None:
        db.close()

@app.route('/')
def show_entries():
    g.db = connect_db()
    cur = g.db.execute('select title, text from entries order by id desc')
    entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
    return render_template('show_entries.html', entries=entries)


@app.route('/register', methods=['POST', 'GET'])
def register():
    g.db = connect_db()
    form = RegistrationForm(request.form)
    if request.method == 'POST' and form.validate():
        user = User(form.username.data, form.email.data, form.password.data)
        g.db.execute('insert into users (username, email, password) values (?, ?, ?)',
             [request.form['username'], request.form['email'], request.form['password']])
        g.db.commit()
        flash('Registered successfully')
        return redirect(url_for('show_entries'))
    return render_template('register.html', form=form)


@app.route('/account') 
def account():
    g.db = connect_db()
    if not session.get('logged_in'):
        abort(401)
    if 'username' in session:
        username = session['username']
        c = g.db.execute("SELECT password from users where username = (?)", (username,))
        passwc = c.fetchone()
        c = g.db.execute("SELECT text from entries")
        texty = c.fetchone()
        return 'Your username is "%s", your password is "%s" and code %s' % (username, passwc[0], texty[0])
    return 'You are not logged in'


@app.route('/add', methods=['POST'])
def add_entry():
    g.db = connect_db()
    if not session.get('logged_in'):
        abort(401)
    g.db.execute('insert into entries (title, text) values (?, ?)',
                 [request.form['title'], request.form['text']])
    g.db.commit()
    flash('New entry was successfully added')
    return redirect(url_for('show_entries'))

@app.route('/login', methods=['GET', 'POST'])
def login():
    g.db = connect_db()
    error = None
    form = Login(request.form)
    user = User(form.username.data, form.email.data, form.password.data)
    if request.method == 'POST':
        c = g.db.execute("SELECT username from users where username = (?)", [form.username.data])
        userexists = c.fetchone()
        if userexists:
            c = g.db.execute("SELECT password from users where password = (?)", [form.password.data])
            passwcorrect = c.fetchone()
            if passwcorrect:
                session['username'] = form.username.data
                session['logged_in'] = True
                flash('You were logged in')
                return redirect(url_for('account'))
            else:
                return 'password fail'
        else:
            return 'username fail'
    return render_template('login.html', form=form)


@app.route('/logout')
def logout():
    g.db = connect_db()
    session.pop('logged_in', None)
    flash('You were logged Out')
    return redirect(url_for('show_entries'))




#Use build in server to run standalone application
if __name__ == '__main__':
    app.run()

模型

import sqlite3
from flask import g
from flask.ext.login import LoginManager, login_required

class User():
    def __init__(self,username,email,password,active=True):
        self.username = username
        self.email = email
        self.password = password
        self.active = active

    def is_authenticated(self):
        return True
        #Return true if authenticated, provided credentials

    def is_active(self):
        return True

def is_anonymous(self):
    return False
    #return true if anon, actual user returns false


def __repr__(self):
    return '<User %r>' % (self.email)

schema.sql文件

drop table if exists entries;
create table entries (
  id integer primary key autoincrement,
  title text not null,
  text text not null
);
drop table if exists users;
create table users (
  id integer primary key autoincrement,
  username text not null,
  email text not null,
  password text not null
);

2 个答案:

答案 0 :(得分:1)

这绝对是做到这一点的方法。客户端cookie允许您唯一标识用户。你只需要确保cookie足够随机,否则有人可以欺骗它。

如果您的Cookie为uid=hailey,那将会很糟糕,因为我可以轻松将其编辑为uid=ach1lles并获取您的管理员权限。相反,你想做一些足够随机的事情,比如我用户名的sha2哈希的base64,以及当前时间和昨天NYT的标题或类似的东西。您可能希望将其添加为数据库表,然后在我发出请求时进行检查。

另外,不要将密码存储为文本,请考虑像bcrypt这样的东西。 :)

答案 1 :(得分:1)

您的第二个SELECT查询会产生安全风险。有人可以通过了解任何一个用户的密码,以任何用户身份登录。

想象一下,您有以下用户(忽略密码是纯文本):

username | password
---------+---------
Jack     | abc123
-------------------
Jill     | def456

让我们说有人试图用&#34; Jack&#34;作为用户名和&#34; def456&#34;作为密码。

您的第一个SELECT

c = g.db.execute("SELECT username from users where username = (?)", [form.username.data])

将返回Jack的记录。这会导致您执行第二个SELECT

c = g.db.execute("SELECT password from users where password = (?)", [form.password.data])

这将返回吉尔的纪录。虽然这是一个不同的用户,但您只检查从查询返回的记录,而不是与第一个查询相同的记录。即使Jack的密码错误,用户也会以Jack身份登录。

理想情况下,您只想根据用户名执行一个查询,并将提交的密码与数据库中的密码进行比较。

c = g.db.execute("SELECT username, password from users where username = (?)", [form.username.data])
user = c.fetchone()
# don't forget to apply your hashing algorithm to form.password.data
if user and user[1] == form.password.data:  
    session['username'] = form.username.data
    session['logged_in'] = True
    flash('You were logged in')
    return redirect(url_for('account'))

所有这一切,我不能推荐像Flask-LoginFlask-Security这样的库。身份验证很难做到。利用社区使其更容易。