Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Learning goals
  • Explain the importance of security in operational technology

  • Identify potential attack vectors in industrial automation systems

  • Evaluate the consequences of security breaches

  • Apply security principles to an industrial control system implementation (in the project)

  • Locate basic security principles in software, e.g., salting

We will analyze security in industrial automation systems

Security principles

Based on the preparation:

Principle of least privilege

... requires that every component of a computer system, e.g., program, user, must be able to access only the information and resources that are necessary for its purpose.

Defense in depth

A concept in which multiple layers of defense are placed through a system

Fail-safe

A design feature or practice that, in the event of a failure of the design feature, inherently responds in a way that will cause minimal or no harm to its environment.

KISS principle

A design principle that implies that simplicity should be a design goal.

Separation of duties

The concept of having more than one person required to complete a task.

Open design
A design which is publicly available so it can be built or understood.
Segmentation
Division of a system into multiple isolated subsystems. Different subsystems can have different security requirements.
Usability

Capacity of a system to provide a condition for its users to perform the tasks safely, effectively, and efficiently while enjoying the experience.

E.g., users use post-its if the password requirements are hard to remember.

Attack surface

The sum of a software environment’s different points where an attacker can try to enter data to, extract data from, or control a device or critical software.

We should minimize the attack surface.

Secure by default
All the security features of a system are turned on when a product is delivered.

E: Analyzing attack surface

Example system login interface

Here is an example project that uses:

Figure 1:A system login user interface some of the security principles. Only the admin has access to user management and database tools. The user can only access robot functions.

You can use this example in your project. You find the project here:

TODO

Protecting a credential database against exploitation

Related concepts:

Hash function

A function that can be used to map data to fixed-size values.

Rainbow table

A precomputed table is a precomputed table for caching the outputs of a cryptographic hash function, usually for cracking password hashes

Salt

Random data fed as an additional input to a one-way function that hashes a password or ...

Salting:

But:

Salting in user registration

Salting in user login

Salting example code

TODO test code

account_service.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
"""
A simple login system: create accounts, check passwords, check admin status.
"""

import hashlib
import hmac
import os

from sqlmodel import Field, Session, SQLModel, create_engine, select

# --------------------------------------------------------------------------
# 1. The database table
# --------------------------------------------------------------------------
# Each Account instance is one row in the "accounts" table.


class Account(SQLModel, table=True):
    username: str = Field(primary_key=True)
    salt: bytes
    password_hash: bytes
    is_admin: bool = False


# --------------------------------------------------------------------------
# 2. Password hashing helpers
# --------------------------------------------------------------------------
# We NEVER store the plain password. Instead we store a random "salt" plus
# a hash of (salt + password). To check a login, we redo the hash and
# compare it to what's stored.

ITERATIONS = 600_000  # makes brute-forcing slow; higher = safer but slower


def hash_password(password: str, salt: bytes) -> bytes:
    return hashlib.pbkdf2_hmac("sha256", password.encode(), salt, ITERATIONS)


def make_new_password(password: str) -> tuple[bytes, bytes]:
    """Create a fresh salt + hash for a brand-new account."""
    salt = os.urandom(16)
    return salt, hash_password(password, salt)


def check_password(password: str, salt: bytes, expected_hash: bytes) -> bool:
    """Return True if `password` matches the stored hash."""
    actual_hash = hash_password(password, salt)
    # compare_digest avoids leaking timing info about how much of the hash matched
    return hmac.compare_digest(actual_hash, expected_hash)


# --------------------------------------------------------------------------
# 3. The account service -- the "business logic" layer
# --------------------------------------------------------------------------


class AccountService:
    def __init__(self, db_path: str = "database.sqlite"):
        self.engine = create_engine(f"sqlite:///{db_path}")
        SQLModel.metadata.create_all(self.engine)  # create table if missing

    def create_account(self, username: str, password: str, is_admin: bool = False):
        salt, password_hash = make_new_password(password)
        account = Account(
            username=username,
            salt=salt,
            password_hash=password_hash,
            is_admin=is_admin,
        )
        with Session(self.engine) as session:
            session.add(account)
            session.commit()

    def username_exists(self, username: str) -> bool:
        return self.get_account(username) is not None

    def credentials_correct(self, username: str, password: str) -> bool:
        account = self.get_account(username)
        if account is None:
            return False
        return check_password(password, account.salt, account.password_hash)

    def is_admin(self, username: str) -> bool:
        account = self.get_account(username)
        return account.is_admin if account else False

    def get_account(self, username: str) -> Account | None:
        with Session(self.engine) as session:
            statement = select(Account).where(Account.username == username)
            return session.exec(statement).first()

E: Analyzing password salting in code