웹 해킹 기초 - 코드 리뷰

2025. 1. 22. 20:04·Security/Web Hacking

과제 목표

- app.py 코드리뷰

 

 

 

(1) 아래의 코드들의 어떤 url들로 연결이 가능한지 설명하기

(2) 각 엔드포인트(@app.route)가 수행하는 주요 기능에 대해 설명

(3) 각 기능을 수행하기 위해 사용되는 SQL 쿼리를 이해하고 설명

(4) 이용자가 POST /write_post 엔드포인트를 통해 게시글을 생성하는 경우, 브라우저, 웹 서비스, 그리고 데이터베이스 간의 상호작용 과정을 상세히 설명

 

#!/usr/bin/env python3
import os
import pymysql
from flask import Flask, abort, redirect, render_template, request

PAGINATION_SIZE = 10

app = Flask(__name__)
app.secret_key = os.urandom(32)

def connect_mysql():
    conn = pymysql.connect(host='db',
                           port=3306,
                           user=os.environ['MYSQL_USER'],
                           passwd=os.environ['MYSQL_PASSWORD'],
                           db='board',
                           charset='utf8mb4')
    cursor = conn.cursor()
    return conn, cursor

@app.route('/')
def index():
    return redirect('/board')

@app.route('/board')
def board():

    page = request.args.get('page')
    page = int(page) if page and page.isdigit() and int(page) > 0 else 1

    ret = []

    conn, cursor = connect_mysql()
    try:
        query = 'SELECT _id, title FROM posts ORDER BY _id DESC LIMIT %s, %s'
        cursor.execute(query, ((page - 1) * PAGINATION_SIZE, PAGINATION_SIZE))
        ret = cursor.fetchall()
    except Exception as e:
        print(e, flush=True)
        abort(400)
    finally:
        cursor.close()
        conn.close()

    return render_template('board.html', page=page, ret=ret)

@app.route('/board/<post_id>')
def board_post(post_id):

    if not post_id or not post_id.isdigit() or int(post_id) < 1:
        abort(400)

    ret = None

    conn, cursor = connect_mysql()
    try:
        query = 'SELECT title, content FROM posts WHERE _id = %s'
        cursor.execute(query, (post_id))
        ret = cursor.fetchone()
    except Exception as e:
        print(e, flush=True)
        abort(400)
    finally:
        cursor.close()
        conn.close()

    if not ret:
        abort(404)

    return render_template('post.html', title=ret[0],
                           content=ret[1], post_id=post_id)

@app.route('/write_post', methods=['POST'])
def write_post():
    if 'title' not in request.form or 'content' not in request.form:
        return render_template('write_post.html')

    title = request.form['title']
    content = request.form['content']

    conn, cursor = connect_mysql()
    try:
        query = 'INSERT INTO posts (title, content) VALUES (%s, %s)'
        cursor.execute(query, (title, content))
        conn.commit()
    except Exception as e:
        print(e, flush=True)
        abort(400)
    finally:
        cursor.close()
        conn.close()

    return redirect('/board')

@app.route('/modify_post', methods=['POST'])
def modify_post():
    post_id = request.form['post_id']

    if not post_id or not post_id.isdigit() or int(post_id) < 1:
        abort(400)

    if 'title' not in request.form or 'content' not in request.form:
        conn, cursor = connect_mysql()
        try:
            query = 'SELECT title, content FROM posts WHERE _id = %s'
            cursor.execute(query, (post_id, ))
            ret = cursor.fetchone()
        except Exception as e:
            print(e, flush=True)
            abort(400)
        finally:
            cursor.close()
            conn.close()

        if not ret:
            abort(404)

        return render_template('modify_post.html', title=ret[0],
                               content=ret[1], post_id=post_id)

    title = request.form['title']
    content = request.form['content']

    conn, cursor = connect_mysql()
    try:
        query = 'UPDATE posts SET title=%s, content=%s WHERE _id = %s'
        cursor.execute(query, (title, content, post_id, ))
        conn.commit()
    except Exception as e:
        print(e, flush=True)
    finally:
        cursor.close()
        conn.close()

    return redirect(f'/board/{post_id}')

@app.route('/delete_post', methods=['POST'])
def delete_post():

    post_id = request.form['post_id']
    if not post_id or not post_id.isdigit() or int(post_id) < 1:
        abort(400)

    if 'answer' not in request.form:
        return render_template('delete_post.html', post_id=post_id)

    if request.form['answer'] == 'y':
        conn, cursor = connect_mysql()
        try:
            query = 'DELETE FROM posts WHERE _id = %s'
            cursor.execute(query, (post_id, ))
            conn.commit()
        except Exception as e:
            print(e, flush=True)
        finally:
            cursor.close()
            conn.close()

        return redirect('/board')

    return redirect(f'/board/{post_id}')

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000)

 

과제 풀이

(1) 연결 가능한 url 

- /

- /board

- /board/<post_id>

- /write_post

- /modify_post

- /delete_post

 

(2)  각 엔드포인트(@app.route)가 수행하는 주요 기능

 

- / : /board api로 리다이렉션

 

- /board : 최근에 등록된 글 순으로 페이징 처리를 하여 게시판 반환

 

