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:
... 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.
A concept in which multiple layers of defense are placed through a system
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.
A design principle that implies that simplicity should be a design goal.
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.
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.
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:
principle of least privilege: only admin can create new accounts
defense in depth: multiple security layers, e.g., password hashing with salting, role-based access control
attack surface minimization, e.g., hiding tabs if a user is logged in.
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:
A function that can be used to map data to fixed-size values.
A precomputed table is a precomputed table for caching the outputs of a cryptographic hash function, usually for cracking password hashes
Random data fed as an additional input to a one-way function that hashes a password or ...
Salting:
prevents precomputed attacks like rainbow tables, so that the attackers must crack each password individually
ensures that identical passwords have different hashes
But:
it does not prevent brute-force attacks against individual passwords, so weak passwords like password123 can be relatively quickly found.
Salting in user registration¶
Salting in user login¶
Salting example code¶
TODO test code
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()