DAO and Security

Cyriel Mallart
Ludovic Deneuville
Rémi Pépin

Outline

  • Data Access Object
    • Why persist data?
    • What is a DAO?
    • DAO Example
  • Computer Security
    • Definition
    • Security vulnerabilities
    • SQL Injection
  • Password Management
    • Authentication with cipher
    • Password hashing
    • Example

Data Access Object (DAO)

A Computer

What are its main components?

  • A processor (CPU): does ONLY calculations
  • RAM: fast, volatile memory
  • Hard drive (HDD, SSD): long-term memory
  • Graphics card: specialized processing unit

How to Solve This Problem?

What problem??

A Question to Ask

p = Personnage(prenom="Leia", nom="Organa", age = 35)

What is a Python variable?

  • A reference (the variable name)
  • An associated object (its value)

A Follow-Up Question

personnage.py
class Personnage:
    def __init__(self, prenom, nom, age):
        self.prenom = prenom
        self.nom = nom
        self.age = age
        self.vaisseaux = []
    def anniversaire(self):
        self.age += 1

What is a Python object?

  • Attributes (which can themselves be objects)
  • Methods

In Summary

  • We want to save key-value pairs
  • With values that can themselves consist of key-value pairs

We want to save a tree 🌳

How to Do This?

  • Write our data to a hard drive in a file
    • csv, parquet, json, xml
  • Use a database
    • SQL tables + relations, NoSQL

What is a DAO?

What is a DAO?

  • Technical class
  • One DAO class per business object
  • Exposes methods to communicate with the persistence layer

Which Methods to Expose?

  • Create
  • Read
  • Update
  • Delete

The Benefit of a Separate Class

Separation of responsibilities

  • “Disposable” class 🚮
  • Modifiable without risk 🔨
  • Parallel work 🦸‍♀️🧙‍♂️👨‍💼👩‍🔬

Quick Recap

How to Connect to a Database in Python?

Use a dedicated library

  • PostgreSQL: psycopg2
  • MySQL: mysql-connector-python
  • Oracle: cx_Oracle
  • MongoDB: pymongo

psycopg2

  • pip install psycopg2-binary
  • connection: Establishes the connection to the database Not super interesting to do, the code is provided

psycopg2 - Connection

db_connection.py
import os
import dotenv
import psycopg2
from psycopg2.extras import RealDictCursor
from utils.singleton import Singleton

class DBConnection(metaclass=Singleton):
    """
    Database connection class
    It allows opening only one unique connection
    """
    def __init__(self):
        """Open the connection"""
        dotenv.load_dotenv()
        self.__connection = psycopg2.connect(
            host=os.environ["POSTGRES_HOST"],
            port=os.environ["POSTGRES_PORT"],
            database=os.environ["POSTGRES_DATABASE"],
            user=os.environ["POSTGRES_USER"],
            password=os.environ["POSTGRES_PASSWORD"],
            options=f"-c search_path={os.environ['POSTGRES_SCHEMA']}",
            cursor_factory=RealDictCursor,
        )
    @property
    def connection(self):
        return self.__connection

psycopg2 - Cursor

  • cursor: Encapsulates the query

    from dao.db_connection import DBConnection
    
    with DBConnection().connection as connection:
        with connection.cursor() as cursor:
  • cursor.execute("<a SQL query>"): Executes a query

  • cursor.fetchone()/fetchall()/fetchmany(): Retrieves the results

Basic Form

def dao_function(self, arg1, arg2, ...):
    # Get the database connection
    with DBConnection().connection as connection:
    # Create a cursor to make a query
        with connection.cursor() as cursor:
            # Send the SQL query to the server
            cursor.execute(
                "<a_sql_query_with_placeholders>",
                fill_in_the_placeholders)
            # Retrieve the query result
            res = cursor.fetchone()  # or fetchall()/fetchmany()
    # If the query returns something
    if res:
      something = "<res formatted>"

    return something

A Small Example: LivreDao

Create a Book

def create(self, livre) -> Livre:
    """To create a book in the database"""
    with DBConnection().connection as conn:
        with conn.cursor() as cursor:
            cursor.execute(
                "INSERT INTO livre (isbm, titre, auteur)         "
                "     VALUES (%(isbm)s, %(titre)s, %(auteur)s)   "
                "  RETURNING id_livre;                           ",
                {"isbm": livre.isbm,
                 "titre": livre.titre,
                 "auteur": livre.auteur},
            )
            livre.id = cursor.fetchone()["id_livre"]
    return livre

List Books

def find_all(self) -> list[Livre]:
    """To retrieve all books from the database"""
    with DBConnection().connection as conn:
        with conn.cursor() as cursor:
            cursor.execute(
                "SELECT id_livre,                  "
                "       isbm,                      "
                "       titre,                     "
                "       auteur                     "
                "  FROM livre ;                    "
            )
            livre_bdd = cursor.fetchall()

    liste_livres = []
    if livre_bdd:
        for livre in livre_bdd:
            liste_livres.append(
                Livre(
                    id=livre["id_livre"],
                    isbm=livre["isbm"],
                    titre=livre["titre"],
                    auteur=livre["auteur"],
                )
            )

    return liste_livres

Find a Book

