창작물

나만의 실습 사이트 만들기 (2편)

ilsancityboy 2024. 6. 18. 08:00

지난글

https://ilsancityboy.tistory.com/72

 

나만의 실습 사이트 만들기 (1편)

매번 sql injection 이나 XSS 등등 공격 기법을 사용해보기 위해 가능한 주어진 사이트들을 돌아다니다가 나도 나만의 실습 사이트를 하나 마련해놓으면 좋겠다 싶어서 기획하게 됐다.시작전 먼저

ilsancityboy.tistory.com

 

지난 번에 이어 이번글에서는 총 2가지를 할 것이다.

 

1.회원가입 페이지 만들기

2.sql (데이터베이스 연동하기)

 

1. register html

<!-- templates/main_page.html -->
{% extends 'layout.html' %}
{% block title %}회원 가입{% endblock %}

{% block content %}
   <div class="text_wrapper">
      <div class="top">
	<h1>회원 가입</h1>
      </div>
	<hr>
      <div class="middle">
            <form method="POST" action="/register">
                <h1>아이디</h1>
                <input type="text" placeholder="아이디" id="username" name="username" required>
                
                <h1>비밀번호</h1>
                <input type="password" placeholder="비밀번호" id="password" name="password" required>
                
                <h1>이메일</h1>
                <input type="email" placeholder="이메일" id="email" name="email" required>
                
                <button type="submit" class="custom_button">회원가입</button>
            </form>
      </div>
	<hr>
      <div class="register_bottom">
        <a href="{{ url_for('login') }}">
	    <p>이미 계정이 있으신가요?</p>
            <button class="custom_button">로그인하기</button>
        </a>
      </div>
   </div>
{% endblock %}

먼저 지난 번에 미리 만들어둔 layout.html을 똑같이 불러오고 간단하게 회원가입에 필요한 입력 폼들만 만들어주면 된다.

 

 

2. sql 연동하기

2-1. 라이브러리 설치

pip install flask-sqlalchemy Werkzeug

 

Flask-SQLAlchemy: SQLAlchemy ORM을 Flask와 통합하여 데이터베이스를 다루는 데 사용된다.

즉, Flask 애플리케이션에서 데이터베이스의 테이블을 Python 클래스로 정의하고, 이를 사용하여 데이터베이스를 쉽게 생성하고 쿼리할 수 있도록 해준다. 이를 통해 개발자는 SQL 쿼리를 직접 작성하지 않고도 Python 객체를 통해 데이터베이스와 상호작용할 수 있다.

 

Werkzeug: 보안과 관련된 여러 기능을 제공하는 유틸리티 라이브러리이다.

 

 

 

2-2. 애플리케이션 설정

1편 글에서 작성했던 app.py 코드를 다시 보면

from flask import Flask, render_template, request, redirect, url_for

app = Flask(__name__)

 

여기서 코드를 추가해 기본설정을 맞춰주었다.

 

from flask import Flask, render_template, request, redirect, url_for , flash
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash


app = Flask(__name__)
app.config['SECRET_KEY'] = '1234'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
db = SQLAlchemy(app)

 

먼저 추가된 부분을 보자면 

1. flash

메시지 플래싱을 제공하는 함수이다. 쉽게 말해 사용자에게 알림이나 경고 메시지를 줄때 사용된다.

#flash 함수에 대해 설명 추가글 올릴 예정

 

2. from flask_sqlalchemy import SQLAlchemy

위에 설명한 것처럼 Flask와 SQLAlchemy를 통합하여 Flask 애플리케이션에서 데이터베이스를 쉽게 다룰 수 있도록 도와주는 모듈을 가져온 것

 

3.from werkzeug.security import generate_password_hash, check_password_hash

해시값을 설정하고 일치하는지 확인하는 도구를 가져온 것.

 

4.app.config['SECRET_KEY'] = '1234'

애플리케이션의 시크릿 키를 설정합니다. 시크릿 키는 보안 관련 기능에 사용한것

 

5.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'

SQLAlchemy 라이브러리를 사용하여 데이터베이스와 연결할 때 필요한

URI(Uniform Resource Identifier)를 설정하는 부분

 

sqlite://: SQLAlchemy에서 SQLite 데이터베이스를 사용할 것임을 나타냄

/users.db : SQLite 데이터베이스 파일의 경로입니다. users.db라는 파일이 현재 작업 디렉토리에 생성될 것

 

2-3. 데이터베이스 모델 정의

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)  # 사용자 ID, 자동 증가
    username = db.Column(db.String(150), nullable=False, unique=True)  # 사용자 이름, 고유값
    password = db.Column(db.String(150), nullable=False)  # 사용자 비밀번호
    email = db.Column(db.String(150), nullable=False, unique=True)  # 사용자 이메일, 고유값

 

sql에서 어떤식으로 데이터가 저장되는지 모델을 정의하는 부분이다.

db.Integer: 정수 형식의 데이터 타입이란 뜻.

nullable = False 는 이 부분은 비워져있으면 안된다는 뜻.

unique = True 는 각각 고유의 username 즉, 중복되는것이 없어야 한단 뜻.

 

즉 user라는 테이블에는

id username password email
1 user user1234 test@gmail.com
2 admin admin1234 hi@daum.net
3 guest guest1234 ggyo@naver.com

 

이런식으로 저장이 되는 것

 

2-4. 데이터베이스 생성

@app.before_request
def create_tables():
    app.before_request_funcs[None].remove(create_tables)

    db.create_all()

Flask 애플리케이션이 요청을 처리하기 전에 실행되는 함수를 정의하는 부분이다.