- /board/<post_id> : 유효한 post_id가 path variable로 넘어왔다면 해당 게시글 반환, 아니라면 클라이언트 측 에러 코드 반환.

 

- /write_post : 게시글 작성 기능. 작성이 완료되면 /board api로 리다이렉션.

 

- /modify_post : 게시글 수정 기능을 수행하고 수정이 완료되면 /board/{post_id} api로 리다이렉션.  해당 게시글이 없다면 클라이언트 측 에러 코드 반환.

 

- /delete_post : 정말 삭제하고 싶은지 사용자에게 묻고 'y'인 경우에 게시글 삭제 후 /board api로 리다이렉션, 'y'가 아닌 경우에는 /board/{post_id} api로 리다이렉션.  해당 게시글이 없다면 클라이언트 측 에러 코드 반환.

 

(3) 각 기능을 수행하기 위해 사용되는 SQL 쿼리

 

- / : 없음

 

- /board : 'SELECT _id, title FROM posts ORDER BY _id DESC LIMIT %s, %s' 쿼리가 사용되었으며 페이지의 시작 위치와 한 페이지당 보여줄 항목 수를 넘겨받아 posts 테이블에서 _id와 title 컬럼을 선택함. _id를 기준으로 내림차순 정렬한 결과를 가져옴. 결국 최근 등록된 글 순으로 페이징 처리된 데이터를 가져오는 역할을 수행함.

 

- /board/<post_id> : 'SELECT title, content FROM posts WHERE _id = %s' 쿼리가 사용되었으며 post id 값을 통해 posts 테이블에서 _id와 title 컬럼을 선택함. 게시글 상세 조회 기능에 해당함.

 

- /write_post : 'INSERT INTO posts (title, content) VALUES (%s, %s)' 쿼리가 사용되었으며 posts 테이블에 title과 content를 등록함. 게시글 등록 기능에 해당함.

 

- /modify_post : 'SELECT title, content FROM posts WHERE _id = %s' 쿼리가 사용되었으며 이는 /board/<post_id> api 속에서 사용되는 쿼리와 동일한 기능을 수행함. 또 'UPDATE posts SET title=%s, content=%s WHERE _id = %s' 쿼리가 사용되었는데 이는 post id에 해당하는 게시글의 제목이나 내용을 수정하는 역할을 수행함.

 

- /delete_post : 'DELETE FROM posts WHERE _id = %s' 쿼리가 사용되었으며 post id에 해당하는 게시글을 삭제하는 역할을 수행함.

 

(4) 이용자가 POST /write_post 엔드포인트를 통해 게시글을 생성하는 경우, 브라우저, 웹 서비스, 그리고 데이터베이스 간의 상호작용 과정

1. 클라이언트 측에서 /write_post api를 POST 메소드를 통해 호출

2. 클라이언트가 보낸 form 데이터에 접근

3. 'title'이나 'content'가 form 데이터에 없다면 write_post.html을 다시 렌더링

4. DB connection

5. 'INSERT INTO posts (title, content) VALUES (%s, %s)' 쿼리와 conn.commit()을 통해 DB에 변경 사항 반영(posts 테이블에 게시글 등록)

6. (예외 발생 시 400번 에러 코드 반환)

7. DB connection 종료

8. /board 페이지로 리다이렉션

 

'Security > Web Hacking' 카테고리의 다른 글

xss-2 풀이  (0) 2025.02.03
드림핵 cookie문제 풀이  (0) 2025.01.22
웹해킹 기초 문제 풀이 - 1  (0) 2025.01.22
웹 해킹 기초  (0) 2025.01.09
전공 세미나 수업 - 해킹과 SQL 인젝션에 대하여  (0) 2022.12.22
'Security/Web Hacking' 카테고리의 다른 글
  • xss-2 풀이
  • 드림핵 cookie문제 풀이
  • 웹해킹 기초 문제 풀이 - 1
  • 웹 해킹 기초
switch_user
switch_user
나의 공부 기록
  • switch_user
    while(true)
    switch_user
  • 전체
    오늘
    어제
    • 분류 전체보기
      • C
      • C++
      • Java
      • Python
      • Web
      • App
      • Security
        • Web Hacking
        • Reverse Engineering
      • DB
      • Machine Learning
      • Computer Science
      • Linux
      • Algorithm
      • 진로
      • 기타
  • 블로그 메뉴

    • 홈
    • 태그
    • velog
    • Github
  • 링크

    • velog
    • Github
  • 공지사항

  • 인기 글

  • 태그

    배치 학습
    Web 기초
    코드 패치
    리버싱
    머신러닝
    클래스 외부에 함수 구현
    웹해킹
    xss
    쿠키
    IDA
    모델 기반 학습
    사례 기반 학습
    race condition
    생성자와 소멸자
    어셈블리
    cin.getline
    디컴파일
    SQL
    반복문
    SQLi
    웹
    ml
    인터프리팅
    HTTP
    Hacking Process
    어셈블리어
    비트연산
    HTML
    CSS
    x64dbg
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
switch_user
웹 해킹 기초 - 코드 리뷰
상단으로

티스토리툴바