def find_by_isbm(self, isbm) -> Livre:
    """To retrieve a book by its isbm"""
    with DBConnection().connection as conn:
        with conn.cursor() as cursor:
            cursor.execute(
                "SELECT *                          "
                "  FROM livre                      "
                " WHERE isbm = %(isbm)s            ",
                {"isbm": isbm}
            livre_bdd = cursor.fetchone()

    livre = None
    if livre_bdd:
        livre = Livre(
            id=livre_bdd["id_livre"],
            isbm=livre_bdd["isbm"],
            titre=livre_bdd["titre"],
            auteur=livre_bdd["auteur"],
        )
    return livre

Conclusion

  • Python works in RAM (volatile)
  • Need a mechanism for data persistence
  • DAO: centralizes methods for reading/writing our data
  • The business layer calls the DAO without worrying about the persistence system
  • Enables effective teamwork 🦸‍♀️🧙‍♂️👨‍💼👩‍🔬

Computer Security

CIA Principles

3 Pillars of Computer Security

  • Confidentiality,
  • Integrity
  • Availability

Plus 2

  • Authenticity (or Proof)
  • Non-repudiation

Confidentiality

Only authorized individuals should have access to the information intended for them (notions of rights or permissions). Any undesirable access must be prevented.

Associated Mechanisms:

  • Rights management (directories, roles, etc.)
  • Cryptography

Integrity

Data must be as expected and must not be altered accidentally, unlawfully, or maliciously.

Associated Mechanisms:

  • Digital signature
  • Checksum

Availability

Access to the information system’s resources must be permanent and flawless during the planned usage periods.

Associated Mechanisms:

  • Server redundancy
  • Virtualization
  • Containerization

Traceability

Ensures that accesses and access attempts to the considered elements are traced and that these traces are preserved and usable.

Associated Mechanism:

  • Logging

Authentication

Users must prove their identity by responding to a “challenge.”

Associated Mechanisms:

  • Weak authentication (identifier, password)
  • Strong authentication (biometric data, multi-factor)

Non-repudiation

No user should be able to dispute the operations they have performed within the scope of their authorized actions, and no third party should be able to attribute the actions of one user to another.

Associated Mechanisms:

  • Traceability
  • Authentication
  • Integrity

Computer Security Vulnerabilities

Too Many Vulnerabilities!!!

  • Low-level physical vulnerabilities
  • High-level physical vulnerabilities
  • SQL Injection
  • Data injection
  • XSS Vulnerability
  • Code execution

What Should You Be Wary Of?

Your users

Example of Vulnerabilities: SQL Injections

Source: https://xkcd.com/

SQL Injection

Involves entering SQL to execute a different query than intended.

Problems:

  • Confidentiality
  • Authentication
  • Integrity
  • Availability

Example: Authenticating Without a Password

Authentication query

SELECT *
  FROM user
 WHERE name = 'input_name'
   AND password = 'input_password';

You Enter

  • Gennysson
  • awesome_password
SELECT *
  FROM user
 WHERE name = 'Gennysson'
   AND password = 'awesome_password';

Example: Authenticating Without a Password

SELECT *
  FROM user
 WHERE name = 'input_name'
   AND password = 'input_password';

You Enter

  • Gennysson
  • ' OR 1=1; --
SELECT *
  FROM user
 WHERE name = 'Gennysson'
   AND password = '' OR 1=1; --';

Example: Deleting a Table

SELECT *
  FROM user
 WHERE name = 'input_name'
   AND password = 'input_password';

You Enter

  • Gennysson
  • '; DROP TABLE user CASCADE; --
SELECT *
  FROM user
 WHERE name='Gennysson'
   AND password=''; DROP TABLE user CASCADE; --;

How to Protect Yourself?

  • Escape special characters
  • Use a prepared query

The library you use only escapes special characters 😨

A prepared query :

Cross-Site Scripting

Involves injecting code that causes actions on the browser. This can allow:

  • Page redirections (phishing)
  • Information theft
  • Actions on the site
  • Making the site difficult to use

How to Protect Yourself?

  • Never insert raw data
  • Escape special characters
  • Verify your data

Tip

Web libraries often do this for you!

To Sum Up: Injection

  • Never trust user inputs, verify/clean them

  • Never trust user inputs, verify/clean them

  • Never trust user inputs, verify/clean them

  • NEVER TRUST USER INPUTS

  • USERS ARE YOUR N°1 VULNERABILTY

  • USERS ARE THE DEVIL

Password Management

To test the strength of a password:

Should Your Application Store Plain Text Passwords?

Does Your Application Need to Know a User’s Password to Authenticate Them?

How Do We Do It?

Hashing the Password

  • Hash the password
  • Store the hash in the database
  • When you need to compare, hash the entered password
  • And compare the hashes

Authentication without persisting passwords!!!!

Add Salt for More Security

A Database Without Added Salt

  • Your email/password database leaks, but the passwords are hashed
  • Attackers must brute-force the passwords
  • They start with the most common passwords and hash them with common hash algorithms
  • Then they compare with the database
  • High chance of having several matches (cf : USERS ARE THE N°1 VULNERABILITY)

Salt is Good for Security

Instead of hashing and storing the password

Store and hash the password,

AND an element linked to the user in a deterministic way (the salt).

Now even if two people have the same password, they will have different hashes.

Salt is Good for Security

  • Your email/password database leaks, but the passwords are hashed and salted
  • Attackers must brute-force the passwords
  • They start with the most common passwords and hash them with common hash algorithms
  • Then they compare with the database.
  • There are no matches because your passwords are salted
  • And finding one password doesn’t allow finding others

Example of Password Hashing

import hashlib

def hash_password(password, idep):
    salt = idep
    return hashlib.sha256(salt.encode() + password.encode()).hexdigest()

print(hash_password("awesome_password", "Gennysson"))

To Sum Up the Security Part

  • Always verify inputs
  • Never trust users
  • Multiple levels of security
  • No need to store plain text passwords

Honest version :

  • Always assume your users are either hackers or idiots with no self preservation instincts
  • Always know you are less smart than you think : you-proof the system
  • Assume your colleagues are as just smart as you : wrap their security in your security
  • For the love of all that is good in the world, do NOT store sensitive identifying information in clear (use your data science instincts)