각 요청이 처리되기 전에 실행되는 Flask의 before_request 데코레이터를 사용하여 create_tables 함수를 등록하고 있다.

 

즉, 맨 처음에 실행하게 되면 데이터베이스가 없을 시 데이터베이스 파일을 만드는 작업인 것.

 

3. 회원가입 라우트 구현

@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':  # 사용자가 회원가입 양식을 제출했을 때
        username = request.form['username']
        password = request.form['password']
        email = request.form['email']

        hashed_password = generate_password_hash(password, method='pbkdf2:sha256')  # 비밀번호 해싱

        new_user = User(username=username, password=hashed_password, email=email)

        try:
            db.session.add(new_user)  # 새로운 사용자 추가
            db.session.commit()  # 데이터베이스에 커밋
            flash('회원가입이 완료되었습니다!', 'success')
            return redirect(url_for('login'))
        except:
            flash('회원가입 중 오류가 발생했습니다.', 'danger')
            return redirect(url_for('register'))

    return render_template('register.html')  # 회원가입 양식 페이지 렌더링

 

 hashed_password = generate_password_hash(password, method='pbkdf2:sha256')

입력된 password를 PBKDF2 알고리즘과 SHA-256 해시 함수를 사용하여 안전하게 해싱한 값을 반환한다.

 

즉, 가입을 하게되면 데이터베이스에는 평문의 비밀번호가 아닌 비밀번호 해싱값이 저장되게 되며 앞으로 로그인 할 때마다 입력한 비밀번호를 해싱 처리하여 데이터베이스 안에 있는 해싱값과 일치한지 확인하고 로그인 처리를 하게 될 것.

(보안차 넣은 부분)

 

flash('회원가입이 완료되었습니다!', 'success')

flash는 사용자에게 알림을 띄우는 것.

단순히 이 코드로 알람이 뜨는것이 아닌 html에 별도 설정을 해줘야 한다.

 

{% with message = get_flashed_messages() %}
{% if messages %}
<script>
alert("{{messages[-1]}}")
</script>
{% endif %}
{% endwith %}

 

이 코드를 html에 넣어 app.py에서 flash된 값을 

get_flashed_messages() 로 html에서 가져와 alert를  띄워주는 것

 

 

4. html 수정

따라서 맨처음에 만든 register.html 에 flash 를 위한 코드를 넣어주자.

<!-- templates/main_page.html -->
{% extends 'layout.html' %}
{% block title %}회원 가입{% endblock %}

{% with message = get_flashed_messages() %}
	{% if messages %}
		<script>
			alert("{{messages[-1]}}")
		</script>
	{% endif %}
{% endwith %}

{% block content %}
   <div class="text_wrapper">
      <div class="top">
	<h1>회원 가입</h1>
      </div>
	<hr>
      <div class="middle">
            <form method="POST" action="/register">
                <h1>아이디</h1>
                <input type="text" placeholder="아이디" id="username" name="username" required>
                
                <h1>비밀번호</h1>
                <input type="password" placeholder="비밀번호" id="password" name="password" required>
                
                <h1>이메일</h1>
                <input type="email" placeholder="이메일" id="email" name="email" required>
                
                <button type="submit" class="custom_button">회원가입</button>
            </form>
      </div>
	<hr>
      <div class="register_bottom">
        <a href="{{ url_for('login') }}">
	    <p>이미 계정이 있으신가요?</p>
            <button class="custom_button">로그인하기</button>
        </a>
      </div>
   </div>
{% endblock %}

 

 


 

 

결과물

 

app.py 전체코드

from flask import Flask, render_template, request, redirect, url_for , flash
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash


app = Flask(__name__)
app.config['SECRET_KEY'] = '1234'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////Users/user/Desktop/바탕화면/공부/파이썬/혼자 프로젝트/lol_clone/instance/users.db'
db = SQLAlchemy(app)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)  # 사용자 ID, 자동 증가
    username = db.Column(db.String(150), nullable=False, unique=True)  # 사용자 이름, 고유값
    password = db.Column(db.String(150), nullable=False)  # 사용자 비밀번호
    email = db.Column(db.String(150), nullable=False, unique=True)  # 사용자 이메일, 고유값


@app.before_request
def create_tables():
    # The following line will remove this handler, making it
    # only run on the first request
    app.before_request_funcs[None].remove(create_tables)

    db.create_all()



@app.route('/')
def index():
    return render_template('index.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']

        user = User.query.filter_by(username=username).first()


        if user and check_password_hash(user.password, password):
            return redirect(url_for('index'))
        else:
            return render_template('login.html', error=True)
    return render_template('login.html')


@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':  # 사용자가 회원가입 양식을 제출했을 때
        username = request.form['username']
        password = request.form['password']
        email = request.form['email']

        hashed_password = generate_password_hash(password, method='pbkdf2:sha256')  # 비밀번호 해싱

        new_user = User(username=username, password=hashed_password, email=email)

        try:
            db.session.add(new_user)  # 새로운 사용자 추가
            db.session.commit()  # 데이터베이스에 커밋
            flash('회원가입이 완료되었습니다!', 'success')
            return redirect(url_for('login'))
        except:
            flash('회원가입 중 오류가 발생했습니다.', 'danger')
            return redirect(url_for('register'))

    return render_template('register.html')  # 회원가입 양식 페이지 렌더링




if __name__ == '__main__':
    app.run(debug=True)

 


 

1

 

2

 

 

3

 

 

 

4

 

 

 

5

 

 

6

 

 

 

7

 


 

 

다음 글에선 게시판 기능 추가에 대해 작성해보겠다.